diff --git a/.gitignore b/.gitignore
index dbcea13..e42cfa2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,4 @@
/paho-mqtt3as-proxy/x64
/x64
.DS_Store
-/docs
/src
diff --git a/README.md b/README.md
index ea752c0..c9e6ff8 100644
--- a/README.md
+++ b/README.md
@@ -14,12 +14,12 @@ RMHook-Win intercepts the reMarkable Desktop app's Qt networking layer and patch
## Compatibility
**Tested and working on:**
-
+
## Installation and usage
diff --git a/STATE.md b/STATE.md
deleted file mode 100644
index 3dafd61..0000000
--- a/STATE.md
+++ /dev/null
@@ -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::length+0x8 (Inline Function) (Inline Function)
-[0x3] paho_mqtt3as!std::basic_string,std::allocator >::{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
diff --git a/docs/latest.png b/docs/latest.png
new file mode 100644
index 0000000..637e507
Binary files /dev/null and b/docs/latest.png differ
diff --git a/docs/rm.png b/docs/rm.png
new file mode 100644
index 0000000..3a9b4ab
Binary files /dev/null and b/docs/rm.png differ
diff --git a/paho-mqtt3as-proxy/common.h b/paho-mqtt3as-proxy/common.h
index bec2b7d..3006a45 100644
--- a/paho-mqtt3as-proxy/common.h
+++ b/paho-mqtt3as-proxy/common.h
@@ -58,6 +58,62 @@ struct paho_mqtt3as_dll {
};
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();
diff --git a/paho-mqtt3as-proxy/exports.cpp b/paho-mqtt3as-proxy/exports.cpp
index ef90bf0..ea65b83 100644
--- a/paho-mqtt3as-proxy/exports.cpp
+++ b/paho-mqtt3as-proxy/exports.cpp
@@ -1,59 +1,9 @@
#include "common.h"
paho_mqtt3as_dll paho_mqtt3as;
-extern "C" FARPROC PA = NULL;
-extern "C"
-{
- extern "C" void FakeMQTTAsync_connect() {}
- extern "C" void FakeMQTTAsync_create() {}
- 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() {}
+extern "C" {
+#define DEFINE_FORWARDER_TARGET(name) FARPROC Original_##name = nullptr;
+ PAHO_FORWARDER_EXPORTS(DEFINE_FORWARDER_TARGET)
+#undef DEFINE_FORWARDER_TARGET
}
-
diff --git a/paho-mqtt3as-proxy/hook.cpp b/paho-mqtt3as-proxy/hook.cpp
index b99b729..3a8f74d 100644
--- a/paho-mqtt3as-proxy/hook.cpp
+++ b/paho-mqtt3as-proxy/hook.cpp
@@ -1,8 +1,17 @@
#include
#include
-#include "MinHook.h"
+#include
#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "MinHook.h"
+#include "common.h"
#include
#include
@@ -12,12 +21,9 @@
#include
#include
-#include
-#include
-#include
-#include
+#include
-static std::string GetLogPath()
+static bool GetLocalAppDataPath(std::filesystem::path& path)
{
char localAppData[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(
@@ -27,8 +33,18 @@ static std::string GetLogPath()
0,
localAppData)))
{
- std::filesystem::path dir =
- std::filesystem::path(localAppData) / "RMHook";
+ path = localAppData;
+ 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);
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(utf8.size()));
+}
-static int gConfiguredPort = 443;
+static QString QStringFromUtf8String(const std::string& value)
+{
+ return QString::fromUtf8(value.data(), static_cast(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(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(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(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()
{
- char localAppData[MAX_PATH];
- if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, localAppData)))
+ std::filesystem::path localAppData;
+ if (!GetLocalAppDataPath(localAppData))
{
- std::filesystem::path configPath = std::filesystem::path(localAppData) / "RMHook" / "config.json";
- QString qConfigPath = QString::fromStdString(configPath.string());
+ Log("[ERROR] Failed to resolve LOCALAPPDATA for config");
+ return;
+ }
- QFile file(qConfigPath);
- if (file.exists() && file.open(QIODevice::ReadOnly))
+ std::filesystem::path configPath = localAppData / "RMHook" / "config.json";
+ Log("[*] Config path: " + configPath.string());
+
+ std::error_code ec;
+ if (std::filesystem::exists(configPath, ec))
+ {
+ std::ifstream file(configPath, std::ios::binary);
+ if (!file.is_open())
{
- QByteArray data = file.readAll();
- file.close();
+ Log("[ERROR] Failed to open config for reading: " + configPath.string());
+ }
+ else
+ {
+ const std::string data(
+ (std::istreambuf_iterator(file)),
+ std::istreambuf_iterator());
- QJsonDocument doc = QJsonDocument::fromJson(data);
- if (!doc.isNull() && doc.isObject())
+ 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)
{
- QJsonObject obj = doc.object();
- if (obj.contains("host"))
- {
- gConfiguredHost = obj["host"].toString().toStdString();
- }
- if (obj.contains("port"))
- {
- gConfiguredPort = obj["port"].toInt();
- }
Log("[*] Loaded config: host=" + gConfiguredHost + ", port=" + std::to_string(gConfiguredPort));
return;
}
- }
- 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());
- if (file.open(QIODevice::WriteOnly))
- {
- QJsonObject obj;
- obj["host"] = QString::fromStdString(gConfiguredHost);
- obj["port"] = gConfiguredPort;
-
- QJsonDocument doc(obj);
- file.write(doc.toJson());
- file.close();
+ 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);
+
+ std::filesystem::create_directories(configPath.parent_path(), ec);
+ if (ec)
+ {
+ Log("[ERROR] Failed to create config directory: " + ec.message());
+ return;
+ }
+
+ const std::string json =
+ "{\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(json.size()));
+ file.close();
+ Log("[*] Created default config: " + configPath.string());
}
-static inline bool shouldPatchURL(const QString& host)
+static bool ShouldPatchHost(const QString& host)
{
if (host.isEmpty())
{
return false;
}
- 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
- vernemq-prod.cloud.remarkable.engineering
- vernemq-dev.cloud.remarkable.engineering
- )""")
- .contains(host, Qt::CaseInsensitive);
+ static constexpr const char* kPatchHosts[] = {
+ "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",
+ "vernemq-prod.cloud.remarkable.engineering",
+ "vernemq-dev.cloud.remarkable.engineering",
+ };
+
+ 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::Operation op,
const QNetworkRequest& req,
QIODevice* outgoingData
);
-typedef void (__fastcall* QWebSocket_Open_t)(
+using QWebSocket_Open_t = void (__fastcall*)(
QWebSocket* self,
const QNetworkRequest& req
);
@@ -140,7 +387,7 @@ typedef void (__fastcall* QWebSocket_Open_t)(
static QNAM_CreateRequest_t originalCreateRequest = nullptr;
static QWebSocket_Open_t originalWebSocketOpen = nullptr;
-QNetworkReply* __fastcall hookedCreateRequest(
+static QNetworkReply* __fastcall hookedCreateRequest(
QNetworkAccessManager* self,
QNetworkAccessManager::Operation op,
const QNetworkRequest& req,
@@ -150,49 +397,54 @@ QNetworkReply* __fastcall hookedCreateRequest(
const QUrl url = req.url();
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);
QUrl newUrl = url;
- newUrl.setHost(QString::fromStdString(gConfiguredHost));
- newUrl.setPort(gConfiguredPort);
+ ApplyConfiguredEndpoint(newUrl);
newReq.setUrl(newUrl);
- Log("[HTTP PATCHED] " + host.toStdString() + ":" + std::to_string(gConfiguredPort) + " -> " + newUrl.toString().toStdString());
- if (originalCreateRequest) {
+ LogUrlPatch("[HTTP PATCHED] ", host, gConfiguredPort, newUrl);
+ if (originalCreateRequest)
+ {
return originalCreateRequest(self, op, newReq, outgoingData);
}
return nullptr;
}
- if (originalCreateRequest) {
- return originalCreateRequest(self, op, req, outgoingData);
+ if (originalCreateRequest)
+ {
+ return originalCreateRequest(self, op, req, outgoingData);
}
return nullptr;
}
-void __fastcall hookedWebSocketOpen(
+static void __fastcall hookedWebSocketOpen(
QWebSocket* self,
const QNetworkRequest& req)
{
Log("[*] Intercepted QWebSocket::open");
- if (!originalWebSocketOpen) {
+ if (!originalWebSocketOpen)
+ {
return;
}
const QUrl url = req.url();
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;
- newUrl.setHost(QString::fromStdString(gConfiguredHost));
- newUrl.setPort(gConfiguredPort);
+ ApplyConfiguredEndpoint(newUrl);
QNetworkRequest newReq(req);
newReq.setUrl(newUrl);
- Log("[WS PATCHED] " + host.toStdString() + ":" + std::to_string(gConfiguredPort) + " -> " + newUrl.toString().toStdString());
+ LogUrlPatch("[WS PATCHED] ", host, gConfiguredPort, newUrl);
originalWebSocketOpen(self, newReq);
return;
}
@@ -200,87 +452,107 @@ void __fastcall hookedWebSocketOpen(
}
-typedef int(__cdecl* MQTTAsync_createWithOptions_t)(
- void** handle,
- const char* serverURI,
- const char* clientId,
- int persistence_type,
- void* persistence_context,
- void* options
+using MQTTAsync_createWithOptions_t = int (__cdecl*)(
+ void** handle,
+ const char* serverURI,
+ const char* clientId,
+ int persistence_type,
+ void* persistence_context,
+ 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"
// Returns patched string, or empty if no patch needed.
static std::string PatchMqttUri(const char* uri)
{
- if (!uri) return {};
- const std::string original(uri);
+ if (!uri)
+ {
+ return {};
+ }
- size_t schemeEnd = original.find("://");
- size_t hostStart = (schemeEnd != std::string::npos) ? schemeEnd + 3 : 0;
- size_t hostEnd = original.find_first_of(":/", hostStart);
- if (hostEnd == std::string::npos) hostEnd = original.size();
+ const std::string original(uri);
+ 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);
+ 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(std::tolower(ch)); });
- // Match any *.remarkable.com or *.remarkable.engineering host
- static const char* kSuffixes[] = {
- ".remarkable.com",
- ".remarkable.engineering",
- nullptr
- };
- bool shouldPatch = false;
- for (int i = 0; kSuffixes[i]; ++i)
- {
- const std::string suffix(kSuffixes[i]);
- if (origHost.size() >= suffix.size() &&
- origHost.compare(origHost.size() - suffix.size(),
- suffix.size(), suffix) == 0)
- {
- shouldPatch = true;
- break;
- }
- }
- if (!shouldPatch) return {};
+ static constexpr const char* kSuffixes[] = {
+ ".remarkable.com",
+ ".remarkable.engineering",
+ };
- std::string patched = original;
- patched.replace(hostStart, hostEnd - hostStart, gConfiguredHost);
+ bool shouldPatch = false;
+ for (const char* suffix : kSuffixes)
+ {
+ const size_t suffixLength = std::strlen(suffix);
+ if (hostLower.size() >= suffixLength &&
+ hostLower.compare(hostLower.size() - suffixLength, suffixLength, suffix) == 0)
+ {
+ shouldPatch = true;
+ break;
+ }
+ }
+ if (!shouldPatch)
+ {
+ return {};
+ }
- // Fix port
- size_t colonPos = patched.find(':', hostStart + gConfiguredHost.size());
- if (colonPos != std::string::npos)
- {
- size_t numEnd = patched.find_first_not_of("0123456789", colonPos + 1);
- if (numEnd == std::string::npos) numEnd = patched.size();
- patched.replace(colonPos + 1, numEnd - colonPos - 1,
- std::to_string(gConfiguredPort));
- }
- return patched;
+ std::string patched = original;
+ patched.replace(hostStart, hostEnd - hostStart, gConfiguredHost);
+
+ const std::string configuredPort = std::to_string(gConfiguredPort);
+ const size_t hostAfter = hostStart + gConfiguredHost.size();
+ if (hostAfter < patched.size() && patched[hostAfter] == ':')
+ {
+ const size_t colonPos = hostAfter;
+ size_t numEnd = patched.find_first_not_of("0123456789", colonPos + 1);
+ if (numEnd == std::string::npos)
+ {
+ numEnd = patched.size();
+ }
+ patched.replace(colonPos + 1, numEnd - colonPos - 1, configuredPort);
+ }
+ else
+ {
+ patched.insert(hostAfter, ":" + configuredPort);
+ }
+ return patched;
}
-int __cdecl hookedMQTTAsyncCreate(
- void** handle,
- const char* serverURI,
- const char* clientId,
- int persistence_type,
- void* persistence_context,
- void* options)
+extern "C" int __cdecl FakeMQTTAsync_createWithOptions(
+ void** handle,
+ const char* serverURI,
+ const char* clientId,
+ int persistence_type,
+ void* persistence_context,
+ 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);
- const char* finalUri = serverURI;
- std::string patched;
- patched = PatchMqttUri(serverURI);
+ auto originalMQTTAsyncCreate =
+ reinterpret_cast(paho_mqtt3as.OrignalMQTTAsync_createWithOptions);
+ if (!originalMQTTAsyncCreate)
+ {
+ Log("[ERROR] Original MQTTAsync_createWithOptions is not loaded");
+ return -1;
+ }
+
+ const char* finalUri = serverURI;
+ std::string patched = PatchMqttUri(serverURI);
if (!patched.empty())
{
finalUri = patched.c_str();
@@ -293,20 +565,25 @@ int __cdecl hookedMQTTAsyncCreate(
persistence_context,
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 -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);
if (!addr)
{
Log(std::string("[ERROR] Failed to resolve symbol: ") + symbol);
+ return nullptr;
}
Log(std::string("[+] Resolved ") + symbol + " at " + std::to_string(reinterpret_cast(addr)));
return addr;
@@ -326,9 +603,6 @@ void InstallHooks()
HMODULE qtNetwork = nullptr;
HMODULE qtWebSockets = nullptr;
- HMODULE mqttCLib = nullptr;
- // HMODULE mqttCppLib = nullptr;
- HMODULE mainModule = GetModuleHandleA(NULL);
while (!qtNetwork)
{
@@ -342,19 +616,7 @@ void InstallHooks()
Sleep(100);
}
- while (!mqttCLib)
- {
- mqttCLib = GetModuleHandleA("paho-mqtt3as.dll");
- Sleep(100);
- }
-
- // while (!mqttCppLib)
- // {
- // mqttCppLib = GetModuleHandleA("paho-mqttpp3.dll");
- // Sleep(100);
- // }
-
- Log("[+] Qt and MQTT DLLs loaded");
+ Log("[+] Qt DLLs loaded");
void* createRequestAddr = ResolveExport(
qtNetwork,
@@ -366,32 +628,11 @@ void InstallHooks()
"?open@QWebSocket@@QEAAXAEBVQNetworkRequest@@@Z"
);
- // void* asyncClientCtorAddr = ResolveExport(
- // mqttCppLib,
- // "??0async_client@mqtt@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0PEAViclient_persistence@1@@Z"
- // );
-
- // if (MH_CreateHook(
- // asyncClientCtorAddr,
- // &hookedAsyncClientCtor,
- // reinterpret_cast(&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(&originalMQTTAsyncCreate)
- ) != MH_OK)
- Log("[ERROR] Failed to hook MQTTAsync_createWithOptions");
- else
- Log("[+] Hooked MQTTAsync_createWithOptions");
-
- if (MH_CreateHook(
+ if (!createRequestAddr)
+ {
+ Log("[ERROR] Skipping createRequest hook because the symbol was not resolved");
+ }
+ else if (MH_CreateHook(
createRequestAddr,
&hookedCreateRequest,
reinterpret_cast(&originalCreateRequest)
@@ -404,7 +645,11 @@ void InstallHooks()
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,
&hookedWebSocketOpen,
reinterpret_cast(&originalWebSocketOpen)
@@ -425,4 +670,4 @@ void InstallHooks()
{
Log("[+] Hooks enabled");
}
-}
\ No newline at end of file
+}
diff --git a/paho-mqtt3as-proxy/main.cpp b/paho-mqtt3as-proxy/main.cpp
index b0aa6c1..723c7eb 100644
--- a/paho-mqtt3as-proxy/main.cpp
+++ b/paho-mqtt3as-proxy/main.cpp
@@ -2,6 +2,10 @@
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_create = GetProcAddress(paho_mqtt3as.dll, "MQTTAsync_create");
paho_mqtt3as.OrignalMQTTAsync_createWithOptions = GetProcAddress(paho_mqtt3as.dll, "MQTTAsync_createWithOptions");
diff --git a/paho-mqtt3as-proxy/paho-mqtt3as.asm b/paho-mqtt3as-proxy/paho-mqtt3as.asm
index 22a9c38..0e0e3c6 100644
--- a/paho-mqtt3as-proxy/paho-mqtt3as.asm
+++ b/paho-mqtt3as-proxy/paho-mqtt3as.asm
@@ -1,7 +1,61 @@
-.data
-extern PA : qword
.code
-RunASM proc
-jmp qword ptr [PA]
-RunASM endp
-end
+
+FORWARD_EXPORT MACRO fakeName, targetName
+EXTERN targetName:QWORD
+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
diff --git a/scripts/install-hook.ps1 b/scripts/install-hook.ps1
index 93c0264..cbe55b0 100644
--- a/scripts/install-hook.ps1
+++ b/scripts/install-hook.ps1
@@ -21,8 +21,11 @@ $OrigName = "paho-mqtt3as_orig.dll"
$TargetPath = Join-Path $InstallDir $TargetName
$OrigPath = Join-Path $InstallDir $OrigName
-$DefaultDebug = "C:\Users\noham\Documents\paho-mqtt3as-proxy\x64\Debug\paho-mqtt3as.dll"
-$DefaultRelease = "C:\Users\noham\Documents\paho-mqtt3as-proxy\x64\Release\paho-mqtt3as.dll"
+$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
+$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 {
"Actions:"
@@ -31,7 +34,7 @@ function Show-Help {
""
"Examples:"
" .\install-hook.ps1 -Action install"
- " .\install-hook.ps1 -Action install -SourcePath `"$DefaultDebug`""
+ " .\install-hook.ps1 -Action install -SourcePath `"$DefaultRelease`""
" .\install-hook.ps1 -Action restore"
}
@@ -63,11 +66,17 @@ function Install-Proxy {
$resolvedSource = $SourcePath
if (-not $resolvedSource) {
- if (Test-Path $DefaultDebug) {
+ if (Test-Path $DefaultRelease) {
+ $resolvedSource = $DefaultRelease
+ }
+ elseif (Test-Path $DefaultDebug) {
$resolvedSource = $DefaultDebug
}
- elseif (Test-Path $DefaultRelease) {
- $resolvedSource = $DefaultRelease
+ elseif (Test-Path $LegacyRelease) {
+ $resolvedSource = $LegacyRelease
+ }
+ elseif (Test-Path $LegacyDebug) {
+ $resolvedSource = $LegacyDebug
}
else {
Write-Error "No source DLL supplied and none found in default Debug/Release paths."
@@ -119,4 +128,4 @@ switch ($Action) {
"restore" { Restore-Original; break }
"help" { Show-Help; break }
default { Show-Help; break }
-}
\ No newline at end of file
+}