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 +}