mirror of
https://github.com/NohamR/RMHook.git
synced 2026-01-10 06:28:12 +00:00
Compare commits
2 Commits
18abae42b7
...
15e5dabc37
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15e5dabc37 | ||
|
|
1619fda631 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
build/
|
build/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/.vscode
|
/.vscode
|
||||||
|
/research
|
||||||
|
|||||||
@@ -6,6 +6,14 @@ enable_language(OBJC OBJCXX)
|
|||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# Build mode options
|
||||||
|
# - rmfakecloud: Redirect reMarkable cloud to rmfakecloud server (default)
|
||||||
|
# - qmldiff: Qt resource data registration hooking (WIP)
|
||||||
|
# - dev: Development/reverse engineering mode with all hooks
|
||||||
|
option(BUILD_MODE_RMFAKECLOUD "Build with rmfakecloud support" ON)
|
||||||
|
option(BUILD_MODE_QMLDIFF "Build with QML diff/resource hooking" OFF)
|
||||||
|
option(BUILD_MODE_DEV "Build with dev/reverse engineering hooks" OFF)
|
||||||
|
|
||||||
# Compiler settings for macOS
|
# Compiler settings for macOS
|
||||||
set(CMAKE_MACOSX_RPATH 1)
|
set(CMAKE_MACOSX_RPATH 1)
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||||
@@ -93,8 +101,24 @@ set_target_properties(reMarkable PROPERTIES
|
|||||||
|
|
||||||
add_definitions(-DQT_NO_VERSION_TAGGING)
|
add_definitions(-DQT_NO_VERSION_TAGGING)
|
||||||
|
|
||||||
|
# Add build mode compile definitions
|
||||||
|
if(BUILD_MODE_RMFAKECLOUD)
|
||||||
|
target_compile_definitions(reMarkable PRIVATE BUILD_MODE_RMFAKECLOUD=1)
|
||||||
|
message(STATUS "Build mode: rmfakecloud (cloud redirection)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_MODE_QMLDIFF)
|
||||||
|
target_compile_definitions(reMarkable PRIVATE BUILD_MODE_QMLDIFF=1)
|
||||||
|
message(STATUS "Build mode: qmldiff (resource hooking)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_MODE_DEV)
|
||||||
|
target_compile_definitions(reMarkable PRIVATE BUILD_MODE_DEV=1)
|
||||||
|
message(STATUS "Build mode: dev (reverse engineering)")
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(reMarkable PRIVATE
|
target_link_libraries(reMarkable PRIVATE
|
||||||
${LIBS}
|
${LIBS}
|
||||||
${QT_LIB_TARGETS}
|
${QT_LIB_TARGETS}
|
||||||
/opt/homebrew/Cellar/libzip/1.11.4/lib/intel/libzstd.1.5.7.dylib
|
${PROJECT_ROOT_DIR}/libs/libzstd.1.dylib
|
||||||
)
|
)
|
||||||
22
README.md
22
README.md
@@ -37,6 +37,7 @@ Use the provided injection script:
|
|||||||
|
|
||||||
This script will:
|
This script will:
|
||||||
- Copy the dylib to the app bundle's Resources folder
|
- Copy the dylib to the app bundle's Resources folder
|
||||||
|
- Copy the `libzstd.1.dylib` dependency and fix library references
|
||||||
- Inject the load command into the executable using `optool`
|
- Inject the load command into the executable using `optool`
|
||||||
- Remove the code signature and resign with ad-hoc signature
|
- Remove the code signature and resign with ad-hoc signature
|
||||||
- Remove the `_MASReceipt` folder
|
- Remove the `_MASReceipt` folder
|
||||||
@@ -145,5 +146,24 @@ cd RMHook
|
|||||||
|
|
||||||
2. **Compile the dylib:**
|
2. **Compile the dylib:**
|
||||||
```bash
|
```bash
|
||||||
./scripts/build.sh
|
./scripts/build.sh [mode]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build modes
|
||||||
|
|
||||||
|
The build script supports different modes for various use cases:
|
||||||
|
|
||||||
|
| Mode | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `rmfakecloud` | Redirect reMarkable cloud to rmfakecloud server (default) |
|
||||||
|
| `qmldiff` | Qt resource data registration hooking (WIP) |
|
||||||
|
| `dev` | Development/reverse engineering mode with all hooks |
|
||||||
|
| `all` | Enable all modes |
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```bash
|
||||||
|
./scripts/build.sh # Build with rmfakecloud mode (default)
|
||||||
|
./scripts/build.sh rmfakecloud # Explicitly build rmfakecloud mode
|
||||||
|
./scripts/build.sh dev # Build with dev/reverse engineering hooks
|
||||||
|
./scripts/build.sh all # Build with all modes enabled
|
||||||
```
|
```
|
||||||
BIN
libs/libzstd.1.dylib
Normal file
BIN
libs/libzstd.1.dylib
Normal file
Binary file not shown.
@@ -1,14 +1,42 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Script to compile the reMarkable dylib
|
# Script to compile the reMarkable dylib with different build modes
|
||||||
|
|
||||||
|
# Build modes:
|
||||||
|
# rmfakecloud - Redirect reMarkable cloud to rmfakecloud server (default)
|
||||||
|
# qmldiff - Qt resource data registration hooking (WIP)
|
||||||
|
# dev - Development/reverse engineering mode with all hooks
|
||||||
|
|
||||||
# By default, compile reMarkable
|
|
||||||
APP_NAME=${1:-reMarkable}
|
|
||||||
PROJECT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
PROJECT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
|
||||||
# Qt path detection (adjust according to your installation)
|
# Qt path detection
|
||||||
QT_PATH=${QT_PATH:-"$HOME/Qt/6.10.0"}
|
QT_PATH=${QT_PATH:-$(ls -d "$HOME/Qt/6."* 2>/dev/null | sort -V | tail -n1)}
|
||||||
|
|
||||||
echo "🔨 Compiling $APP_NAME.dylib..."
|
# Parse build mode argument
|
||||||
|
BUILD_MODE=${1:-rmfakecloud}
|
||||||
|
|
||||||
|
# Set CMake options based on build mode
|
||||||
|
CMAKE_OPTIONS=""
|
||||||
|
case "$BUILD_MODE" in
|
||||||
|
rmfakecloud)
|
||||||
|
CMAKE_OPTIONS="-DBUILD_MODE_RMFAKECLOUD=ON -DBUILD_MODE_QMLDIFF=OFF -DBUILD_MODE_DEV=OFF"
|
||||||
|
;;
|
||||||
|
qmldiff)
|
||||||
|
CMAKE_OPTIONS="-DBUILD_MODE_RMFAKECLOUD=OFF -DBUILD_MODE_QMLDIFF=ON -DBUILD_MODE_DEV=OFF"
|
||||||
|
;;
|
||||||
|
dev)
|
||||||
|
CMAKE_OPTIONS="-DBUILD_MODE_RMFAKECLOUD=OFF -DBUILD_MODE_QMLDIFF=OFF -DBUILD_MODE_DEV=ON"
|
||||||
|
;;
|
||||||
|
all)
|
||||||
|
CMAKE_OPTIONS="-DBUILD_MODE_RMFAKECLOUD=ON -DBUILD_MODE_QMLDIFF=ON -DBUILD_MODE_DEV=ON"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "❌ Unknown build mode: $BUILD_MODE"
|
||||||
|
echo "Available modes: rmfakecloud (default), qmldiff, dev, all"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "🔨 Compiling reMarkable.dylib (mode: $BUILD_MODE)..."
|
||||||
echo "📦 Qt path: $QT_PATH"
|
echo "📦 Qt path: $QT_PATH"
|
||||||
|
|
||||||
# Create build directories if necessary
|
# Create build directories if necessary
|
||||||
@@ -17,21 +45,21 @@ cd "$PROJECT_DIR/build"
|
|||||||
|
|
||||||
# Configure with CMake and compile
|
# Configure with CMake and compile
|
||||||
if [ -d "$QT_PATH" ]; then
|
if [ -d "$QT_PATH" ]; then
|
||||||
cmake -DCMAKE_PREFIX_PATH="$QT_PATH" ..
|
cmake -DCMAKE_PREFIX_PATH="$QT_PATH" $CMAKE_OPTIONS ..
|
||||||
else
|
else
|
||||||
echo "⚠️ Qt not found at $QT_PATH, trying without specifying path..."
|
echo "⚠️ Qt not found at $QT_PATH, trying without specifying path..."
|
||||||
cmake ..
|
cmake $CMAKE_OPTIONS ..
|
||||||
fi
|
fi
|
||||||
|
|
||||||
make $APP_NAME
|
make reMarkable
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "✅ Compilation successful!"
|
echo "✅ Compilation successful!"
|
||||||
echo "📍 Dylib: $PROJECT_DIR/build/dylibs/$APP_NAME.dylib"
|
echo "📍 Dylib: $PROJECT_DIR/build/dylibs/reMarkable.dylib"
|
||||||
echo ""
|
echo ""
|
||||||
echo "🚀 To inject into the reMarkable application:"
|
echo "🚀 To inject into the reMarkable application:"
|
||||||
echo " DYLD_INSERT_LIBRARIES=\"$PROJECT_DIR/build/dylibs/$APP_NAME.dylib\" /Applications/reMarkable.app/Contents/MacOS/reMarkable"
|
echo " DYLD_INSERT_LIBRARIES=\"$PROJECT_DIR/build/dylibs/reMarkable.dylib\" /Applications/reMarkable.app/Contents/MacOS/reMarkable"
|
||||||
echo ""
|
echo ""
|
||||||
else
|
else
|
||||||
echo "❌ Compilation failed"
|
echo "❌ Compilation failed"
|
||||||
|
|||||||
@@ -63,7 +63,27 @@ mkdir -p "$APP_PATH/Contents/Resources/"
|
|||||||
cp "$DYLIB" "$APP_PATH/Contents/Resources/"
|
cp "$DYLIB" "$APP_PATH/Contents/Resources/"
|
||||||
echo "[INFO] Copied $DYLIB to $APP_PATH/Contents/Resources/"
|
echo "[INFO] Copied $DYLIB to $APP_PATH/Contents/Resources/"
|
||||||
|
|
||||||
optool install -c load -p "@executable_path/../Resources/$(basename "$DYLIB")" -t "$EXECUTABLE_PATH"
|
# Use optool from the scripts folder
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Copy libzstd dependency and fix the reference in reMarkable.dylib
|
||||||
|
LIBZSTD_PATH="$SCRIPT_DIR/../libs/libzstd.1.dylib"
|
||||||
|
if [ -f "$LIBZSTD_PATH" ]; then
|
||||||
|
cp "$LIBZSTD_PATH" "$APP_PATH/Contents/Resources/"
|
||||||
|
echo "[INFO] Copied libzstd.1.dylib to $APP_PATH/Contents/Resources/"
|
||||||
|
|
||||||
|
# Update the dylib reference to @executable_path/../Resources (handle multiple possible source paths)
|
||||||
|
DYLIB_IN_APP="$APP_PATH/Contents/Resources/$(basename "$DYLIB")"
|
||||||
|
install_name_tool -change "/usr/local/lib/libzstd.1.dylib" "@executable_path/../Resources/libzstd.1.dylib" "$DYLIB_IN_APP"
|
||||||
|
install_name_tool -change "/usr/local/opt/zstd/lib/libzstd.1.dylib" "@executable_path/../Resources/libzstd.1.dylib" "$DYLIB_IN_APP"
|
||||||
|
install_name_tool -change "/opt/homebrew/lib/libzstd.1.dylib" "@executable_path/../Resources/libzstd.1.dylib" "$DYLIB_IN_APP"
|
||||||
|
install_name_tool -change "/opt/homebrew/opt/zstd/lib/libzstd.1.dylib" "@executable_path/../Resources/libzstd.1.dylib" "$DYLIB_IN_APP"
|
||||||
|
echo "[INFO] Updated libzstd references in $(basename "$DYLIB")"
|
||||||
|
else
|
||||||
|
echo "[WARNING] libzstd.1.dylib not found at $LIBZSTD_PATH - app may fail on systems without zstd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$SCRIPT_DIR/optool" install -c load -p "@executable_path/../Resources/$(basename "$DYLIB")" -t "$EXECUTABLE_PATH"
|
||||||
echo "[INFO] Injected $DYLIB into $EXECUTABLE_PATH"
|
echo "[INFO] Injected $DYLIB into $EXECUTABLE_PATH"
|
||||||
|
|
||||||
sudo codesign --remove-signature "$EXECUTABLE_PATH"
|
sudo codesign --remove-signature "$EXECUTABLE_PATH"
|
||||||
|
|||||||
BIN
scripts/optool
Executable file
BIN
scripts/optool
Executable file
Binary file not shown.
@@ -214,18 +214,21 @@ static inline QString QStringFromNSStringSafe(NSString *string) {
|
|||||||
reMarkableDylib *dylib = [[reMarkableDylib alloc] init];
|
reMarkableDylib *dylib = [[reMarkableDylib alloc] init];
|
||||||
[dylib hook];
|
[dylib hook];
|
||||||
|
|
||||||
|
#ifdef BUILD_MODE_RMFAKECLOUD
|
||||||
// Add custom Help menu entry to open config file
|
// Add custom Help menu entry to open config file
|
||||||
NSString *configPath = ReMarkableConfigFilePath();
|
NSString *configPath = ReMarkableConfigFilePath();
|
||||||
NSString *fileURL = [NSString stringWithFormat:@"file://%@", configPath];
|
NSString *fileURL = [NSString stringWithFormat:@"file://%@", configPath];
|
||||||
[MenuActionController addCustomHelpMenuEntry:@"Open rmfakecloud config"
|
[MenuActionController addCustomHelpMenuEntry:@"Open rmfakecloud config"
|
||||||
withURL:fileURL
|
withURL:fileURL
|
||||||
withDelay:2.0];
|
withDelay:2.0];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation reMarkableDylib
|
@implementation reMarkableDylib
|
||||||
|
|
||||||
|
#ifdef BUILD_MODE_RMFAKECLOUD
|
||||||
static QNetworkReply *(*original_qNetworkAccessManager_createRequest)(
|
static QNetworkReply *(*original_qNetworkAccessManager_createRequest)(
|
||||||
QNetworkAccessManager *self,
|
QNetworkAccessManager *self,
|
||||||
QNetworkAccessManager::Operation op,
|
QNetworkAccessManager::Operation op,
|
||||||
@@ -235,13 +238,69 @@ static QNetworkReply *(*original_qNetworkAccessManager_createRequest)(
|
|||||||
static void (*original_qWebSocket_open)(
|
static void (*original_qWebSocket_open)(
|
||||||
QWebSocket *self,
|
QWebSocket *self,
|
||||||
const QNetworkRequest &request) = NULL;
|
const QNetworkRequest &request) = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BUILD_MODE_QMLDIFF
|
||||||
static int (*original_qRegisterResourceData)(
|
static int (*original_qRegisterResourceData)(
|
||||||
int,
|
int,
|
||||||
const unsigned char *,
|
const unsigned char *,
|
||||||
const unsigned char *,
|
const unsigned char *,
|
||||||
const unsigned char *) = NULL;
|
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 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) {
|
static inline bool shouldPatchURL(const QString &host) {
|
||||||
if (host.isEmpty()) {
|
if (host.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -264,10 +323,13 @@ static inline bool shouldPatchURL(const QString &host) {
|
|||||||
)""")
|
)""")
|
||||||
.contains(host, Qt::CaseInsensitive);
|
.contains(host, Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
- (BOOL)hook {
|
- (BOOL)hook {
|
||||||
NSLogger(@"[reMarkable] Starting hooks...");
|
NSLogger(@"[reMarkable] Starting hooks...");
|
||||||
|
|
||||||
|
#ifdef BUILD_MODE_RMFAKECLOUD
|
||||||
|
NSLogger(@"[reMarkable] Build mode: rmfakecloud");
|
||||||
ReMarkableLoadOrCreateConfig();
|
ReMarkableLoadOrCreateConfig();
|
||||||
NSLogger(@"[reMarkable] Using override host %@ and port %@", gConfiguredHost, gConfiguredPort);
|
NSLogger(@"[reMarkable] Using override host %@ and port %@", gConfiguredHost, gConfiguredPort);
|
||||||
|
|
||||||
@@ -282,17 +344,44 @@ static inline bool shouldPatchURL(const QString &host) {
|
|||||||
hookFunction:(void *)hooked_qWebSocket_open
|
hookFunction:(void *)hooked_qWebSocket_open
|
||||||
originalFunction:(void **)&original_qWebSocket_open
|
originalFunction:(void **)&original_qWebSocket_open
|
||||||
logPrefix:@"[reMarkable]"];
|
logPrefix:@"[reMarkable]"];
|
||||||
|
#endif
|
||||||
|
|
||||||
// WIP: Implement resource data registration hooking
|
#ifdef BUILD_MODE_QMLDIFF
|
||||||
// [MemoryUtils hookSymbol:@"QtCore"
|
NSLogger(@"[reMarkable] Build mode: qmldiff");
|
||||||
// symbolName:@"__Z21qRegisterResourceDataiPKhS0_S0_"
|
[MemoryUtils hookSymbol:@"QtCore"
|
||||||
// hookFunction:(void *)hooked_qRegisterResourceData
|
symbolName:@"__Z21qRegisterResourceDataiPKhS0_S0_"
|
||||||
// originalFunction:(void **)&original_qRegisterResourceData
|
hookFunction:(void *)hooked_qRegisterResourceData
|
||||||
// logPrefix:@"[reMarkable]"];
|
originalFunction:(void **)&original_qRegisterResourceData
|
||||||
|
logPrefix:@"[reMarkable]"];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BUILD_MODE_DEV
|
||||||
|
NSLogger(@"[reMarkable] Build mode: dev/reverse engineering");
|
||||||
|
[MemoryUtils hookSymbol:@"QtCore"
|
||||||
|
symbolName:@"__ZN9QIODevice5writeEPKcx"
|
||||||
|
hookFunction:(void *)hooked_qIODevice_write
|
||||||
|
originalFunction:(void **)&original_qIODevice_write
|
||||||
|
logPrefix:@"[reMarkable]"];
|
||||||
|
|
||||||
|
// Hook function at address 0x10016D520
|
||||||
|
[MemoryUtils hookAddress:@"reMarkable"
|
||||||
|
staticAddress:0x10016D520
|
||||||
|
hookFunction:(void *)hooked_function_at_0x10016D520
|
||||||
|
originalFunction:(void **)&original_function_at_0x10016D520
|
||||||
|
logPrefix:@"[reMarkable]"];
|
||||||
|
|
||||||
|
// Hook function at address 0x1001B6EE0
|
||||||
|
[MemoryUtils hookAddress:@"reMarkable"
|
||||||
|
staticAddress:0x1001B6EE0
|
||||||
|
hookFunction:(void *)hooked_function_at_0x1001B6EE0
|
||||||
|
originalFunction:(void **)&original_function_at_0x1001B6EE0
|
||||||
|
logPrefix:@"[reMarkable]"];
|
||||||
|
#endif
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BUILD_MODE_RMFAKECLOUD
|
||||||
extern "C" QNetworkReply* hooked_qNetworkAccessManager_createRequest(
|
extern "C" QNetworkReply* hooked_qNetworkAccessManager_createRequest(
|
||||||
QNetworkAccessManager* self,
|
QNetworkAccessManager* self,
|
||||||
QNetworkAccessManager::Operation op,
|
QNetworkAccessManager::Operation op,
|
||||||
@@ -345,7 +434,9 @@ extern "C" void hooked_qWebSocket_open(
|
|||||||
|
|
||||||
original_qWebSocket_open(self, req);
|
original_qWebSocket_open(self, req);
|
||||||
}
|
}
|
||||||
|
#endif // BUILD_MODE_RMFAKECLOUD
|
||||||
|
|
||||||
|
#ifdef BUILD_MODE_QMLDIFF
|
||||||
extern "C" int hooked_qRegisterResourceData(
|
extern "C" int hooked_qRegisterResourceData(
|
||||||
int version,
|
int version,
|
||||||
const unsigned char *tree,
|
const unsigned char *tree,
|
||||||
@@ -386,5 +477,108 @@ extern "C" int hooked_qRegisterResourceData(
|
|||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
#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_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
|
@end
|
||||||
@@ -30,4 +30,20 @@
|
|||||||
logPrefix:(NSString *)logPrefix
|
logPrefix:(NSString *)logPrefix
|
||||||
delayInSeconds:(NSTimeInterval)delayInSeconds;
|
delayInSeconds:(NSTimeInterval)delayInSeconds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks a function at a specific address after calculating ASLR slide.
|
||||||
|
*
|
||||||
|
* @param imageName The name of the image/library (e.g., "QtNetwork" or "reMarkable").
|
||||||
|
* @param staticAddress The static address from the binary (before ASLR).
|
||||||
|
* @param hookFunction The function to replace the original with.
|
||||||
|
* @param originalFunction Pointer to store the original function address.
|
||||||
|
* @param logPrefix Prefix for log messages (optional, can be nil).
|
||||||
|
* @return YES if the hook was successfully installed, NO otherwise.
|
||||||
|
*/
|
||||||
|
+ (BOOL)hookAddress:(NSString *)imageName
|
||||||
|
staticAddress:(uintptr_t)staticAddress
|
||||||
|
hookFunction:(void *)hookFunction
|
||||||
|
originalFunction:(void **)originalFunction
|
||||||
|
logPrefix:(NSString *)logPrefix;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -103,4 +103,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (BOOL)hookAddress:(NSString *)imageName
|
||||||
|
staticAddress:(uintptr_t)staticAddress
|
||||||
|
hookFunction:(void *)hookFunction
|
||||||
|
originalFunction:(void **)originalFunction
|
||||||
|
logPrefix:(NSString *)logPrefix {
|
||||||
|
|
||||||
|
NSLogger(@"%@ Starting hook installation at static address: 0x%lx", logPrefix, staticAddress);
|
||||||
|
|
||||||
|
int imageIndex = [self indexForImageWithName:imageName];
|
||||||
|
if (imageIndex < 0) {
|
||||||
|
NSLogger(@"%@ ERROR: Image %@ not found", logPrefix, imageName);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate ASLR slide
|
||||||
|
intptr_t slide = _dyld_get_image_vmaddr_slide(imageIndex);
|
||||||
|
NSLogger(@"%@ Image %@ ASLR slide: 0x%lx", logPrefix, imageName, slide);
|
||||||
|
|
||||||
|
// Calculate actual runtime address
|
||||||
|
void *actualAddress = (void *)(staticAddress + slide);
|
||||||
|
NSLogger(@"%@ Calculated runtime address: %p (static: 0x%lx + slide: 0x%lx)", logPrefix, actualAddress, staticAddress, slide);
|
||||||
|
|
||||||
|
int hookResult = tiny_hook(actualAddress, hookFunction, originalFunction);
|
||||||
|
|
||||||
|
if (hookResult == 0) {
|
||||||
|
NSLogger(@"%@ Hook successfully installed at address %p", logPrefix, actualAddress);
|
||||||
|
return YES;
|
||||||
|
} else {
|
||||||
|
NSLogger(@"%@ ERROR: Failed to install hook at address %p (code: %d)", logPrefix, actualAddress, hookResult);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ static NSString *ReMarkableDumpRootDirectory(void) {
|
|||||||
return dumpDirectory;
|
return dumpDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BUILD_MODE_QMLDIFF
|
||||||
uint32_t readUInt32(uint8_t *addr, int offset) {
|
uint32_t readUInt32(uint8_t *addr, int offset) {
|
||||||
return (uint32_t)(addr[offset + 0] << 24) |
|
return (uint32_t)(addr[offset + 0] << 24) |
|
||||||
(uint32_t)(addr[offset + 1] << 16) |
|
(uint32_t)(addr[offset + 1] << 16) |
|
||||||
@@ -379,3 +380,4 @@ void processNode(struct ResourceRoot *root, int node, const char *rootName) {
|
|||||||
ReMarkableDumpResourceFile(root, node, rootName ? rootName : "", nameBuffer, fileFlags);
|
ReMarkableDumpResourceFile(root, node, rootName ? rootName : "", nameBuffer, fileFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // BUILD_MODE_QMLDIFF
|
||||||
Reference in New Issue
Block a user