Move dev hooks to separate DevHooks files

Extracted development/reverse engineering hooks and helpers from reMarkable.m into new DevHooks.h and DevHooks.m files. Updated CMakeLists.txt to include the new files and their directory. This improves code organization and maintainability for development-only instrumentation.
This commit is contained in:
√(noham)²
2025-12-05 18:28:59 +01:00
parent 83b12e297e
commit 9322b0319e
4 changed files with 466 additions and 200 deletions

View File

@@ -4,6 +4,9 @@
#import "MemoryUtils.h"
#import "Logger.h"
#import "ResourceUtils.h"
#ifdef BUILD_MODE_DEV
#import "DevHooks.h"
#endif
#import <objc/runtime.h>
#import <Cocoa/Cocoa.h>
#include <stdint.h>
@@ -26,6 +29,7 @@
#include <QtCore/QVariant>
#include <QtCore/QAnyStringView>
static NSString *const kReMarkableConfigFileName = @"rmfakecloud.config";
static NSString *const kReMarkableConfigHostKey = @"host";
static NSString *const kReMarkableConfigPortKey = @"port";
@@ -248,63 +252,7 @@ static int (*original_qRegisterResourceData)(
const unsigned char *) = NULL;
#endif
#ifdef BUILD_MODE_DEV
static ssize_t (*original_qIODevice_write)(
QIODevice *self,
const char *data,
qint64 maxSize) = NULL;
// Hook for function at 0x10015A130
static int64_t (*original_function_at_0x10015A130)(int64_t a1, int64_t a2) = NULL;
// Hook for function at 0x10015BC90
static void (*original_function_at_0x10015BC90)(int64_t a1, int64_t a2) = NULL;
// Hook for function at 0x10016D520
static int64_t (*original_function_at_0x10016D520)(int64_t a1, int64_t *a2, unsigned int a3, int64_t a4) = NULL;
// Hook for function at 0x1001B6EE0
static void (*original_function_at_0x1001B6EE0)(int64_t a1, int64_t *a2, unsigned int a3) = NULL;
#endif
#if defined(BUILD_MODE_DEV)
// Memory logging helper function
static void logMemory(const char *label, void *address, size_t length) {
if (!address) {
NSLogger(@"[reMarkable] %s: (null)", label);
return;
}
unsigned char *ptr = (unsigned char *)address;
NSMutableString *hexLine = [NSMutableString stringWithFormat:@"[reMarkable] %s: ", label];
for (size_t i = 0; i < length; i++) {
[hexLine appendFormat:@"%02x ", ptr[i]];
if ((i + 1) % 16 == 0 && i < length - 1) {
NSLogger(@"%@", hexLine);
hexLine = [NSMutableString stringWithString:@"[reMarkable] "];
}
}
// Log remaining bytes if any
if ([hexLine length] > 28) { // More than just the prefix
NSLogger(@"%@", hexLine);
}
}
// Stack trace logging helper function
static void logStackTrace(const char *label) {
NSLogger(@"[reMarkable] %s - Stack trace:", label);
NSArray<NSString *> *callStack = [NSThread callStackSymbols];
NSUInteger count = [callStack count];
// Skip first 2 frames (this function and the immediate caller's logging statement)
for (NSUInteger i = 0; i < count; i++) {
NSString *frame = callStack[i];
NSLogger(@"[reMarkable] #%lu: %@", (unsigned long)i, frame);
}
}
#endif
#ifdef BUILD_MODE_RMFAKECLOUD
static inline bool shouldPatchURL(const QString &host) {
@@ -362,13 +310,6 @@ static inline bool shouldPatchURL(const QString &host) {
#endif
#ifdef BUILD_MODE_DEV
// // Hook function at address 0x1????
// [MemoryUtils hookAddress:@"reMarkable"
// staticAddress:0x????
// hookFunction:(void *)hooked_function_at_0x????
// originalFunction:(void **)&original_function_at_0x????
// logPrefix:@"[reMarkable]"];
NSLogger(@"[reMarkable] Build mode: dev/reverse engineering");
// [MemoryUtils hookSymbol:@"QtCore"
// symbolName:@"__ZN9QIODevice5writeEPKcx"
@@ -403,6 +344,29 @@ static inline bool shouldPatchURL(const QString &host) {
// hookFunction:(void *)hooked_function_at_0x1001B6EE0
// originalFunction:(void **)&original_function_at_0x1001B6EE0
// logPrefix:@"[reMarkable]"];
// PlatformHelpers.exportFile implementation WIP
// // Hook function at address 0x100011790
// [MemoryUtils hookAddress:@"reMarkable"
// staticAddress:0x100011790
// hookFunction:(void *)hooked_function_at_0x100011790
// originalFunction:(void **)&original_function_at_0x100011790
// logPrefix:@"[reMarkable]"];
// // Hook function at address 0x100011CE0
// [MemoryUtils hookAddress:@"reMarkable"
// staticAddress:0x100011CE0
// hookFunction:(void *)hooked_function_at_0x100011CE0
// originalFunction:(void **)&original_function_at_0x100011CE0
// logPrefix:@"[reMarkable]"];
// [MemoryUtils hookSymbol:@"QtQml"
// symbolName:@"__ZN11QQmlPrivate11qmlregisterENS_16RegistrationTypeEPv"
// hookFunction:(void *)hooked_qmlregister
// originalFunction:(void **)&original_qmlregister
// logPrefix:@"[reMarkable]"];
#endif
return YES;
@@ -509,141 +473,4 @@ extern "C" int hooked_qRegisterResourceData(
}
#endif // BUILD_MODE_QMLDIFF
#ifdef BUILD_MODE_DEV
extern "C" ssize_t hooked_qIODevice_write(
QIODevice *self,
const char *data,
qint64 maxSize) {
NSLogger(@"[reMarkable] QIODevice::write called with maxSize: %lld", (long long)maxSize);
// Log the call stack
logStackTrace("QIODevice::write call stack");
// Log the data to write
logMemory("Data to write", (void *)data, (size_t)(maxSize < 64 ? maxSize : 64));
if (original_qIODevice_write) {
ssize_t result = original_qIODevice_write(self, data, maxSize);
NSLogger(@"[reMarkable] QIODevice::write result: %zd", result);
return result;
}
NSLogger(@"[reMarkable] WARNING: Original QIODevice::write not available, returning 0");
return 0;
}
extern "C" int64_t hooked_function_at_0x10015A130(int64_t a1, int64_t a2) {
NSLogger(@"[reMarkable] Hook at 0x10015A130 called!");
NSLogger(@"[reMarkable] a1 = 0x%llx", (unsigned long long)a1);
NSLogger(@"[reMarkable] a2 = 0x%llx", (unsigned long long)a2);
logMemory("Memory at a1", (void *)a1, 64);
logMemory("Memory at a2", (void *)a2, 64);
if (original_function_at_0x10015A130) {
int64_t result = original_function_at_0x10015A130(a1, a2);
NSLogger(@"[reMarkable] result = 0x%llx", (unsigned long long)result);
return result;
}
NSLogger(@"[reMarkable] WARNING: Original function at 0x10015A130 not available, returning 0");
return 0;
}
extern "C" void hooked_function_at_0x10015BC90(int64_t a1, int64_t a2) {
NSLogger(@"[reMarkable] Hook at 0x10015BC90 called!");
NSLogger(@"[reMarkable] a1 = 0x%llx", (unsigned long long)a1);
NSLogger(@"[reMarkable] a2 = 0x%llx", (unsigned long long)a2);
logMemory("Memory at a1", (void *)a1, 64);
logMemory("Memory at a2", (void *)a2, 64);
if (original_function_at_0x10015BC90) {
original_function_at_0x10015BC90(a1, a2);
NSLogger(@"[reMarkable] original function returned (void)");
return;
}
NSLogger(@"[reMarkable] WARNING: Original function at 0x10015BC90 not available");
}
extern "C" int64_t hooked_function_at_0x10016D520(int64_t a1, int64_t *a2, unsigned int a3, int64_t a4) {
NSLogger(@"[reMarkable] Hook at 0x10016D520 called!");
NSLogger(@"[reMarkable] a1 = 0x%llx", (unsigned long long)a1);
NSLogger(@"[reMarkable] a2 = %p", a2);
if (a2) {
NSLogger(@"[reMarkable] *a2 = 0x%llx", (unsigned long long)*a2);
}
NSLogger(@"[reMarkable] a3 = %u (0x%x)", a3, a3);
NSLogger(@"[reMarkable] a4 = 0x%llx", (unsigned long long)a4);
// Log memory contents using helper function
logMemory("Memory at a1", (void *)a1, 64);
logMemory("Memory at a2", (void *)a2, 64);
if (a2 && *a2 != 0) {
logMemory("Memory at *a2", (void *)*a2, 64);
}
logMemory("Memory at a4", (void *)a4, 64);
if (original_function_at_0x10016D520) {
int64_t result = original_function_at_0x10016D520(a1, a2, a3, a4);
NSLogger(@"[reMarkable] result = 0x%llx", (unsigned long long)result);
return result;
}
NSLogger(@"[reMarkable] WARNING: Original function not available, returning 0");
return 0;
}
extern "C" void hooked_function_at_0x1001B6EE0(int64_t a1, int64_t *a2, unsigned int a3) {
NSLogger(@"[reMarkable] Hook at 0x1001B6EE0 called!");
NSLogger(@"[reMarkable] a1 = 0x%llx", (unsigned long long)a1);
// At a1 (PdfExporter object at 0x7ff4c17391e0):
// +0x10 0x000600043EC10 QString (likely document name)
NSLogger(@"[reMarkable] Reading QString at a1+0x10:");
logMemory("a1 + 0x10 (raw)", (void *)(a1 + 0x10), 64);
void **qstrPtr = (void **)(a1 + 0x10);
void *dataPtr = *qstrPtr;
if (!dataPtr) {
NSLogger(@"[reMarkable] QString has null data pointer");
return;
}
// try reading potential size fields near dataPtr
int32_t size = 0;
for (int delta = 4; delta <= 32; delta += 4) {
int32_t candidate = *(int32_t *)((char *)dataPtr - delta);
if (candidate > 0 && candidate < 10000) {
size = candidate;
NSLogger(@"[reMarkable] QString plausible size=%d (found at -%d)", size, delta);
break;
}
}
if (size > 0) {
NSString *qstringValue = [[NSString alloc] initWithCharacters:(unichar *)dataPtr length:size];
NSLogger(@"[reMarkable] QString value: \"%@\"", qstringValue);
} else {
NSLogger(@"[reMarkable] QString: could not find valid size");
}
NSLogger(@"[reMarkable] a2 = %p", a2);
if (a2) {
NSLogger(@"[reMarkable] *a2 = 0x%llx", (unsigned long long)*a2);
}
NSLogger(@"[reMarkable] a3 = %u (0x%x)", a3, a3);
if (original_function_at_0x1001B6EE0) {
original_function_at_0x1001B6EE0(a1, a2, a3);
NSLogger(@"[reMarkable] Original function at 0x1001B6EE0 executed");
} else {
NSLogger(@"[reMarkable] WARNING: Original function not available");
}
}
#endif // BUILD_MODE_DEV
@end