mirror of
https://github.com/NohamR/RMHook.git
synced 2026-08-27 18:29:42 +00:00
PoC
This commit is contained in:
@@ -29,6 +29,11 @@
|
|||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/Qt>
|
#include <QtCore/Qt>
|
||||||
#include <QtWebSockets/QWebSocket>
|
#include <QtWebSockets/QWebSocket>
|
||||||
|
#include <QtNetwork/QSslConfiguration>
|
||||||
|
#include <QtNetwork/QSslCertificate>
|
||||||
|
#include <QtNetwork/QSslKey>
|
||||||
|
#include <QtNetwork/QSslSocket>
|
||||||
|
#include <QtCore/QFile>
|
||||||
#include <QtCore/QSettings>
|
#include <QtCore/QSettings>
|
||||||
#include <QtCore/QVariant>
|
#include <QtCore/QVariant>
|
||||||
#include <QtCore/QAnyStringView>
|
#include <QtCore/QAnyStringView>
|
||||||
@@ -37,15 +42,32 @@
|
|||||||
static NSString *const kReMarkableConfigFileName = @"rmfakecloud.config";
|
static NSString *const kReMarkableConfigFileName = @"rmfakecloud.config";
|
||||||
static NSString *const kReMarkableConfigHostKey = @"host";
|
static NSString *const kReMarkableConfigHostKey = @"host";
|
||||||
static NSString *const kReMarkableConfigPortKey = @"port";
|
static NSString *const kReMarkableConfigPortKey = @"port";
|
||||||
|
static NSString *const kReMarkableConfigClientCertKey = @"client_cert";
|
||||||
|
static NSString *const kReMarkableConfigClientKeyKey = @"client_key";
|
||||||
|
static NSString *const kReMarkableConfigCACertKey = @"ca_cert";
|
||||||
|
static NSString *const kReMarkableConfigDisableSSLVerifyKey = @"disable_ssl_verification";
|
||||||
static NSString *const kReMarkableDefaultHost = @"example.com";
|
static NSString *const kReMarkableDefaultHost = @"example.com";
|
||||||
static NSNumber *const kReMarkableDefaultPort = @(443);
|
static NSNumber *const kReMarkableDefaultPort = @(443);
|
||||||
|
|
||||||
static NSString *gConfiguredHost = @"example.com";
|
static NSString *gConfiguredHost = @"example.com";
|
||||||
static NSNumber *gConfiguredPort = @(443);
|
static NSNumber *gConfiguredPort = @(443);
|
||||||
|
static QString gConfiguredClientCertPath;
|
||||||
|
static QString gConfiguredClientKeyPath;
|
||||||
|
static QString gConfiguredCACertPath;
|
||||||
|
static bool gDisableSSLVerification = false;
|
||||||
|
|
||||||
|
// Loaded SSL objects (populated once at startup)
|
||||||
|
static QSslCertificate gClientCert;
|
||||||
|
static QSslKey gClientKey;
|
||||||
|
static QSslCertificate gCACert;
|
||||||
|
static bool gSSLInitialized = false;
|
||||||
|
|
||||||
static pthread_mutex_t gResourceMutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t gResourceMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
static NSString *ReMarkablePreferencesDirectory(void);
|
static NSString *ReMarkablePreferencesDirectory(void);
|
||||||
|
|
||||||
|
static void ReMarkableLoadSSLConfig(void);
|
||||||
|
|
||||||
static NSString *ReMarkablePreferencesDirectory(void) {
|
static NSString *ReMarkablePreferencesDirectory(void) {
|
||||||
NSArray<NSString *> *libraryPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
|
NSArray<NSString *> *libraryPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
|
||||||
NSString *libraryDir = [libraryPaths firstObject];
|
NSString *libraryDir = [libraryPaths firstObject];
|
||||||
@@ -59,6 +81,59 @@ static NSString *ReMarkableConfigFilePath(void) {
|
|||||||
return [ReMarkablePreferencesDirectory() stringByAppendingPathComponent:kReMarkableConfigFileName];
|
return [ReMarkablePreferencesDirectory() stringByAppendingPathComponent:kReMarkableConfigFileName];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ReMarkableLoadSSLConfig(void) {
|
||||||
|
if (gSSLInitialized) return;
|
||||||
|
|
||||||
|
if (!gConfiguredClientCertPath.isEmpty() && !gConfiguredClientKeyPath.isEmpty()) {
|
||||||
|
QString certPath = gConfiguredClientCertPath;
|
||||||
|
QString keyPath = gConfiguredClientKeyPath;
|
||||||
|
|
||||||
|
QFile certFile(QString::fromUtf8(certPath.toUtf8()));
|
||||||
|
if (certFile.open(QIODevice::ReadOnly)) {
|
||||||
|
gClientCert = QSslCertificate(certFile.readAll(), QSsl::Pem);
|
||||||
|
certFile.close();
|
||||||
|
if (gClientCert.isNull()) {
|
||||||
|
NSLogger(@"[reMarkable] Failed to parse client certificate from %s", certPath.toUtf8().constData());
|
||||||
|
} else {
|
||||||
|
NSLogger(@"[reMarkable] Loaded client certificate from %s", certPath.toUtf8().constData());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSLogger(@"[reMarkable] Failed to open client certificate file %s", certPath.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile keyFile(QString::fromUtf8(keyPath.toUtf8()));
|
||||||
|
if (keyFile.open(QIODevice::ReadOnly)) {
|
||||||
|
gClientKey = QSslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
||||||
|
keyFile.close();
|
||||||
|
if (gClientKey.isNull()) {
|
||||||
|
NSLogger(@"[reMarkable] Failed to parse client key from %s", keyPath.toUtf8().constData());
|
||||||
|
} else {
|
||||||
|
NSLogger(@"[reMarkable] Loaded client key from %s", keyPath.toUtf8().constData());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSLogger(@"[reMarkable] Failed to open client key file %s", keyPath.toUtf8().constData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gConfiguredCACertPath.isEmpty()) {
|
||||||
|
QString caPath = gConfiguredCACertPath;
|
||||||
|
QFile caFile(QString::fromUtf8(caPath.toUtf8()));
|
||||||
|
if (caFile.open(QIODevice::ReadOnly)) {
|
||||||
|
gCACert = QSslCertificate(caFile.readAll(), QSsl::Pem);
|
||||||
|
caFile.close();
|
||||||
|
if (gCACert.isNull()) {
|
||||||
|
NSLogger(@"[reMarkable] Failed to parse CA certificate from %s", caPath.toUtf8().constData());
|
||||||
|
} else {
|
||||||
|
NSLogger(@"[reMarkable] Loaded CA certificate from %s", caPath.toUtf8().constData());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSLogger(@"[reMarkable] Failed to open CA certificate file %s", caPath.toUtf8().constData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gSSLInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL ReMarkableWriteConfig(NSString *path, NSDictionary<NSString *, id> *config) {
|
static BOOL ReMarkableWriteConfig(NSString *path, NSDictionary<NSString *, id> *config) {
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:config options:NSJSONWritingPrettyPrinted error:&error];
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:config options:NSJSONWritingPrettyPrinted error:&error];
|
||||||
@@ -73,6 +148,13 @@ static BOOL ReMarkableWriteConfig(NSString *path, NSDictionary<NSString *, id> *
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline QString QStringFromNSStringSafe(NSString *string) {
|
||||||
|
if (!string) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return QString::fromUtf8([string UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
static void ReMarkableLoadOrCreateConfig(void) {
|
static void ReMarkableLoadOrCreateConfig(void) {
|
||||||
NSString *configPath = ReMarkableConfigFilePath();
|
NSString *configPath = ReMarkableConfigFilePath();
|
||||||
NSString *directory = [configPath stringByDeletingLastPathComponent];
|
NSString *directory = [configPath stringByDeletingLastPathComponent];
|
||||||
@@ -112,6 +194,26 @@ static void ReMarkableLoadOrCreateConfig(void) {
|
|||||||
|
|
||||||
gConfiguredHost = [resolvedHost copy];
|
gConfiguredHost = [resolvedHost copy];
|
||||||
gConfiguredPort = @(portCandidate);
|
gConfiguredPort = @(portCandidate);
|
||||||
|
|
||||||
|
// TLS config keys (optional)
|
||||||
|
NSString *certPathValue = configDict[kReMarkableConfigClientCertKey];
|
||||||
|
NSString *keyPathValue = configDict[kReMarkableConfigClientKeyKey];
|
||||||
|
NSString *caPathValue = configDict[kReMarkableConfigCACertKey];
|
||||||
|
NSNumber *disableSSLValue = configDict[kReMarkableConfigDisableSSLVerifyKey];
|
||||||
|
|
||||||
|
if ([certPathValue isKindOfClass:[NSString class]] && [certPathValue length]) {
|
||||||
|
gConfiguredClientCertPath = QStringFromNSStringSafe(certPathValue);
|
||||||
|
}
|
||||||
|
if ([keyPathValue isKindOfClass:[NSString class]] && [keyPathValue length]) {
|
||||||
|
gConfiguredClientKeyPath = QStringFromNSStringSafe(keyPathValue);
|
||||||
|
}
|
||||||
|
if ([caPathValue isKindOfClass:[NSString class]] && [caPathValue length]) {
|
||||||
|
gConfiguredCACertPath = QStringFromNSStringSafe(caPathValue);
|
||||||
|
}
|
||||||
|
if ([disableSSLValue respondsToSelector:@selector(boolValue)]) {
|
||||||
|
gDisableSSLVerification = [disableSSLValue boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
NSLogger(@"[reMarkable] Loaded config from %@ with host %@ and port %@", configPath, gConfiguredHost, gConfiguredPort);
|
NSLogger(@"[reMarkable] Loaded config from %@ with host %@ and port %@", configPath, gConfiguredHost, gConfiguredPort);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -129,13 +231,6 @@ static void ReMarkableLoadOrCreateConfig(void) {
|
|||||||
gConfiguredPort = kReMarkableDefaultPort;
|
gConfiguredPort = kReMarkableDefaultPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline QString QStringFromNSStringSafe(NSString *string) {
|
|
||||||
if (!string) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return QString::fromUtf8([string UTF8String]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@interface MenuActionController : NSObject
|
@interface MenuActionController : NSObject
|
||||||
@property (strong, nonatomic) NSURL *targetURL;
|
@property (strong, nonatomic) NSURL *targetURL;
|
||||||
- (void)openURLAction:(id)sender;
|
- (void)openURLAction:(id)sender;
|
||||||
@@ -300,6 +395,7 @@ static inline bool shouldPatchURL(const QString &host) {
|
|||||||
#ifdef BUILD_MODE_RMFAKECLOUD
|
#ifdef BUILD_MODE_RMFAKECLOUD
|
||||||
NSLogger(@"[reMarkable] Build mode: rmfakecloud");
|
NSLogger(@"[reMarkable] Build mode: rmfakecloud");
|
||||||
ReMarkableLoadOrCreateConfig();
|
ReMarkableLoadOrCreateConfig();
|
||||||
|
ReMarkableLoadSSLConfig();
|
||||||
NSLogger(@"[reMarkable] Using override host %@ and port %@", gConfiguredHost, gConfiguredPort);
|
NSLogger(@"[reMarkable] Using override host %@ and port %@", gConfiguredHost, gConfiguredPort);
|
||||||
|
|
||||||
[MemoryUtils hookSymbol:@"QtNetwork"
|
[MemoryUtils hookSymbol:@"QtNetwork"
|
||||||
@@ -424,6 +520,30 @@ extern "C" QNetworkReply* hooked_qNetworkAccessManager_createRequest(
|
|||||||
newUrl.setPort([gConfiguredPort intValue]);
|
newUrl.setPort([gConfiguredPort intValue]);
|
||||||
newReq.setUrl(newUrl);
|
newReq.setUrl(newUrl);
|
||||||
|
|
||||||
|
// Apply SSL configuration for mTLS / custom CA
|
||||||
|
if (gSSLInitialized) {
|
||||||
|
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||||
|
|
||||||
|
if (!gClientCert.isNull() && !gClientKey.isNull()) {
|
||||||
|
QList<QSslCertificate> localCerts = sslConfig.localCertificateChain();
|
||||||
|
localCerts.append(gClientCert);
|
||||||
|
sslConfig.setLocalCertificateChain(localCerts);
|
||||||
|
sslConfig.setPrivateKey(gClientKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gCACert.isNull()) {
|
||||||
|
QList<QSslCertificate> caCerts = sslConfig.caCertificates();
|
||||||
|
caCerts.append(gCACert);
|
||||||
|
sslConfig.setCaCertificates(caCerts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gDisableSSLVerification) {
|
||||||
|
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
newReq.setSslConfiguration(sslConfig);
|
||||||
|
}
|
||||||
|
|
||||||
if (original_qNetworkAccessManager_createRequest) {
|
if (original_qNetworkAccessManager_createRequest) {
|
||||||
return original_qNetworkAccessManager_createRequest(self, op, newReq, outgoingData);
|
return original_qNetworkAccessManager_createRequest(self, op, newReq, outgoingData);
|
||||||
}
|
}
|
||||||
@@ -454,6 +574,30 @@ extern "C" void hooked_qWebSocket_open(
|
|||||||
QNetworkRequest newReq(req);
|
QNetworkRequest newReq(req);
|
||||||
newReq.setUrl(newUrl);
|
newReq.setUrl(newUrl);
|
||||||
|
|
||||||
|
// Apply SSL configuration for mTLS / custom CA
|
||||||
|
if (gSSLInitialized) {
|
||||||
|
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||||
|
|
||||||
|
if (!gClientCert.isNull() && !gClientKey.isNull()) {
|
||||||
|
QList<QSslCertificate> localCerts = sslConfig.localCertificateChain();
|
||||||
|
localCerts.append(gClientCert);
|
||||||
|
sslConfig.setLocalCertificateChain(localCerts);
|
||||||
|
sslConfig.setPrivateKey(gClientKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gCACert.isNull()) {
|
||||||
|
QList<QSslCertificate> caCerts = sslConfig.caCertificates();
|
||||||
|
caCerts.append(gCACert);
|
||||||
|
sslConfig.setCaCertificates(caCerts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gDisableSSLVerification) {
|
||||||
|
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
newReq.setSslConfiguration(sslConfig);
|
||||||
|
}
|
||||||
|
|
||||||
original_qWebSocket_open(self, newReq);
|
original_qWebSocket_open(self, newReq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user