mirror of
https://github.com/NohamR/RMHook-Win.git
synced 2026-05-24 19:59:43 +00:00
Implement export forwarders and refactor hooks
Introduce a PAHO_FORWARDER_EXPORTS macro and generate Original_* FARPROC forwarder targets, replace the many hand-written fake exports with assembly forwarding stubs, and simplify exports.cpp to declare and initialize those forwarder pointers. Refactor paho-mqtt3as-proxy hook logic: improve logging, robustly load/save JSON config using std::filesystem and safe parsing, consolidate URL/host patching logic for HTTP/WS/MQTT, and add safer MQTT URI patching and resolution helpers. Update main to populate forwarder addresses at startup and make hook installation more defensive when symbols aren't found. Add docs images, update README (bump tested version and enable images), remove STATE.md, and adjust .gitignore to keep docs included.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,5 +2,4 @@
|
|||||||
/paho-mqtt3as-proxy/x64
|
/paho-mqtt3as-proxy/x64
|
||||||
/x64
|
/x64
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/docs
|
|
||||||
/src
|
/src
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ RMHook-Win intercepts the reMarkable Desktop app's Qt networking layer and patch
|
|||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
**Tested and working on:**
|
**Tested and working on:**
|
||||||
<!-- - reMarkable Desktop v3.27.0 (released 2026-06-05)
|
- reMarkable Desktop v3.27.1 (released 2026-06-05)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="docs/latest.png" width="40%" />
|
<img src="docs/latest.png" width="40%" />
|
||||||
<img src="docs/rm.png" width="50%" />
|
<img src="docs/rm.png" width="50%" />
|
||||||
</p> -->
|
</p>
|
||||||
|
|
||||||
## Installation and usage
|
## Installation and usage
|
||||||
|
|
||||||
|
|||||||
157
STATE.md
157
STATE.md
@@ -1,157 +0,0 @@
|
|||||||
Without hook
|
|
||||||
|
|
||||||
[*] Loaded config: host=rm.noh.am, port=443
|
|
||||||
[*] Initializing MinHook
|
|
||||||
[+] Qt and MQTT DLLs loaded
|
|
||||||
[+] Resolved ?createRequest@QNetworkAccessManager@@MEAAPEAVQNetworkReply@@W4Operation@1@AEBVQNetworkRequest@@PEAVQIODevice@@@Z at 140718018555616
|
|
||||||
[+] Resolved ?open@QWebSocket@@QEAAXAEBVQNetworkRequest@@@Z at 140718986708784
|
|
||||||
[+] Hooked createRequest
|
|
||||||
[+] Hooked QWebSocket::open
|
|
||||||
[+] Hooks enabled
|
|
||||||
|
|
||||||
|
|
||||||
(4d8.23e4): Access violation - code c0000005 (first chance)
|
|
||||||
First chance exceptions are reported before any exception handling.
|
|
||||||
This exception may be expected and handled.
|
|
||||||
ucrtbase!#strlen:
|
|
||||||
00007ffb`f6a9d040 38400402 ldrb w2,[x0],#0
|
|
||||||
|
|
||||||
|
|
||||||
[0x0] ucrtbase!#strlen 0x885a3fb920 0x7ffbf6a9d00c
|
|
||||||
[0x1] ucrtbase!#strlen_entry_thunk+0xc 0x885a3fb920 0x7ffb784faab6
|
|
||||||
[0x2] paho_mqttpp3!mqtt::exception::error_str+0x56 0x885a3fb930 0x7ffb784f5bad
|
|
||||||
[0x3] paho_mqttpp3!mqtt::exception::exception+0x2d 0x885a3fb9d0 0x7ffb784f54ef
|
|
||||||
[0x4] paho_mqttpp3!mqtt::async_client::async_client+0x29f 0x885a3fba40 0x7ffb784f582d
|
|
||||||
[0x5] paho_mqttpp3!mqtt::async_client::async_client+0x5d 0x885a3fbb10 0x7ff764ce0100
|
|
||||||
[0x6] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xf1fcf 0x885a3fbba0 0x7ff764cdb066
|
|
||||||
[0x7] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xecf35 0x885a3fbcd0 0x7ff764c6a51a
|
|
||||||
[0x8] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0x7c3e9 0x885a3fbd10 0x7ff764ca14f5
|
|
||||||
[0x9] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xb33c4 0x885a3fbd50 0x7ff764c90318
|
|
||||||
[0xa] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xa21e7 0x885a3fbe30 0x7ff764cbcaac
|
|
||||||
[0xb] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xce97b 0x885a3fbe90 0x7ff764cbbf0a
|
|
||||||
[0xc] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xcddd9 0x885a3fbf60 0x7ffb2a0f1a51
|
|
||||||
[0xd] Qt6Core!QObject::qt_static_metacall+0x1451 0x885a3fbfe0 0x7ffb2a0f4564
|
|
||||||
[0xe] Qt6Core!QMetaObject::activate+0x84 0x885a3fc110 0x7ff764c99964
|
|
||||||
[0xf] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xab833 0x885a3fc140 0x7ffb2a0fda9b
|
|
||||||
[0x10] Qt6Core!QMetaCallEvent::placeMetaCall+0x3b 0x885a3fc170 0x7ffb2a0fb8d2
|
|
||||||
[0x11] Qt6Core!QObject::event+0x182 0x885a3fc1b0 0x7ffb2a0b301b
|
|
||||||
[0x12] Qt6Core!QCoreApplication::notify+0xbb 0x885a3fc2c0 0x7ffb2a0b313f
|
|
||||||
[0x13] Qt6Core!QCoreApplication::notifyInternal2+0x10f 0x885a3fc310 0x7ffb2a0b584c
|
|
||||||
[0x14] Qt6Core!QCoreApplicationPrivate::sendPostedEvents+0x1fc 0x885a3fc380 0x7ffb2a23eb70
|
|
||||||
[0x15] Qt6Core!QEventDispatcherWin32::processEvents+0x90 0x885a3fc470 0x7ffb2a0b9c44
|
|
||||||
[0x16] Qt6Core!QEventLoop::exec+0x1c4 0x885a3ff5d0 0x7ffb2a19d90f
|
|
||||||
[0x17] Qt6Core!QThread::exec+0x16f 0x885a3ff670 0x7ffb2a245e29
|
|
||||||
[0x18] Qt6Core!QThread::start+0x579 0x885a3ff6e0 0x7ffbfa0809fc
|
|
||||||
[0x19] KERNEL32!$iexit_thunk$cdecl$i8$i8+0x1c 0x885a3ff780 0x7ffbfa017bb0
|
|
||||||
[0x1a] KERNEL32!#BaseThreadInitThunk+0x30 0x885a3ff7b0 0x7ffbfb67c4c8
|
|
||||||
[0x1b] ntdll!#RtlUserThreadStart+0x48 0x885a3ff7c0 0x0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
With MQTTAsync_createWithOptions from paho-mqtt3as_orig.dll hook to return patched url
|
|
||||||
|
|
||||||
[*] Loaded config: host=rm.noh.am, port=443
|
|
||||||
[*] Initializing MinHook
|
|
||||||
[+] Qt and MQTT DLLs loaded
|
|
||||||
[+] Resolved ?createRequest@QNetworkAccessManager@@MEAAPEAVQNetworkReply@@W4Operation@1@AEBVQNetworkRequest@@PEAVQIODevice@@@Z at 140718030679776
|
|
||||||
[+] Resolved ?open@QWebSocket@@QEAAXAEBVQNetworkRequest@@@Z at 140718986643248
|
|
||||||
[+] Resolved MQTTAsync_createWithOptions at 140718859938896
|
|
||||||
[+] Hooked MQTTAsync_createWithOptions
|
|
||||||
[+] Hooked createRequest
|
|
||||||
[+] Hooked QWebSocket::open
|
|
||||||
[+] Hooks enabled
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(1184.1c88): Access violation - code c0000005 (first chance)
|
|
||||||
First chance exceptions are reported before any exception handling.
|
|
||||||
This exception may be expected and handled.
|
|
||||||
ucrtbase!#strlen:
|
|
||||||
00007ffb`f6a9d040 38400402 ldrb w2,[x0],#0
|
|
||||||
|
|
||||||
|
|
||||||
[0x0] ucrtbase!#strlen 0x70df7fc050 0x7ffbf6a9d00c
|
|
||||||
[0x1] ucrtbase!#strlen_entry_thunk+0xc 0x70df7fc050 0x7ffb7b1daab6
|
|
||||||
[0x2] paho_mqttpp3!mqtt::exception::error_str+0x56 0x70df7fc060 0x7ffb7b1d5bad
|
|
||||||
[0x3] paho_mqttpp3!mqtt::exception::exception+0x2d 0x70df7fc100 0x7ffb7b1d54ef
|
|
||||||
[0x4] paho_mqttpp3!mqtt::async_client::async_client+0x29f 0x70df7fc170 0x7ffb7b1d582d
|
|
||||||
[0x5] paho_mqttpp3!mqtt::async_client::async_client+0x5d 0x70df7fc240 0x7ff764ce0100
|
|
||||||
[0x6] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xf1fcf 0x70df7fc2d0 0x7ff764cdb066
|
|
||||||
[0x7] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xecf35 0x70df7fc400 0x7ff764c6a51a
|
|
||||||
[0x8] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0x7c3e9 0x70df7fc440 0x7ff764ca14f5
|
|
||||||
[0x9] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xb33c4 0x70df7fc480 0x7ff764c90318
|
|
||||||
[0xa] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xa21e7 0x70df7fc560 0x7ff764cbcaac
|
|
||||||
[0xb] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xce97b 0x70df7fc5c0 0x7ff764cbbf0a
|
|
||||||
[0xc] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xcddd9 0x70df7fc690 0x7ffb29b81a51
|
|
||||||
[0xd] Qt6Core!QObject::qt_static_metacall+0x1451 0x70df7fc710 0x7ffb29b84564
|
|
||||||
[0xe] Qt6Core!QMetaObject::activate+0x84 0x70df7fc840 0x7ff764c99964
|
|
||||||
[0xf] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xab833 0x70df7fc870 0x7ffb29b8da9b
|
|
||||||
[0x10] Qt6Core!QMetaCallEvent::placeMetaCall+0x3b 0x70df7fc8a0 0x7ffb29b8b8d2
|
|
||||||
[0x11] Qt6Core!QObject::event+0x182 0x70df7fc8e0 0x7ffb29b4301b
|
|
||||||
[0x12] Qt6Core!QCoreApplication::notify+0xbb 0x70df7fc9f0 0x7ffb29b4313f
|
|
||||||
[0x13] Qt6Core!QCoreApplication::notifyInternal2+0x10f 0x70df7fca40 0x7ffb29b4584c
|
|
||||||
[0x14] Qt6Core!QCoreApplicationPrivate::sendPostedEvents+0x1fc 0x70df7fcab0 0x7ffb29cceb70
|
|
||||||
[0x15] Qt6Core!QEventDispatcherWin32::processEvents+0x90 0x70df7fcba0 0x7ffb29b49c44
|
|
||||||
[0x16] Qt6Core!QEventLoop::exec+0x1c4 0x70df7ffd00 0x7ffb29c2d90f
|
|
||||||
[0x17] Qt6Core!QThread::exec+0x16f 0x70df7ffda0 0x7ffb29cd5e29
|
|
||||||
[0x18] Qt6Core!QThread::start+0x579 0x70df7ffe10 0x7ffbfa0809fc
|
|
||||||
[0x19] KERNEL32!$iexit_thunk$cdecl$i8$i8+0x1c 0x70df7ffeb0 0x7ffbfa017bb0
|
|
||||||
[0x1a] KERNEL32!#BaseThreadInitThunk+0x30 0x70df7ffee0 0x7ffbfb67c4c8
|
|
||||||
[0x1b] ntdll!#RtlUserThreadStart+0x48 0x70df7ffef0 0x0
|
|
||||||
|
|
||||||
|
|
||||||
With MQTTAsync_createWithOptions from paho-mqtt3as.dll hook to return patched url
|
|
||||||
|
|
||||||
[*] Loaded config: host=rm.noh.am, port=443
|
|
||||||
[*] Initializing MinHook
|
|
||||||
[+] Qt and MQTT DLLs loaded
|
|
||||||
[+] Resolved ?createRequest@QNetworkAccessManager@@MEAAPEAVQNetworkReply@@W4Operation@1@AEBVQNetworkRequest@@PEAVQIODevice@@@Z at 140718064627424
|
|
||||||
[+] Resolved ?open@QWebSocket@@QEAAXAEBVQNetworkRequest@@@Z at 140719255209776
|
|
||||||
[+] Resolved MQTTAsync_createWithOptions at 140719258086256
|
|
||||||
[+] Hooked MQTTAsync_createWithOptions
|
|
||||||
[+] Hooked createRequest
|
|
||||||
[+] Hooked QWebSocket::open
|
|
||||||
[+] Hooks enabled
|
|
||||||
[MQTT] MQTTAsync_createWithOptions URI: ssl://vernemq-prod.cloud.remarkable.engineering:443 ClientId: noham-ccd226f9-4f37-4c45-a6ee-22f21f5e7548 PersistenceType: 1
|
|
||||||
[MQTT] originalMQTTAsyncCreate returned -1050472512 Final URI: ssl://rm.noh.am:443
|
|
||||||
|
|
||||||
|
|
||||||
(538.14bc): Access violation - code c0000005 (first chance)
|
|
||||||
First chance exceptions are reported before any exception handling.
|
|
||||||
This exception may be expected and handled.
|
|
||||||
ucrtbase!#strlen:
|
|
||||||
00007ffb`f6a9d040 38400402 ldrb w2,[x0],#0
|
|
||||||
|
|
||||||
|
|
||||||
[0x0] ucrtbase!#strlen 0x86d71fbcb0 0x7ffbf6a9d00c
|
|
||||||
[0x1] ucrtbase!#strlen_entry_thunk+0xc 0x86d71fbcb0 0x7ffbc1649348
|
|
||||||
[0x2] paho_mqtt3as!std::_Narrow_char_traits<char,int>::length+0x8 (Inline Function) (Inline Function)
|
|
||||||
[0x3] paho_mqtt3as!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::{ctor}+0x14 (Inline Function) (Inline Function)
|
|
||||||
[0x4] paho_mqtt3as!hookedMQTTAsyncCreate+0x88 0x86d71fbcc0 0x7ffba6a1aa95
|
|
||||||
[0x5] paho_mqttpp3!mqtt::exception::error_str+0x35 0x86d71fbe80 0x7ffba6a15bad
|
|
||||||
[0x6] paho_mqttpp3!mqtt::exception::exception+0x2d 0x86d71fbf20 0x7ffba6a154ef
|
|
||||||
[0x7] paho_mqttpp3!mqtt::async_client::async_client+0x29f 0x86d71fbf90 0x7ffba6a1582d
|
|
||||||
[0x8] paho_mqttpp3!mqtt::async_client::async_client+0x5d 0x86d71fc060 0x7ff764ce0100
|
|
||||||
[0x9] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xf1fcf 0x86d71fc0f0 0x7ff764cdb066
|
|
||||||
[0xa] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xecf35 0x86d71fc220 0x7ff764c6a51a
|
|
||||||
[0xb] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0x7c3e9 0x86d71fc260 0x7ff764ca14f5
|
|
||||||
[0xc] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xb33c4 0x86d71fc2a0 0x7ff764c90318
|
|
||||||
[0xd] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xa21e7 0x86d71fc380 0x7ff764cbcaac
|
|
||||||
[0xe] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xce97b 0x86d71fc3e0 0x7ff764cbbf0a
|
|
||||||
[0xf] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xcddd9 0x86d71fc4b0 0x7ffb29271a51
|
|
||||||
[0x10] Qt6Core!QObject::qt_static_metacall+0x1451 0x86d71fc530 0x7ffb29274564
|
|
||||||
[0x11] Qt6Core!QMetaObject::activate+0x84 0x86d71fc660 0x7ff764c99964
|
|
||||||
[0x12] reMarkable!rtc::Description::Entry::ExtMap::ExtMap+0xab833 0x86d71fc690 0x7ffb2927da9b
|
|
||||||
[0x13] Qt6Core!QMetaCallEvent::placeMetaCall+0x3b 0x86d71fc6c0 0x7ffb2927b8d2
|
|
||||||
[0x14] Qt6Core!QObject::event+0x182 0x86d71fc700 0x7ffb2923301b
|
|
||||||
[0x15] Qt6Core!QCoreApplication::notify+0xbb 0x86d71fc810 0x7ffb2923313f
|
|
||||||
[0x16] Qt6Core!QCoreApplication::notifyInternal2+0x10f 0x86d71fc860 0x7ffb2923584c
|
|
||||||
[0x17] Qt6Core!QCoreApplicationPrivate::sendPostedEvents+0x1fc 0x86d71fc8d0 0x7ffb293beb70
|
|
||||||
[0x18] Qt6Core!QEventDispatcherWin32::processEvents+0x90 0x86d71fc9c0 0x7ffb29239c44
|
|
||||||
[0x19] Qt6Core!QEventLoop::exec+0x1c4 0x86d71ffb20 0x7ffb2931d90f
|
|
||||||
[0x1a] Qt6Core!QThread::exec+0x16f 0x86d71ffbc0 0x7ffb293c5e29
|
|
||||||
[0x1b] Qt6Core!QThread::start+0x579 0x86d71ffc30 0x7ffbfa0809fc
|
|
||||||
[0x1c] KERNEL32!$iexit_thunk$cdecl$i8$i8+0x1c 0x86d71ffcd0 0x7ffbfa017bb0
|
|
||||||
[0x1d] KERNEL32!#BaseThreadInitThunk+0x30 0x86d71ffd00 0x7ffbfb67c4c8
|
|
||||||
[0x1e] ntdll!#RtlUserThreadStart+0x48 0x86d71ffd10 0x0
|
|
||||||
BIN
docs/latest.png
Normal file
BIN
docs/latest.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 272 KiB |
BIN
docs/rm.png
Normal file
BIN
docs/rm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 385 KiB |
@@ -58,6 +58,62 @@ struct paho_mqtt3as_dll {
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern paho_mqtt3as_dll paho_mqtt3as;
|
extern paho_mqtt3as_dll paho_mqtt3as;
|
||||||
extern "C" FARPROC PA;
|
|
||||||
|
#define PAHO_FORWARDER_EXPORTS(X) \
|
||||||
|
X(MQTTAsync_connect) \
|
||||||
|
X(MQTTAsync_create) \
|
||||||
|
X(MQTTAsync_destroy) \
|
||||||
|
X(MQTTAsync_disconnect) \
|
||||||
|
X(MQTTAsync_free) \
|
||||||
|
X(MQTTAsync_freeMessage) \
|
||||||
|
X(MQTTAsync_getPendingTokens) \
|
||||||
|
X(MQTTAsync_getVersionInfo) \
|
||||||
|
X(MQTTAsync_global_init) \
|
||||||
|
X(MQTTAsync_isComplete) \
|
||||||
|
X(MQTTAsync_isConnected) \
|
||||||
|
X(MQTTAsync_malloc) \
|
||||||
|
X(MQTTAsync_reconnect) \
|
||||||
|
X(MQTTAsync_send) \
|
||||||
|
X(MQTTAsync_sendMessage) \
|
||||||
|
X(MQTTAsync_setAfterPersistenceRead) \
|
||||||
|
X(MQTTAsync_setBeforePersistenceWrite) \
|
||||||
|
X(MQTTAsync_setCallbacks) \
|
||||||
|
X(MQTTAsync_setConnected) \
|
||||||
|
X(MQTTAsync_setConnectionLostCallback) \
|
||||||
|
X(MQTTAsync_setDeliveryCompleteCallback) \
|
||||||
|
X(MQTTAsync_setDisconnected) \
|
||||||
|
X(MQTTAsync_setMessageArrivedCallback) \
|
||||||
|
X(MQTTAsync_setTraceCallback) \
|
||||||
|
X(MQTTAsync_setTraceLevel) \
|
||||||
|
X(MQTTAsync_setUpdateConnectOptions) \
|
||||||
|
X(MQTTAsync_strerror) \
|
||||||
|
X(MQTTAsync_subscribe) \
|
||||||
|
X(MQTTAsync_subscribeMany) \
|
||||||
|
X(MQTTAsync_unsubscribe) \
|
||||||
|
X(MQTTAsync_unsubscribeMany) \
|
||||||
|
X(MQTTAsync_waitForCompletion) \
|
||||||
|
X(MQTTProperties_add) \
|
||||||
|
X(MQTTProperties_copy) \
|
||||||
|
X(MQTTProperties_free) \
|
||||||
|
X(MQTTProperties_getNumericValue) \
|
||||||
|
X(MQTTProperties_getNumericValueAt) \
|
||||||
|
X(MQTTProperties_getProperty) \
|
||||||
|
X(MQTTProperties_getPropertyAt) \
|
||||||
|
X(MQTTProperties_hasProperty) \
|
||||||
|
X(MQTTProperties_propertyCount) \
|
||||||
|
X(MQTTPropertyName) \
|
||||||
|
X(MQTTProperty_getType) \
|
||||||
|
X(MQTTReasonCode_toString) \
|
||||||
|
X(Thread_create_mutex) \
|
||||||
|
X(Thread_getid) \
|
||||||
|
X(Thread_lock_mutex) \
|
||||||
|
X(Thread_start) \
|
||||||
|
X(Thread_unlock_mutex)
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#define DECLARE_FORWARDER_TARGET(name) extern FARPROC Original_##name;
|
||||||
|
PAHO_FORWARDER_EXPORTS(DECLARE_FORWARDER_TARGET)
|
||||||
|
#undef DECLARE_FORWARDER_TARGET
|
||||||
|
}
|
||||||
|
|
||||||
void InstallHooks();
|
void InstallHooks();
|
||||||
|
|||||||
@@ -1,59 +1,9 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
paho_mqtt3as_dll paho_mqtt3as;
|
paho_mqtt3as_dll paho_mqtt3as;
|
||||||
extern "C" FARPROC PA = NULL;
|
|
||||||
|
|
||||||
extern "C"
|
extern "C" {
|
||||||
{
|
#define DEFINE_FORWARDER_TARGET(name) FARPROC Original_##name = nullptr;
|
||||||
extern "C" void FakeMQTTAsync_connect() {}
|
PAHO_FORWARDER_EXPORTS(DEFINE_FORWARDER_TARGET)
|
||||||
extern "C" void FakeMQTTAsync_create() {}
|
#undef DEFINE_FORWARDER_TARGET
|
||||||
extern "C" void FakeMQTTAsync_createWithOptions() {}
|
|
||||||
extern "C" void FakeMQTTAsync_destroy() {}
|
|
||||||
extern "C" void FakeMQTTAsync_disconnect() {}
|
|
||||||
extern "C" void FakeMQTTAsync_free() {}
|
|
||||||
extern "C" void FakeMQTTAsync_freeMessage() {}
|
|
||||||
extern "C" void FakeMQTTAsync_getPendingTokens() {}
|
|
||||||
extern "C" void FakeMQTTAsync_getVersionInfo() {}
|
|
||||||
extern "C" void FakeMQTTAsync_global_init() {}
|
|
||||||
extern "C" void FakeMQTTAsync_isComplete() {}
|
|
||||||
extern "C" void FakeMQTTAsync_isConnected() {}
|
|
||||||
extern "C" void FakeMQTTAsync_malloc() {}
|
|
||||||
extern "C" void FakeMQTTAsync_reconnect() {}
|
|
||||||
extern "C" void FakeMQTTAsync_send() {}
|
|
||||||
extern "C" void FakeMQTTAsync_sendMessage() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setAfterPersistenceRead() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setBeforePersistenceWrite() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setCallbacks() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setConnected() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setConnectionLostCallback() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setDeliveryCompleteCallback() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setDisconnected() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setMessageArrivedCallback() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setTraceCallback() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setTraceLevel() {}
|
|
||||||
extern "C" void FakeMQTTAsync_setUpdateConnectOptions() {}
|
|
||||||
extern "C" void FakeMQTTAsync_strerror() {}
|
|
||||||
extern "C" void FakeMQTTAsync_subscribe() {}
|
|
||||||
extern "C" void FakeMQTTAsync_subscribeMany() {}
|
|
||||||
extern "C" void FakeMQTTAsync_unsubscribe() {}
|
|
||||||
extern "C" void FakeMQTTAsync_unsubscribeMany() {}
|
|
||||||
extern "C" void FakeMQTTAsync_waitForCompletion() {}
|
|
||||||
extern "C" void FakeMQTTProperties_add() {}
|
|
||||||
extern "C" void FakeMQTTProperties_copy() {}
|
|
||||||
extern "C" void FakeMQTTProperties_free() {}
|
|
||||||
extern "C" void FakeMQTTProperties_getNumericValue() {}
|
|
||||||
extern "C" void FakeMQTTProperties_getNumericValueAt() {}
|
|
||||||
extern "C" void FakeMQTTProperties_getProperty() {}
|
|
||||||
extern "C" void FakeMQTTProperties_getPropertyAt() {}
|
|
||||||
extern "C" void FakeMQTTProperties_hasProperty() {}
|
|
||||||
extern "C" void FakeMQTTProperties_propertyCount() {}
|
|
||||||
extern "C" void FakeMQTTPropertyName() {}
|
|
||||||
extern "C" void FakeMQTTProperty_getType() {}
|
|
||||||
extern "C" void FakeMQTTReasonCode_toString() {}
|
|
||||||
extern "C" void FakeThread_create_mutex() {}
|
|
||||||
extern "C" void FakeThread_getid() {}
|
|
||||||
extern "C" void FakeThread_lock_mutex() {}
|
|
||||||
extern "C" void FakeThread_start() {}
|
|
||||||
extern "C" void FakeThread_unlock_mutex() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,17 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include "MinHook.h"
|
#include <iterator>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#include "MinHook.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
#include <QtNetwork/QNetworkAccessManager>
|
#include <QtNetwork/QNetworkAccessManager>
|
||||||
#include <QtNetwork/QNetworkRequest>
|
#include <QtNetwork/QNetworkRequest>
|
||||||
@@ -12,12 +21,9 @@
|
|||||||
|
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QByteArray>
|
||||||
#include <QtCore/QJsonDocument>
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
#include <QtCore/QJsonValue>
|
|
||||||
|
|
||||||
static std::string GetLogPath()
|
static bool GetLocalAppDataPath(std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
char localAppData[MAX_PATH];
|
char localAppData[MAX_PATH];
|
||||||
if (SUCCEEDED(SHGetFolderPathA(
|
if (SUCCEEDED(SHGetFolderPathA(
|
||||||
@@ -27,8 +33,18 @@ static std::string GetLogPath()
|
|||||||
0,
|
0,
|
||||||
localAppData)))
|
localAppData)))
|
||||||
{
|
{
|
||||||
std::filesystem::path dir =
|
path = localAppData;
|
||||||
std::filesystem::path(localAppData) / "RMHook";
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetLogPath()
|
||||||
|
{
|
||||||
|
std::filesystem::path localAppData;
|
||||||
|
if (GetLocalAppDataPath(localAppData))
|
||||||
|
{
|
||||||
|
std::filesystem::path dir = localAppData / "RMHook";
|
||||||
std::filesystem::create_directories(dir);
|
std::filesystem::create_directories(dir);
|
||||||
return (dir / "rmhook.log").string();
|
return (dir / "rmhook.log").string();
|
||||||
}
|
}
|
||||||
@@ -45,94 +61,325 @@ static void Log(const std::string& msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string SafeCString(const char* value)
|
||||||
|
{
|
||||||
|
return value ? std::string(value) : std::string("(null)");
|
||||||
|
}
|
||||||
|
|
||||||
static std::string gConfiguredHost = "example.com";
|
static std::string QStringToUtf8String(const QString& value)
|
||||||
|
{
|
||||||
|
const QByteArray utf8 = value.toUtf8();
|
||||||
|
if (utf8.isEmpty())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return std::string(utf8.constData(), static_cast<size_t>(utf8.size()));
|
||||||
|
}
|
||||||
|
|
||||||
static int gConfiguredPort = 443;
|
static QString QStringFromUtf8String(const std::string& value)
|
||||||
|
{
|
||||||
|
return QString::fromUtf8(value.data(), static_cast<qsizetype>(value.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LogHttpRequest(const char* prefix, const QUrl& url, QNetworkAccessManager::Operation op)
|
||||||
|
{
|
||||||
|
std::string message(prefix);
|
||||||
|
message += QStringToUtf8String(url.toString());
|
||||||
|
message += " (Method: ";
|
||||||
|
message += std::to_string(static_cast<int>(op));
|
||||||
|
message += ")";
|
||||||
|
Log(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LogUrlPatch(const char* prefix, const QString& originalHost, int port, const QUrl& newUrl)
|
||||||
|
{
|
||||||
|
std::string message(prefix);
|
||||||
|
message += QStringToUtf8String(originalHost);
|
||||||
|
message += ":";
|
||||||
|
message += std::to_string(port);
|
||||||
|
message += " -> ";
|
||||||
|
message += QStringToUtf8String(newUrl.toString());
|
||||||
|
Log(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr const char* kDefaultHost = "example.com";
|
||||||
|
static constexpr int kDefaultPort = 443;
|
||||||
|
|
||||||
|
static std::string gConfiguredHost = kDefaultHost;
|
||||||
|
static int gConfiguredPort = kDefaultPort;
|
||||||
|
|
||||||
|
static size_t SkipJsonWhitespace(const std::string& text, size_t pos)
|
||||||
|
{
|
||||||
|
while (pos < text.size() && std::isspace(static_cast<unsigned char>(text[pos])))
|
||||||
|
{
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool FindJsonValueStart(const std::string& text, const char* key, size_t& valueStart)
|
||||||
|
{
|
||||||
|
const std::string quotedKey = std::string("\"") + key + "\"";
|
||||||
|
size_t keyPos = text.find(quotedKey);
|
||||||
|
if (keyPos == std::string::npos)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t colonPos = text.find(':', keyPos + quotedKey.size());
|
||||||
|
if (colonPos == std::string::npos)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
valueStart = SkipJsonWhitespace(text, colonPos + 1);
|
||||||
|
return valueStart < text.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ReadJsonStringValue(const std::string& text, const char* key, std::string& value)
|
||||||
|
{
|
||||||
|
size_t pos = 0;
|
||||||
|
if (!FindJsonValueStart(text, key, pos) || text[pos] != '"')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
++pos;
|
||||||
|
std::string parsed;
|
||||||
|
while (pos < text.size())
|
||||||
|
{
|
||||||
|
char ch = text[pos++];
|
||||||
|
if (ch == '"')
|
||||||
|
{
|
||||||
|
value = parsed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '\\' && pos < text.size())
|
||||||
|
{
|
||||||
|
char escaped = text[pos++];
|
||||||
|
switch (escaped)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
case '\\':
|
||||||
|
case '/':
|
||||||
|
parsed.push_back(escaped);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
parsed.push_back('\b');
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
parsed.push_back('\f');
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
parsed.push_back('\n');
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
parsed.push_back('\r');
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
parsed.push_back('\t');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parsed.push_back(escaped);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parsed.push_back(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ReadJsonIntValue(const std::string& text, const char* key, int& value)
|
||||||
|
{
|
||||||
|
size_t pos = 0;
|
||||||
|
if (!FindJsonValueStart(text, key, pos))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* end = nullptr;
|
||||||
|
long parsed = std::strtol(text.c_str() + pos, &end, 10);
|
||||||
|
if (end == text.c_str() + pos)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = static_cast<int>(parsed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string JsonEscapeString(const std::string& value)
|
||||||
|
{
|
||||||
|
std::string escaped;
|
||||||
|
escaped.reserve(value.size());
|
||||||
|
for (char ch : value)
|
||||||
|
{
|
||||||
|
switch (ch)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
escaped += "\\\"";
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
escaped += "\\\\";
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
escaped += "\\b";
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
escaped += "\\f";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
escaped += "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
escaped += "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
escaped += "\\t";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
escaped.push_back(ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
|
||||||
static void LoadConfig()
|
static void LoadConfig()
|
||||||
{
|
{
|
||||||
char localAppData[MAX_PATH];
|
std::filesystem::path localAppData;
|
||||||
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, localAppData)))
|
if (!GetLocalAppDataPath(localAppData))
|
||||||
{
|
{
|
||||||
std::filesystem::path configPath = std::filesystem::path(localAppData) / "RMHook" / "config.json";
|
Log("[ERROR] Failed to resolve LOCALAPPDATA for config");
|
||||||
QString qConfigPath = QString::fromStdString(configPath.string());
|
return;
|
||||||
|
|
||||||
QFile file(qConfigPath);
|
|
||||||
if (file.exists() && file.open(QIODevice::ReadOnly))
|
|
||||||
{
|
|
||||||
QByteArray data = file.readAll();
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
|
||||||
if (!doc.isNull() && doc.isObject())
|
|
||||||
{
|
|
||||||
QJsonObject obj = doc.object();
|
|
||||||
if (obj.contains("host"))
|
|
||||||
{
|
|
||||||
gConfiguredHost = obj["host"].toString().toStdString();
|
|
||||||
}
|
}
|
||||||
if (obj.contains("port"))
|
|
||||||
|
std::filesystem::path configPath = localAppData / "RMHook" / "config.json";
|
||||||
|
Log("[*] Config path: " + configPath.string());
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
if (std::filesystem::exists(configPath, ec))
|
||||||
{
|
{
|
||||||
gConfiguredPort = obj["port"].toInt();
|
std::ifstream file(configPath, std::ios::binary);
|
||||||
|
if (!file.is_open())
|
||||||
|
{
|
||||||
|
Log("[ERROR] Failed to open config for reading: " + configPath.string());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::string data(
|
||||||
|
(std::istreambuf_iterator<char>(file)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
|
std::string host;
|
||||||
|
int port = 0;
|
||||||
|
bool readAnySetting = false;
|
||||||
|
if (ReadJsonStringValue(data, "host", host) && !host.empty())
|
||||||
|
{
|
||||||
|
gConfiguredHost = host;
|
||||||
|
readAnySetting = true;
|
||||||
|
}
|
||||||
|
if (ReadJsonIntValue(data, "port", port) && port > 0 && port <= 65535)
|
||||||
|
{
|
||||||
|
gConfiguredPort = port;
|
||||||
|
readAnySetting = true;
|
||||||
|
}
|
||||||
|
if (readAnySetting)
|
||||||
|
{
|
||||||
Log("[*] Loaded config: host=" + gConfiguredHost + ", port=" + std::to_string(gConfiguredPort));
|
Log("[*] Loaded config: host=" + gConfiguredHost + ", port=" + std::to_string(gConfiguredPort));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log("[ERROR] Config exists but no valid host or port was found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ec)
|
||||||
|
{
|
||||||
|
Log("[ERROR] Failed to check config existence: " + ec.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageBoxA(NULL, "First launch detected.\nUsing default config (example.com:443).\nYou can edit configuration in %LOCALAPPDATA%\\RMHook\\config.json", "RMHook Configuration", MB_OK | MB_ICONINFORMATION);
|
MessageBoxA(NULL, "First launch detected.\nUsing default config (example.com:443).\nYou can edit configuration in %LOCALAPPDATA%\\RMHook\\config.json", "RMHook Configuration", MB_OK | MB_ICONINFORMATION);
|
||||||
|
|
||||||
std::filesystem::create_directories(configPath.parent_path());
|
std::filesystem::create_directories(configPath.parent_path(), ec);
|
||||||
if (file.open(QIODevice::WriteOnly))
|
if (ec)
|
||||||
{
|
{
|
||||||
QJsonObject obj;
|
Log("[ERROR] Failed to create config directory: " + ec.message());
|
||||||
obj["host"] = QString::fromStdString(gConfiguredHost);
|
return;
|
||||||
obj["port"] = gConfiguredPort;
|
}
|
||||||
|
|
||||||
QJsonDocument doc(obj);
|
const std::string json =
|
||||||
file.write(doc.toJson());
|
"{\n"
|
||||||
|
" \"host\": \"" + JsonEscapeString(gConfiguredHost) + "\",\n"
|
||||||
|
" \"port\": " + std::to_string(gConfiguredPort) + "\n"
|
||||||
|
"}\n";
|
||||||
|
std::ofstream file(configPath, std::ios::binary | std::ios::trunc);
|
||||||
|
if (!file.is_open())
|
||||||
|
{
|
||||||
|
Log("[ERROR] Failed to open config for writing: " + configPath.string());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(json.data(), static_cast<std::streamsize>(json.size()));
|
||||||
file.close();
|
file.close();
|
||||||
}
|
Log("[*] Created default config: " + configPath.string());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool shouldPatchURL(const QString& host)
|
static bool ShouldPatchHost(const QString& host)
|
||||||
{
|
{
|
||||||
if (host.isEmpty())
|
if (host.isEmpty())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return QString(R"""(
|
static constexpr const char* kPatchHosts[] = {
|
||||||
hwr-production-dot-remarkable-production.appspot.com
|
"hwr-production-dot-remarkable-production.appspot.com",
|
||||||
service-manager-production-dot-remarkable-production.appspot.com
|
"service-manager-production-dot-remarkable-production.appspot.com",
|
||||||
local.appspot.com
|
"local.appspot.com",
|
||||||
my.remarkable.com
|
"my.remarkable.com",
|
||||||
ping.remarkable.com
|
"ping.remarkable.com",
|
||||||
internal.cloud.remarkable.com
|
"internal.cloud.remarkable.com",
|
||||||
eu.tectonic.remarkable.com
|
"eu.tectonic.remarkable.com",
|
||||||
backtrace-proxy.cloud.remarkable.engineering
|
"backtrace-proxy.cloud.remarkable.engineering",
|
||||||
dev.ping.remarkable.com
|
"dev.ping.remarkable.com",
|
||||||
dev.tectonic.remarkable.com
|
"dev.tectonic.remarkable.com",
|
||||||
dev.internal.cloud.remarkable.com
|
"dev.internal.cloud.remarkable.com",
|
||||||
eu.internal.tctn.cloud.remarkable.com
|
"eu.internal.tctn.cloud.remarkable.com",
|
||||||
webapp-prod.cloud.remarkable.engineering
|
"webapp-prod.cloud.remarkable.engineering",
|
||||||
vernemq-prod.cloud.remarkable.engineering
|
"vernemq-prod.cloud.remarkable.engineering",
|
||||||
vernemq-dev.cloud.remarkable.engineering
|
"vernemq-dev.cloud.remarkable.engineering",
|
||||||
)""")
|
};
|
||||||
.contains(host, Qt::CaseInsensitive);
|
|
||||||
|
for (const char* patchHost : kPatchHosts)
|
||||||
|
{
|
||||||
|
if (host.compare(QString::fromLatin1(patchHost), Qt::CaseInsensitive) == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ApplyConfiguredEndpoint(QUrl& url)
|
||||||
|
{
|
||||||
|
url.setHost(QStringFromUtf8String(gConfiguredHost));
|
||||||
|
url.setPort(gConfiguredPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef QNetworkReply* (__fastcall* QNAM_CreateRequest_t)(
|
using QNAM_CreateRequest_t = QNetworkReply* (__fastcall*)(
|
||||||
QNetworkAccessManager* self,
|
QNetworkAccessManager* self,
|
||||||
QNetworkAccessManager::Operation op,
|
QNetworkAccessManager::Operation op,
|
||||||
const QNetworkRequest& req,
|
const QNetworkRequest& req,
|
||||||
QIODevice* outgoingData
|
QIODevice* outgoingData
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef void (__fastcall* QWebSocket_Open_t)(
|
using QWebSocket_Open_t = void (__fastcall*)(
|
||||||
QWebSocket* self,
|
QWebSocket* self,
|
||||||
const QNetworkRequest& req
|
const QNetworkRequest& req
|
||||||
);
|
);
|
||||||
@@ -140,7 +387,7 @@ typedef void (__fastcall* QWebSocket_Open_t)(
|
|||||||
static QNAM_CreateRequest_t originalCreateRequest = nullptr;
|
static QNAM_CreateRequest_t originalCreateRequest = nullptr;
|
||||||
static QWebSocket_Open_t originalWebSocketOpen = nullptr;
|
static QWebSocket_Open_t originalWebSocketOpen = nullptr;
|
||||||
|
|
||||||
QNetworkReply* __fastcall hookedCreateRequest(
|
static QNetworkReply* __fastcall hookedCreateRequest(
|
||||||
QNetworkAccessManager* self,
|
QNetworkAccessManager* self,
|
||||||
QNetworkAccessManager::Operation op,
|
QNetworkAccessManager::Operation op,
|
||||||
const QNetworkRequest& req,
|
const QNetworkRequest& req,
|
||||||
@@ -150,49 +397,54 @@ QNetworkReply* __fastcall hookedCreateRequest(
|
|||||||
const QUrl url = req.url();
|
const QUrl url = req.url();
|
||||||
const QString host = url.host();
|
const QString host = url.host();
|
||||||
|
|
||||||
Log("[HTTP] Request to: " + url.toString().toStdString() + " (Method: " + std::to_string((int)op) + ")");
|
LogHttpRequest("[HTTP] Request to: ", url, op);
|
||||||
|
|
||||||
if (shouldPatchURL(host)) {
|
if (ShouldPatchHost(host))
|
||||||
|
{
|
||||||
QNetworkRequest newReq(req);
|
QNetworkRequest newReq(req);
|
||||||
QUrl newUrl = url;
|
QUrl newUrl = url;
|
||||||
newUrl.setHost(QString::fromStdString(gConfiguredHost));
|
ApplyConfiguredEndpoint(newUrl);
|
||||||
newUrl.setPort(gConfiguredPort);
|
|
||||||
newReq.setUrl(newUrl);
|
newReq.setUrl(newUrl);
|
||||||
Log("[HTTP PATCHED] " + host.toStdString() + ":" + std::to_string(gConfiguredPort) + " -> " + newUrl.toString().toStdString());
|
LogUrlPatch("[HTTP PATCHED] ", host, gConfiguredPort, newUrl);
|
||||||
if (originalCreateRequest) {
|
if (originalCreateRequest)
|
||||||
|
{
|
||||||
return originalCreateRequest(self, op, newReq, outgoingData);
|
return originalCreateRequest(self, op, newReq, outgoingData);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (originalCreateRequest) {
|
if (originalCreateRequest)
|
||||||
|
{
|
||||||
return originalCreateRequest(self, op, req, outgoingData);
|
return originalCreateRequest(self, op, req, outgoingData);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void __fastcall hookedWebSocketOpen(
|
static void __fastcall hookedWebSocketOpen(
|
||||||
QWebSocket* self,
|
QWebSocket* self,
|
||||||
const QNetworkRequest& req)
|
const QNetworkRequest& req)
|
||||||
{
|
{
|
||||||
Log("[*] Intercepted QWebSocket::open");
|
Log("[*] Intercepted QWebSocket::open");
|
||||||
if (!originalWebSocketOpen) {
|
if (!originalWebSocketOpen)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QUrl url = req.url();
|
const QUrl url = req.url();
|
||||||
const QString host = url.host();
|
const QString host = url.host();
|
||||||
|
|
||||||
Log("[WS] Opening: " + url.toString().toStdString());
|
std::string openMessage("[WS] Opening: ");
|
||||||
|
openMessage += QStringToUtf8String(url.toString());
|
||||||
|
Log(openMessage);
|
||||||
|
|
||||||
if (shouldPatchURL(host)) {
|
if (ShouldPatchHost(host))
|
||||||
|
{
|
||||||
QUrl newUrl = url;
|
QUrl newUrl = url;
|
||||||
newUrl.setHost(QString::fromStdString(gConfiguredHost));
|
ApplyConfiguredEndpoint(newUrl);
|
||||||
newUrl.setPort(gConfiguredPort);
|
|
||||||
QNetworkRequest newReq(req);
|
QNetworkRequest newReq(req);
|
||||||
newReq.setUrl(newUrl);
|
newReq.setUrl(newUrl);
|
||||||
Log("[WS PATCHED] " + host.toStdString() + ":" + std::to_string(gConfiguredPort) + " -> " + newUrl.toString().toStdString());
|
LogUrlPatch("[WS PATCHED] ", host, gConfiguredPort, newUrl);
|
||||||
originalWebSocketOpen(self, newReq);
|
originalWebSocketOpen(self, newReq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -200,7 +452,7 @@ void __fastcall hookedWebSocketOpen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef int(__cdecl* MQTTAsync_createWithOptions_t)(
|
using MQTTAsync_createWithOptions_t = int (__cdecl*)(
|
||||||
void** handle,
|
void** handle,
|
||||||
const char* serverURI,
|
const char* serverURI,
|
||||||
const char* clientId,
|
const char* clientId,
|
||||||
@@ -208,67 +460,74 @@ typedef int(__cdecl* MQTTAsync_createWithOptions_t)(
|
|||||||
void* persistence_context,
|
void* persistence_context,
|
||||||
void* options
|
void* options
|
||||||
);
|
);
|
||||||
static MQTTAsync_createWithOptions_t originalMQTTAsyncCreate = nullptr;
|
|
||||||
|
|
||||||
// typedef void* (__fastcall* async_client_ctor_t)(void* self, void* serverURI, void* clientId, void* persistence);
|
|
||||||
// static async_client_ctor_t originalAsyncClientCtor = nullptr;
|
|
||||||
|
|
||||||
// void* __fastcall hookedAsyncClientCtor(void* self, void* serverURI, void* clientId, void* persistence)
|
|
||||||
// {
|
|
||||||
// Log("[MQTT] Intercepted mqtt::async_client constructor");
|
|
||||||
// return self;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Patch a paho URI: "ssl://host.remarkable.com:port" -> "ssl://proxy:port"
|
// Patch a paho URI: "ssl://host.remarkable.com:port" -> "ssl://proxy:port"
|
||||||
// Returns patched string, or empty if no patch needed.
|
// Returns patched string, or empty if no patch needed.
|
||||||
static std::string PatchMqttUri(const char* uri)
|
static std::string PatchMqttUri(const char* uri)
|
||||||
{
|
{
|
||||||
if (!uri) return {};
|
if (!uri)
|
||||||
const std::string original(uri);
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
size_t schemeEnd = original.find("://");
|
const std::string original(uri);
|
||||||
size_t hostStart = (schemeEnd != std::string::npos) ? schemeEnd + 3 : 0;
|
const size_t schemeEnd = original.find("://");
|
||||||
|
const size_t hostStart = (schemeEnd != std::string::npos) ? schemeEnd + 3 : 0;
|
||||||
size_t hostEnd = original.find_first_of(":/", hostStart);
|
size_t hostEnd = original.find_first_of(":/", hostStart);
|
||||||
if (hostEnd == std::string::npos) hostEnd = original.size();
|
if (hostEnd == std::string::npos)
|
||||||
|
{
|
||||||
|
hostEnd = original.size();
|
||||||
|
}
|
||||||
|
|
||||||
const std::string origHost = original.substr(hostStart, hostEnd - hostStart);
|
const std::string origHost = original.substr(hostStart, hostEnd - hostStart);
|
||||||
|
std::string hostLower = origHost;
|
||||||
|
std::transform(hostLower.begin(), hostLower.end(), hostLower.begin(),
|
||||||
|
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
|
||||||
|
|
||||||
// Match any *.remarkable.com or *.remarkable.engineering host
|
static constexpr const char* kSuffixes[] = {
|
||||||
static const char* kSuffixes[] = {
|
|
||||||
".remarkable.com",
|
".remarkable.com",
|
||||||
".remarkable.engineering",
|
".remarkable.engineering",
|
||||||
nullptr
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool shouldPatch = false;
|
bool shouldPatch = false;
|
||||||
for (int i = 0; kSuffixes[i]; ++i)
|
for (const char* suffix : kSuffixes)
|
||||||
{
|
{
|
||||||
const std::string suffix(kSuffixes[i]);
|
const size_t suffixLength = std::strlen(suffix);
|
||||||
if (origHost.size() >= suffix.size() &&
|
if (hostLower.size() >= suffixLength &&
|
||||||
origHost.compare(origHost.size() - suffix.size(),
|
hostLower.compare(hostLower.size() - suffixLength, suffixLength, suffix) == 0)
|
||||||
suffix.size(), suffix) == 0)
|
|
||||||
{
|
{
|
||||||
shouldPatch = true;
|
shouldPatch = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!shouldPatch) return {};
|
if (!shouldPatch)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::string patched = original;
|
std::string patched = original;
|
||||||
patched.replace(hostStart, hostEnd - hostStart, gConfiguredHost);
|
patched.replace(hostStart, hostEnd - hostStart, gConfiguredHost);
|
||||||
|
|
||||||
// Fix port
|
const std::string configuredPort = std::to_string(gConfiguredPort);
|
||||||
size_t colonPos = patched.find(':', hostStart + gConfiguredHost.size());
|
const size_t hostAfter = hostStart + gConfiguredHost.size();
|
||||||
if (colonPos != std::string::npos)
|
if (hostAfter < patched.size() && patched[hostAfter] == ':')
|
||||||
{
|
{
|
||||||
|
const size_t colonPos = hostAfter;
|
||||||
size_t numEnd = patched.find_first_not_of("0123456789", colonPos + 1);
|
size_t numEnd = patched.find_first_not_of("0123456789", colonPos + 1);
|
||||||
if (numEnd == std::string::npos) numEnd = patched.size();
|
if (numEnd == std::string::npos)
|
||||||
patched.replace(colonPos + 1, numEnd - colonPos - 1,
|
{
|
||||||
std::to_string(gConfiguredPort));
|
numEnd = patched.size();
|
||||||
|
}
|
||||||
|
patched.replace(colonPos + 1, numEnd - colonPos - 1, configuredPort);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
patched.insert(hostAfter, ":" + configuredPort);
|
||||||
}
|
}
|
||||||
return patched;
|
return patched;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __cdecl hookedMQTTAsyncCreate(
|
extern "C" int __cdecl FakeMQTTAsync_createWithOptions(
|
||||||
void** handle,
|
void** handle,
|
||||||
const char* serverURI,
|
const char* serverURI,
|
||||||
const char* clientId,
|
const char* clientId,
|
||||||
@@ -276,11 +535,24 @@ int __cdecl hookedMQTTAsyncCreate(
|
|||||||
void* persistence_context,
|
void* persistence_context,
|
||||||
void* options)
|
void* options)
|
||||||
{
|
{
|
||||||
Log("[MQTT] MQTTAsync_createWithOptions URI: " + std::string(serverURI ? serverURI : "(null)") + " ClientId: " + (clientId ? clientId : "(null)") + " PersistenceType: " + std::to_string(persistence_type));
|
std::string message("[MQTT] MQTTAsync_createWithOptions URI: ");
|
||||||
|
message += SafeCString(serverURI);
|
||||||
|
message += " ClientId: ";
|
||||||
|
message += SafeCString(clientId);
|
||||||
|
message += " PersistenceType: ";
|
||||||
|
message += std::to_string(persistence_type);
|
||||||
|
Log(message);
|
||||||
|
|
||||||
|
auto originalMQTTAsyncCreate =
|
||||||
|
reinterpret_cast<MQTTAsync_createWithOptions_t>(paho_mqtt3as.OrignalMQTTAsync_createWithOptions);
|
||||||
|
if (!originalMQTTAsyncCreate)
|
||||||
|
{
|
||||||
|
Log("[ERROR] Original MQTTAsync_createWithOptions is not loaded");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
const char* finalUri = serverURI;
|
const char* finalUri = serverURI;
|
||||||
std::string patched;
|
std::string patched = PatchMqttUri(serverURI);
|
||||||
patched = PatchMqttUri(serverURI);
|
|
||||||
if (!patched.empty())
|
if (!patched.empty())
|
||||||
{
|
{
|
||||||
finalUri = patched.c_str();
|
finalUri = patched.c_str();
|
||||||
@@ -293,20 +565,25 @@ int __cdecl hookedMQTTAsyncCreate(
|
|||||||
persistence_context,
|
persistence_context,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
Log("[MQTT] originalMQTTAsyncCreate returned " + std::to_string(ret) + " Final URI: " + std::string(finalUri));
|
Log("[MQTT] originalMQTTAsyncCreate returned " + std::to_string(ret) + " Final URI: " + SafeCString(finalUri));
|
||||||
return ret;
|
return ret;
|
||||||
// return -1; // Return error to prevent actual MQTT connection attempts until we implement the full patch.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void* ResolveExport(HMODULE module, const char* symbol)
|
static void* ResolveExport(HMODULE module, const char* symbol)
|
||||||
{
|
{
|
||||||
if (!symbol) return nullptr;
|
if (!module || !symbol)
|
||||||
|
{
|
||||||
|
Log("[ERROR] ResolveExport called with null module or symbol");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void* addr = (void*)GetProcAddress(module, symbol);
|
void* addr = (void*)GetProcAddress(module, symbol);
|
||||||
if (!addr)
|
if (!addr)
|
||||||
{
|
{
|
||||||
Log(std::string("[ERROR] Failed to resolve symbol: ") + symbol);
|
Log(std::string("[ERROR] Failed to resolve symbol: ") + symbol);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
Log(std::string("[+] Resolved ") + symbol + " at " + std::to_string(reinterpret_cast<uintptr_t>(addr)));
|
Log(std::string("[+] Resolved ") + symbol + " at " + std::to_string(reinterpret_cast<uintptr_t>(addr)));
|
||||||
return addr;
|
return addr;
|
||||||
@@ -326,9 +603,6 @@ void InstallHooks()
|
|||||||
|
|
||||||
HMODULE qtNetwork = nullptr;
|
HMODULE qtNetwork = nullptr;
|
||||||
HMODULE qtWebSockets = nullptr;
|
HMODULE qtWebSockets = nullptr;
|
||||||
HMODULE mqttCLib = nullptr;
|
|
||||||
// HMODULE mqttCppLib = nullptr;
|
|
||||||
HMODULE mainModule = GetModuleHandleA(NULL);
|
|
||||||
|
|
||||||
while (!qtNetwork)
|
while (!qtNetwork)
|
||||||
{
|
{
|
||||||
@@ -342,19 +616,7 @@ void InstallHooks()
|
|||||||
Sleep(100);
|
Sleep(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!mqttCLib)
|
Log("[+] Qt DLLs loaded");
|
||||||
{
|
|
||||||
mqttCLib = GetModuleHandleA("paho-mqtt3as.dll");
|
|
||||||
Sleep(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// while (!mqttCppLib)
|
|
||||||
// {
|
|
||||||
// mqttCppLib = GetModuleHandleA("paho-mqttpp3.dll");
|
|
||||||
// Sleep(100);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Log("[+] Qt and MQTT DLLs loaded");
|
|
||||||
|
|
||||||
void* createRequestAddr = ResolveExport(
|
void* createRequestAddr = ResolveExport(
|
||||||
qtNetwork,
|
qtNetwork,
|
||||||
@@ -366,32 +628,11 @@ void InstallHooks()
|
|||||||
"?open@QWebSocket@@QEAAXAEBVQNetworkRequest@@@Z"
|
"?open@QWebSocket@@QEAAXAEBVQNetworkRequest@@@Z"
|
||||||
);
|
);
|
||||||
|
|
||||||
// void* asyncClientCtorAddr = ResolveExport(
|
if (!createRequestAddr)
|
||||||
// mqttCppLib,
|
{
|
||||||
// "??0async_client@mqtt@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0PEAViclient_persistence@1@@Z"
|
Log("[ERROR] Skipping createRequest hook because the symbol was not resolved");
|
||||||
// );
|
}
|
||||||
|
else if (MH_CreateHook(
|
||||||
// if (MH_CreateHook(
|
|
||||||
// asyncClientCtorAddr,
|
|
||||||
// &hookedAsyncClientCtor,
|
|
||||||
// reinterpret_cast<void**>(&originalAsyncClientCtor)
|
|
||||||
// ) != MH_OK)
|
|
||||||
// Log("[ERROR] Failed to hook mqtt::async_client constructor");
|
|
||||||
// else
|
|
||||||
// Log("[+] Hooked mqtt::async_client constructor");
|
|
||||||
|
|
||||||
void* mqttCreateAddr = ResolveExport(mqttCLib, "MQTTAsync_createWithOptions");
|
|
||||||
|
|
||||||
if (MH_CreateHook(
|
|
||||||
mqttCreateAddr,
|
|
||||||
&hookedMQTTAsyncCreate,
|
|
||||||
reinterpret_cast<void**>(&originalMQTTAsyncCreate)
|
|
||||||
) != MH_OK)
|
|
||||||
Log("[ERROR] Failed to hook MQTTAsync_createWithOptions");
|
|
||||||
else
|
|
||||||
Log("[+] Hooked MQTTAsync_createWithOptions");
|
|
||||||
|
|
||||||
if (MH_CreateHook(
|
|
||||||
createRequestAddr,
|
createRequestAddr,
|
||||||
&hookedCreateRequest,
|
&hookedCreateRequest,
|
||||||
reinterpret_cast<void**>(&originalCreateRequest)
|
reinterpret_cast<void**>(&originalCreateRequest)
|
||||||
@@ -404,7 +645,11 @@ void InstallHooks()
|
|||||||
Log("[+] Hooked createRequest");
|
Log("[+] Hooked createRequest");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MH_CreateHook(
|
if (!webSocketOpenAddr)
|
||||||
|
{
|
||||||
|
Log("[ERROR] Skipping QWebSocket::open hook because the symbol was not resolved");
|
||||||
|
}
|
||||||
|
else if (MH_CreateHook(
|
||||||
webSocketOpenAddr,
|
webSocketOpenAddr,
|
||||||
&hookedWebSocketOpen,
|
&hookedWebSocketOpen,
|
||||||
reinterpret_cast<void**>(&originalWebSocketOpen)
|
reinterpret_cast<void**>(&originalWebSocketOpen)
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
void LoadOriginalDllFunctions()
|
void LoadOriginalDllFunctions()
|
||||||
{
|
{
|
||||||
|
#define LOAD_FORWARDER_TARGET(name) Original_##name = GetProcAddress(paho_mqtt3as.dll, #name);
|
||||||
|
PAHO_FORWARDER_EXPORTS(LOAD_FORWARDER_TARGET)
|
||||||
|
#undef LOAD_FORWARDER_TARGET
|
||||||
|
|
||||||
paho_mqtt3as.OrignalMQTTAsync_connect = GetProcAddress(paho_mqtt3as.dll, "MQTTAsync_connect");
|
paho_mqtt3as.OrignalMQTTAsync_connect = GetProcAddress(paho_mqtt3as.dll, "MQTTAsync_connect");
|
||||||
paho_mqtt3as.OrignalMQTTAsync_create = GetProcAddress(paho_mqtt3as.dll, "MQTTAsync_create");
|
paho_mqtt3as.OrignalMQTTAsync_create = GetProcAddress(paho_mqtt3as.dll, "MQTTAsync_create");
|
||||||
paho_mqtt3as.OrignalMQTTAsync_createWithOptions = GetProcAddress(paho_mqtt3as.dll, "MQTTAsync_createWithOptions");
|
paho_mqtt3as.OrignalMQTTAsync_createWithOptions = GetProcAddress(paho_mqtt3as.dll, "MQTTAsync_createWithOptions");
|
||||||
|
|||||||
@@ -1,7 +1,61 @@
|
|||||||
.data
|
|
||||||
extern PA : qword
|
|
||||||
.code
|
.code
|
||||||
RunASM proc
|
|
||||||
jmp qword ptr [PA]
|
FORWARD_EXPORT MACRO fakeName, targetName
|
||||||
RunASM endp
|
EXTERN targetName:QWORD
|
||||||
end
|
PUBLIC fakeName
|
||||||
|
fakeName PROC
|
||||||
|
jmp QWORD PTR [targetName]
|
||||||
|
fakeName ENDP
|
||||||
|
ENDM
|
||||||
|
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_connect, Original_MQTTAsync_connect
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_create, Original_MQTTAsync_create
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_destroy, Original_MQTTAsync_destroy
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_disconnect, Original_MQTTAsync_disconnect
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_free, Original_MQTTAsync_free
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_freeMessage, Original_MQTTAsync_freeMessage
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_getPendingTokens, Original_MQTTAsync_getPendingTokens
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_getVersionInfo, Original_MQTTAsync_getVersionInfo
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_global_init, Original_MQTTAsync_global_init
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_isComplete, Original_MQTTAsync_isComplete
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_isConnected, Original_MQTTAsync_isConnected
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_malloc, Original_MQTTAsync_malloc
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_reconnect, Original_MQTTAsync_reconnect
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_send, Original_MQTTAsync_send
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_sendMessage, Original_MQTTAsync_sendMessage
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setAfterPersistenceRead, Original_MQTTAsync_setAfterPersistenceRead
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setBeforePersistenceWrite, Original_MQTTAsync_setBeforePersistenceWrite
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setCallbacks, Original_MQTTAsync_setCallbacks
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setConnected, Original_MQTTAsync_setConnected
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setConnectionLostCallback, Original_MQTTAsync_setConnectionLostCallback
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setDeliveryCompleteCallback, Original_MQTTAsync_setDeliveryCompleteCallback
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setDisconnected, Original_MQTTAsync_setDisconnected
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setMessageArrivedCallback, Original_MQTTAsync_setMessageArrivedCallback
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setTraceCallback, Original_MQTTAsync_setTraceCallback
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setTraceLevel, Original_MQTTAsync_setTraceLevel
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_setUpdateConnectOptions, Original_MQTTAsync_setUpdateConnectOptions
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_strerror, Original_MQTTAsync_strerror
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_subscribe, Original_MQTTAsync_subscribe
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_subscribeMany, Original_MQTTAsync_subscribeMany
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_unsubscribe, Original_MQTTAsync_unsubscribe
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_unsubscribeMany, Original_MQTTAsync_unsubscribeMany
|
||||||
|
FORWARD_EXPORT FakeMQTTAsync_waitForCompletion, Original_MQTTAsync_waitForCompletion
|
||||||
|
FORWARD_EXPORT FakeMQTTProperties_add, Original_MQTTProperties_add
|
||||||
|
FORWARD_EXPORT FakeMQTTProperties_copy, Original_MQTTProperties_copy
|
||||||
|
FORWARD_EXPORT FakeMQTTProperties_free, Original_MQTTProperties_free
|
||||||
|
FORWARD_EXPORT FakeMQTTProperties_getNumericValue, Original_MQTTProperties_getNumericValue
|
||||||
|
FORWARD_EXPORT FakeMQTTProperties_getNumericValueAt, Original_MQTTProperties_getNumericValueAt
|
||||||
|
FORWARD_EXPORT FakeMQTTProperties_getProperty, Original_MQTTProperties_getProperty
|
||||||
|
FORWARD_EXPORT FakeMQTTProperties_getPropertyAt, Original_MQTTProperties_getPropertyAt
|
||||||
|
FORWARD_EXPORT FakeMQTTProperties_hasProperty, Original_MQTTProperties_hasProperty
|
||||||
|
FORWARD_EXPORT FakeMQTTProperties_propertyCount, Original_MQTTProperties_propertyCount
|
||||||
|
FORWARD_EXPORT FakeMQTTPropertyName, Original_MQTTPropertyName
|
||||||
|
FORWARD_EXPORT FakeMQTTProperty_getType, Original_MQTTProperty_getType
|
||||||
|
FORWARD_EXPORT FakeMQTTReasonCode_toString, Original_MQTTReasonCode_toString
|
||||||
|
FORWARD_EXPORT FakeThread_create_mutex, Original_Thread_create_mutex
|
||||||
|
FORWARD_EXPORT FakeThread_getid, Original_Thread_getid
|
||||||
|
FORWARD_EXPORT FakeThread_lock_mutex, Original_Thread_lock_mutex
|
||||||
|
FORWARD_EXPORT FakeThread_start, Original_Thread_start
|
||||||
|
FORWARD_EXPORT FakeThread_unlock_mutex, Original_Thread_unlock_mutex
|
||||||
|
|
||||||
|
END
|
||||||
|
|||||||
@@ -21,8 +21,11 @@ $OrigName = "paho-mqtt3as_orig.dll"
|
|||||||
$TargetPath = Join-Path $InstallDir $TargetName
|
$TargetPath = Join-Path $InstallDir $TargetName
|
||||||
$OrigPath = Join-Path $InstallDir $OrigName
|
$OrigPath = Join-Path $InstallDir $OrigName
|
||||||
|
|
||||||
$DefaultDebug = "C:\Users\noham\Documents\paho-mqtt3as-proxy\x64\Debug\paho-mqtt3as.dll"
|
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||||
$DefaultRelease = "C:\Users\noham\Documents\paho-mqtt3as-proxy\x64\Release\paho-mqtt3as.dll"
|
$DefaultDebug = Join-Path $RepoRoot "paho-mqtt3as-proxy\x64\Debug\paho-mqtt3as.dll"
|
||||||
|
$DefaultRelease = Join-Path $RepoRoot "paho-mqtt3as-proxy\x64\Release\paho-mqtt3as.dll"
|
||||||
|
$LegacyDebug = Join-Path $RepoRoot "x64\Debug\paho-mqtt3as.dll"
|
||||||
|
$LegacyRelease = Join-Path $RepoRoot "x64\Release\paho-mqtt3as.dll"
|
||||||
|
|
||||||
function Show-Help {
|
function Show-Help {
|
||||||
"Actions:"
|
"Actions:"
|
||||||
@@ -31,7 +34,7 @@ function Show-Help {
|
|||||||
""
|
""
|
||||||
"Examples:"
|
"Examples:"
|
||||||
" .\install-hook.ps1 -Action install"
|
" .\install-hook.ps1 -Action install"
|
||||||
" .\install-hook.ps1 -Action install -SourcePath `"$DefaultDebug`""
|
" .\install-hook.ps1 -Action install -SourcePath `"$DefaultRelease`""
|
||||||
" .\install-hook.ps1 -Action restore"
|
" .\install-hook.ps1 -Action restore"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,11 +66,17 @@ function Install-Proxy {
|
|||||||
$resolvedSource = $SourcePath
|
$resolvedSource = $SourcePath
|
||||||
|
|
||||||
if (-not $resolvedSource) {
|
if (-not $resolvedSource) {
|
||||||
if (Test-Path $DefaultDebug) {
|
if (Test-Path $DefaultRelease) {
|
||||||
|
$resolvedSource = $DefaultRelease
|
||||||
|
}
|
||||||
|
elseif (Test-Path $DefaultDebug) {
|
||||||
$resolvedSource = $DefaultDebug
|
$resolvedSource = $DefaultDebug
|
||||||
}
|
}
|
||||||
elseif (Test-Path $DefaultRelease) {
|
elseif (Test-Path $LegacyRelease) {
|
||||||
$resolvedSource = $DefaultRelease
|
$resolvedSource = $LegacyRelease
|
||||||
|
}
|
||||||
|
elseif (Test-Path $LegacyDebug) {
|
||||||
|
$resolvedSource = $LegacyDebug
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Write-Error "No source DLL supplied and none found in default Debug/Release paths."
|
Write-Error "No source DLL supplied and none found in default Debug/Release paths."
|
||||||
|
|||||||
Reference in New Issue
Block a user