Add build script and hook updates

This commit is contained in:
√(noham)²
2026-05-15 19:44:54 +02:00
parent bb174602ba
commit 33f9772c90
8 changed files with 289 additions and 30 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
/src/.theos
/src/packages
.DS_Store
/rev
/rev

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Rivoirard Noham
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

29
script/build.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
# Default to 3.25.0 if no argument is provided
VERSION=${1:-3.25.0}
if [ "$VERSION" == "3.25.0" ]; then
MACRO="-DV3_25_0=1"
QT_VERSION="6.8.2"
elif [ "$VERSION" == "3.27.1" ]; then
MACRO="-DV3_27_1=1"
QT_VERSION="6.10.0"
else
echo "Error: Unknown version '$VERSION'. Supported versions are: 3.25.0, 3.27.1"
exit 1
fi
MODE=${2:-dev}
echo "Building for reMarkable version: $VERSION ($MACRO) in $MODE mode"
make clean
if [ "$MODE" == "release" ]; then
# Modify control file to set the version to match the target app version
sed -i '' "s/^Version: .*/Version: $VERSION/" control
make package THEOS_PACKAGE_SCHEME=rootless FINALPACKAGE=1 RM_VERSION_FLAG="$MACRO" QT_VERSION="$QT_VERSION"
else
make package THEOS_PACKAGE_SCHEME=rootless DEBUG=0 RM_VERSION_FLAG="$MACRO" QT_VERSION="$QT_VERSION"
fi

17
src/Config.h Normal file
View File

@@ -0,0 +1,17 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
extern NSString *gConfiguredHost;
extern NSNumber *gConfiguredPort;
#ifdef __cplusplus
extern "C" {
#endif
void loadConfiguration(void);
void saveConfiguration(NSString *host, NSNumber *port);
void showConfigAlert(void);
#ifdef __cplusplus
}
#endif

86
src/Config.mm Normal file
View File

@@ -0,0 +1,86 @@
#import "Config.h"
NSString *gConfiguredHost = @"";
NSNumber *gConfiguredPort = @(0);
void saveConfiguration(NSString *host, NSNumber *port) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:host forKey:@"RMHook_Host"];
[defaults setObject:port forKey:@"RMHook_Port"];
[defaults synchronize];
gConfiguredHost = host;
gConfiguredPort = port;
NSLog(@"[RMHook-iOS] Saved config - Host: %@, Port: %@", gConfiguredHost, gConfiguredPort);
}
void loadConfiguration() {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *host = [defaults stringForKey:@"RMHook_Host"];
NSNumber *port = [defaults objectForKey:@"RMHook_Port"];
if (host && host.length > 0 && port && [port intValue] > 0) {
gConfiguredHost = host;
gConfiguredPort = port;
NSLog(@"[RMHook-iOS] Loaded config - Host: %@, Port: %@", gConfiguredHost, gConfiguredPort);
}
}
void showConfigAlert() {
dispatch_async(dispatch_get_main_queue(), ^{
UIWindow *window = nil;
if (@available(iOS 13.0, *)) {
for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive) {
for (UIWindow *w in scene.windows) {
if (w.isKeyWindow) {
window = w;
break;
}
}
}
}
}
if (!window) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
window = [UIApplication sharedApplication].keyWindow;
#pragma clang diagnostic pop
}
if (!window || !window.rootViewController) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
showConfigAlert();
});
return;
}
UIViewController *rootVC = window.rootViewController;
while (rootVC.presentedViewController) {
rootVC = rootVC.presentedViewController;
}
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"RMHook"
message:@"First Launch: Enter Host and Port"
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"Host (e.g. example.com)";
textField.text = @"example.com";
}];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"Port (e.g. 443)";
textField.text = @"443";
textField.keyboardType = UIKeyboardTypeNumberPad;
}];
UIAlertAction *saveAction = [UIAlertAction actionWithTitle:@"Save" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSString *host = alert.textFields[0].text;
NSNumber *port = @([alert.textFields[1].text integerValue]);
saveConfiguration(host, port);
}];
[alert addAction:saveAction];
[rootVC presentViewController:alert animated:YES completion:nil];
});
}

View File

@@ -1,18 +1,20 @@
TARGET = iphone:latest:14.0
TARGET = iphone:latest:17.0
INSTALL_TARGET_PROCESSES = remarkable_mobile
ARCHS = arm64 arm64e
ARCHS = arm64
include $(THEOS)/makefiles/common.mk
TWEAK_NAME = RMHook
RMHook_FILES = Tweak.xm
RMHook_CFLAGS = -fobjc-arc -F$(HOME)/Qt/6.10.0/lib
RMHook_CXXFLAGS = -fobjc-arc -F$(HOME)/Qt/6.10.0/lib -std=c++17
ADDITIONAL_CFLAGS = -std=c++17 -Wno-c++17-extensions
ADDITIONAL_CXXFLAGS = -std=c++17 -Wno-c++17-extensions -DQT_NO_VERSION_TAGGING
ADDITIONAL_OBJCCFLAGS = -std=c++17 -Wno-c++17-extensions -DQT_NO_VERSION_TAGGING
RMHook_LDFLAGS =
RMHook_FRAMEWORKS = Foundation
QT_VERSION ?= 6.8.2
RMHook_FILES = Tweak.xm Config.mm
RMHook_CFLAGS = -fobjc-arc -F$(HOME)/Qt/$(QT_VERSION)/ios/lib
RMHook_CXXFLAGS = -fobjc-arc -F$(HOME)/Qt/$(QT_VERSION)/ios/lib -std=c++17 -DQT_NO_VERSION_TAGGING
ADDITIONAL_CFLAGS = -std=c++17 -Wno-c++17-extensions $(RM_VERSION_FLAG)
ADDITIONAL_CXXFLAGS = -std=c++17 -Wno-c++17-extensions -DQT_NO_VERSION_TAGGING $(RM_VERSION_FLAG)
ADDITIONAL_OBJCCFLAGS = -std=c++17 -Wno-c++17-extensions -DQT_NO_VERSION_TAGGING $(RM_VERSION_FLAG)
RMHook_FRAMEWORKS = Foundation QtNetwork QtCore QtWebSockets UIKit UniformTypeIdentifiers Network SystemConfiguration Security IOKit
RMHook_LDFLAGS = -F$(HOME)/Qt/$(QT_VERSION)/ios/lib -lz $(HOME)/Qt/$(QT_VERSION)/ios/lib/libQt6BundledPcre2.a
include $(THEOS_MAKE_PATH)/tweak.mk

View File

@@ -17,34 +17,127 @@
#include <QtCore/QVariant>
#include <QtCore/QAnyStringView>
#import <UIKit/UIKit.h>
#define TARGET_MODULE "remarkable_mobile"
#define IDA_BASE 0x100000000
// __ZN21QNetworkAccessManager13createRequestENS_9OperationERK15QNetworkRequestP9QIODevice
#if V3_25_0
#define QtNetworkAccessManager_createRequest 0x1017FB9F4 // sub_1017FB9F4
#elif V3_27_1
#define QtNetworkAccessManager_createRequest 0x10192472C // sub_10192472C
#endif
// __ZN10QWebSocket4openERK15QNetworkRequest
#if V3_25_0
# define QtWebSocket_open 0x100526A18 // sub_100526A18
#elif V3_27_1
# define QtWebSocket_open 0x100564A24 // sub_100564A24
#endif
// QObject *__fastcall QNetworkAccessManager::createRequest(
// QtSharedPointer::ExternalRefCountData *a1,
// __int64 a2,
// const QNetworkRequest *a3,
// __int64 a4)
static void *(*orig_createRequest)(void *a1, int a2, const void *a3, void *a4);
#import "Config.h"
void *hook_createRequest(void *a1, int a2, const void *a3, void *a4) {
NSLog(@"[RMHook-iOS] createRequest called");
return orig_createRequest(a1, a2, a3, a4);
static inline QString QStringFromNSStringSafe(NSString* str) {
if (!str) return QString();
return QString::fromNSString(str);
}
// void __fastcall QWebSocket::open(QWebSocket *this, const QUrl *a2, const QWebSocketHandshakeOptions *a3)
static void (*orig_open)(void *this_ptr, const void *a2, const void *a3);
static inline bool shouldPatchURL(const QString &host) {
if (host.isEmpty()) {
return false;
}
void hook_open(void *this_ptr, const void *a2, const void *a3) {
NSLog(@"[RMHook-iOS] QWebSocket::open called");
orig_open(this_ptr, a2, a3);
return QString(R"""(
hwr-production-dot-remarkable-production.appspot.com
service-manager-production-dot-remarkable-production.appspot.com
local.appspot.com
my.remarkable.com
ping.remarkable.com
internal.cloud.remarkable.com
eu.tectonic.remarkable.com
backtrace-proxy.cloud.remarkable.engineering
dev.ping.remarkable.com
dev.tectonic.remarkable.com
dev.internal.cloud.remarkable.com
eu.internal.tctn.cloud.remarkable.com
webapp-prod.cloud.remarkable.engineering
)""")
.contains(host, Qt::CaseInsensitive);
}
// QObject *__fastcall QNetworkAccessManager::createRequest(
// QtSharedPointer::ExternalRefCountData *self,
// __int64 op,
// const QNetworkRequest *req,
// __int64 outgoingData)
static QNetworkReply* (*original_qNetworkAccessManager_createRequest)(
QNetworkAccessManager* self,
QNetworkAccessManager::Operation op,
const QNetworkRequest& req,
QIODevice* outgoingData
);
QNetworkReply* hooked_qNetworkAccessManager_createRequest(
QNetworkAccessManager* self,
QNetworkAccessManager::Operation op,
const QNetworkRequest& req,
QIODevice* outgoingData
) {
NSLog(@"[RMHook-iOS] createRequest called for URL: %s", req.url().toString().toStdString().c_str());
const QString host = req.url().host();
if (shouldPatchURL(host)) {
QNetworkRequest newReq(req);
QUrl newUrl = req.url();
const QString overrideHost = QStringFromNSStringSafe(gConfiguredHost);
newUrl.setHost(overrideHost);
newUrl.setPort([gConfiguredPort intValue]);
newReq.setUrl(newUrl);
if (original_qNetworkAccessManager_createRequest) {
return original_qNetworkAccessManager_createRequest(self, op, newReq, outgoingData);
}
return nullptr;
}
if (original_qNetworkAccessManager_createRequest) {
return original_qNetworkAccessManager_createRequest(self, op, req, outgoingData);
}
return nullptr;
}
// void __fastcall QWebSocket::open(QWebSocket *self, const QNetworkRequest *req)
static void (*original_qWebSocket_open)(
QWebSocket* self,
const QNetworkRequest& req
);
void hooked_qWebSocket_open(
QWebSocket* self,
const QNetworkRequest& req
) {
NSLog(@"[RMHook-iOS] QWebSocket::open called for URL: %s", req.url().toString().toStdString().c_str());
if (!original_qWebSocket_open) {
return;
}
const QString host = req.url().host();
if (shouldPatchURL(host)) {
QUrl newUrl = req.url();
const QString overrideHost = QStringFromNSStringSafe(gConfiguredHost);
newUrl.setHost(overrideHost);
newUrl.setPort([gConfiguredPort intValue]);
QNetworkRequest newReq(req);
newReq.setUrl(newUrl);
original_qWebSocket_open(self, newReq);
return;
}
original_qWebSocket_open(self, req);
}
@@ -58,9 +151,20 @@ static uintptr_t findModuleBase(const char *moduleName) {
return 0;
}
// Constructor
%ctor {
@autoreleasepool {
loadConfiguration();
if (gConfiguredHost.length == 0 || [gConfiguredPort intValue] == 0) {
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * _Nonnull note) {
showConfigAlert();
}];
}
uintptr_t base = findModuleBase(TARGET_MODULE);
if (!base) {
NSLog(@"[RMHook-iOS] Module '%s' not found, tweak inactive.", TARGET_MODULE);
@@ -70,12 +174,12 @@ static uintptr_t findModuleBase(const char *moduleName) {
uintptr_t offset = QtNetworkAccessManager_createRequest - IDA_BASE;
uintptr_t addr = base + offset;
MSHookFunction((void *)addr, (void *)hook_createRequest, (void **)&orig_createRequest);
MSHookFunction((void *)addr, (void *)hooked_qNetworkAccessManager_createRequest, (void **)&original_qNetworkAccessManager_createRequest);
NSLog(@"[RMHook-iOS] Hooked QtNetworkAccessManager_createRequest @ 0x%lx (offset 0x%lx)", (unsigned long)addr, (unsigned long)offset);
uintptr_t offset2 = QtWebSocket_open - IDA_BASE;
uintptr_t addr2 = base + offset2;
MSHookFunction((void *)addr2, (void *)hook_open, (void **)&orig_open);
MSHookFunction((void *)addr2, (void *)hooked_qWebSocket_open, (void **)&original_qWebSocket_open);
NSLog(@"[RMHook-iOS] Hooked QtWebSocket_open @ 0x%lx (offset 0x%lx)", (unsigned long)addr2, (unsigned long)offset2);
}
}

View File

@@ -1,8 +1,8 @@
Package: xyz.noham.rmhook
Name: RMHook
Version: 0.0.1
Version: 3.27.1
Architecture: iphoneos-arm
Description: An awesome MobileSubstrate tweak!
Description: A tweak to hook remarkable mobile app.
Maintainer: NohamR
Author: NohamR
Section: Tweaks