mirror of
https://github.com/NohamR/RMHook-Win.git
synced 2026-05-24 19:59:43 +00:00
Add LICENSE, expand README; tidy proxy code
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Rivoirard Noham
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
119
README.md
119
README.md
@@ -1,9 +1,114 @@
|
||||
Set-ExecutionPolicy
|
||||
Run from an elevated PowerShell (script will attempt to relaunch elevated if not already) or right-click -> Run with PowerShell.
|
||||
Example: .\scripts\install-hook.ps1 -Action install
|
||||
To use a specific DLL: .\scripts\install-hook.ps1 -Action install -SourcePath "C:\path\to\your.dll"
|
||||
To restore original: .\scripts\install-hook.ps1 -Action restore
|
||||
# RMHook-Win
|
||||
|
||||
A Windows port of [RMHook](https://github.com/NohamR/RMHook) for the reMarkable Desktop application. This repo builds a proxy DLL that hooks Qt network APIs and redirects reMarkable cloud traffic to a self-hosted [rmfakecloud](https://github.com/ddvk/rmfakecloud) server.
|
||||
|
||||
install-hook.bat -Action install
|
||||
install-hook.bat -Action restore
|
||||
## Overview
|
||||
|
||||
RMHook-Win intercepts the reMarkable Desktop app's Qt networking layer and patches outgoing requests to the configured host and port. It is designed for the Windows reMarkable Desktop client and uses a DLL proxy for `paho-mqtt3as.dll`.
|
||||
|
||||
## Features
|
||||
|
||||
- Redirect reMarkable cloud HTTP(s) requests to a self-hosted rmfakecloud server
|
||||
- Patch Qt WebSocket connections used by the reMarkable app
|
||||
|
||||
## Compatibility
|
||||
|
||||
**Tested and working on:**
|
||||
<!-- - reMarkable Desktop v3.27.0 (released 2026-06-05)
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/latest.png" width="40%" />
|
||||
<img src="docs/rm.png" width="50%" />
|
||||
</p> -->
|
||||
|
||||
## Installation and usage
|
||||
|
||||
### Important legal note
|
||||
|
||||
⚠️ **For legal reasons, this repository does not include a pre-patched reMarkable app.** However, the latest compiled dylib is available in the [Releases](https://github.com/NohamR/RMHook-Win/releases/latest) section.
|
||||
|
||||
### Step 1: Build or obtain the proxy DLL
|
||||
|
||||
Build the `paho-mqtt3as-proxy` project with Visual Studio using `paho-mqtt3as-proxy.slnx`, or use an existing `paho-mqtt3as.dll` built from this repo.
|
||||
|
||||
### Step 2: Install the hook
|
||||
|
||||
Use the installer script from the `scripts` folder.
|
||||
Note: Run from an elevated PowerShell session. The installer script will request administrator privileges if needed.
|
||||
|
||||
From PowerShell:
|
||||
```powershell
|
||||
.\scripts\install-hook.ps1 -Action install
|
||||
```
|
||||
|
||||
Or with the batch wrapper:
|
||||
```cmd
|
||||
.\scripts\install-hook.bat -Action install
|
||||
```
|
||||
|
||||
If you want to install a custom DLL build:
|
||||
```powershell
|
||||
.\scripts\install-hook.ps1 -Action install -SourcePath "C:\path\to\paho-mqtt3as.dll"
|
||||
```
|
||||
|
||||
The script expects the Windows reMarkable install folder at:
|
||||
```text
|
||||
C:\Program Files\reMarkable
|
||||
```
|
||||
|
||||
### Step 3: Restore the original DLL
|
||||
|
||||
To remove the proxy and restore the original `paho-mqtt3as.dll`:
|
||||
```powershell
|
||||
.\scripts\install-hook.ps1 -Action restore
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Config path:
|
||||
```text
|
||||
%LOCALAPPDATA%\RMHook\config.json
|
||||
```
|
||||
|
||||
Example config:
|
||||
```json
|
||||
{
|
||||
"host": "your-server.example.com",
|
||||
"port": 443
|
||||
}
|
||||
```
|
||||
|
||||
If the config file does not exist, it will be created automatically with default values on first launch.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Hook install fails
|
||||
- Confirm the reMarkable install path is `C:\Program Files\reMarkable`
|
||||
- Run PowerShell as administrator
|
||||
- Verify the source DLL exists and is a valid proxy build
|
||||
|
||||
### App crashes or misbehaves
|
||||
- Restore the original DLL with `-Action restore`
|
||||
- Check the config file for valid JSON
|
||||
- Make sure the `host` and `port` values point to a reachable rmfakecloud server
|
||||
|
||||
## Credits
|
||||
|
||||
- MinHook: [TsudaKageyu/minhook](https://github.com/TsudaKageyu/minhook) - API hooking framework used by the project
|
||||
rmfakecloud: [ddvk/rmfakecloud](https://github.com/ddvk/rmfakecloud) - Self-hosted reMarkable cloud
|
||||
- xovi-rmfakecloud: [asivery/xovi-rmfakecloud](https://github.com/asivery/xovi-rmfakecloud) - Original hooking information
|
||||
- rm-xovi-extensions: [asivery/rm-xovi-extensions](https://github.com/asivery/rm-xovi-extensions) - Extension framework for reMarkable, used as reference for hooking Qt functions
|
||||
- [qt-resource-rebuilder](https://github.com/asivery/rm-xovi-extensions/tree/master/qt-resource-rebuilder)
|
||||
- [xovi-message-broker](https://github.com/asivery/rm-xovi-extensions/tree/master/xovi-message-broker)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This project is not affiliated with, endorsed by, or sponsored by reMarkable AS. Use at your own risk. This tool modifies the reMarkable Desktop application and may violate the application's terms of service.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit issues or pull requests.
|
||||
@@ -17,321 +17,277 @@
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonValue>
|
||||
|
||||
|
||||
static std::string GetLogPath()
|
||||
{
|
||||
char localAppData[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathA(
|
||||
NULL,
|
||||
CSIDL_LOCAL_APPDATA,
|
||||
NULL,
|
||||
0,
|
||||
localAppData)))
|
||||
{
|
||||
std::filesystem::path dir =
|
||||
std::filesystem::path(localAppData) / "RMHook";
|
||||
std::filesystem::create_directories(dir);
|
||||
return (dir / "rmhook.log").string();
|
||||
}
|
||||
return "rmhook.log";
|
||||
char localAppData[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathA(
|
||||
NULL,
|
||||
CSIDL_LOCAL_APPDATA,
|
||||
NULL,
|
||||
0,
|
||||
localAppData)))
|
||||
{
|
||||
std::filesystem::path dir =
|
||||
std::filesystem::path(localAppData) / "RMHook";
|
||||
std::filesystem::create_directories(dir);
|
||||
return (dir / "rmhook.log").string();
|
||||
}
|
||||
return "rmhook.log";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Logging
|
||||
// ------------------------------------------------------------
|
||||
|
||||
static void Log(const std::string& msg)
|
||||
{
|
||||
std::ofstream file(GetLogPath(), std::ios::app);
|
||||
if (file.is_open())
|
||||
{
|
||||
file << msg << std::endl;
|
||||
}
|
||||
std::ofstream file(GetLogPath(), std::ios::app);
|
||||
if (file.is_open())
|
||||
{
|
||||
file << msg << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Configuration
|
||||
// ------------------------------------------------------------
|
||||
|
||||
static std::string gConfiguredHost = "example.com";
|
||||
|
||||
static int gConfiguredPort = 443;
|
||||
|
||||
static void LoadConfig()
|
||||
{
|
||||
char localAppData[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, localAppData)))
|
||||
{
|
||||
std::filesystem::path configPath = std::filesystem::path(localAppData) / "RMHook" / "config.json";
|
||||
QString qConfigPath = QString::fromStdString(configPath.string());
|
||||
char localAppData[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, localAppData)))
|
||||
{
|
||||
std::filesystem::path configPath = std::filesystem::path(localAppData) / "RMHook" / "config.json";
|
||||
QString qConfigPath = QString::fromStdString(configPath.string());
|
||||
|
||||
QFile file(qConfigPath);
|
||||
if (file.exists() && file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QByteArray data = file.readAll();
|
||||
file.close();
|
||||
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")) gConfiguredPort = obj["port"].toInt();
|
||||
return;
|
||||
}
|
||||
}
|
||||
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"))
|
||||
{
|
||||
gConfiguredPort = obj["port"].toInt();
|
||||
}
|
||||
|
||||
// If we reach here, no config exists or it failed to load.
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Save defaults as JSON
|
||||
std::filesystem::create_directories(configPath.parent_path());
|
||||
if (file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj["host"] = QString::fromStdString(gConfiguredHost);
|
||||
obj["port"] = gConfiguredPort;
|
||||
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);
|
||||
|
||||
QJsonDocument doc(obj);
|
||||
file.write(doc.toJson());
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool shouldPatchURL(const QString& host) {
|
||||
if (host.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
static inline bool shouldPatchURL(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
|
||||
)""")
|
||||
.contains(host, Qt::CaseInsensitive);
|
||||
return QString(R"""(
|
||||
hwr-production-dot-remarkable-production.appspot.com
|
||||
service-manager-production-dot-remarkable-production.appspot.com
|
||||
local.appspot.com
|
||||
my.remarkable.com
|
||||
ping.remarkable.com
|
||||
internal.cloud.remarkable.com
|
||||
eu.tectonic.remarkable.com
|
||||
backtrace-proxy.cloud.remarkable.engineering
|
||||
dev.ping.remarkable.com
|
||||
dev.tectonic.remarkable.com
|
||||
dev.internal.cloud.remarkable.com
|
||||
eu.internal.tctn.cloud.remarkable.com
|
||||
webapp-prod.cloud.remarkable.engineering
|
||||
)""")
|
||||
.contains(host, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Original typedefs
|
||||
// ------------------------------------------------------------
|
||||
|
||||
typedef QNetworkReply* (__fastcall* QNAM_CreateRequest_t)(
|
||||
QNetworkAccessManager* self,
|
||||
QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest& req,
|
||||
QIODevice* outgoingData
|
||||
QNetworkAccessManager* self,
|
||||
QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest& req,
|
||||
QIODevice* outgoingData
|
||||
);
|
||||
|
||||
typedef void (__fastcall* QWebSocket_Open_t)(
|
||||
QWebSocket* self,
|
||||
const QNetworkRequest& req
|
||||
QWebSocket* self,
|
||||
const QNetworkRequest& req
|
||||
);
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Originals
|
||||
// ------------------------------------------------------------
|
||||
|
||||
static QNAM_CreateRequest_t originalCreateRequest = nullptr;
|
||||
static QWebSocket_Open_t originalWebSocketOpen = nullptr;
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Hooked createRequest
|
||||
// ------------------------------------------------------------
|
||||
|
||||
QNetworkReply* __fastcall hookedCreateRequest(
|
||||
QNetworkAccessManager* self,
|
||||
QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest& req,
|
||||
QIODevice* outgoingData
|
||||
QNetworkAccessManager* self,
|
||||
QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest& req,
|
||||
QIODevice* outgoingData
|
||||
)
|
||||
{
|
||||
const QString host = req.url().host();
|
||||
if (shouldPatchURL(host)) {
|
||||
QNetworkRequest newReq(req);
|
||||
QUrl newUrl = req.url();
|
||||
newUrl.setHost(QString::fromStdString(gConfiguredHost));
|
||||
newUrl.setPort(gConfiguredPort);
|
||||
newReq.setUrl(newUrl);
|
||||
const QString host = req.url().host();
|
||||
if (shouldPatchURL(host)) {
|
||||
QNetworkRequest newReq(req);
|
||||
QUrl newUrl = req.url();
|
||||
newUrl.setHost(QString::fromStdString(gConfiguredHost));
|
||||
newUrl.setPort(gConfiguredPort);
|
||||
newReq.setUrl(newUrl);
|
||||
|
||||
Log("[HTTP PATCHED] " + host.toStdString() + " -> " + newUrl.toString().toStdString());
|
||||
Log("[HTTP PATCHED] " + host.toStdString() + " -> " + newUrl.toString().toStdString());
|
||||
|
||||
if (originalCreateRequest) {
|
||||
return originalCreateRequest(self, op, newReq, outgoingData);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
if (originalCreateRequest) {
|
||||
return originalCreateRequest(self, op, newReq, outgoingData);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (originalCreateRequest) {
|
||||
return originalCreateRequest(self, op, req, outgoingData);
|
||||
}
|
||||
return nullptr;
|
||||
if (originalCreateRequest) {
|
||||
return originalCreateRequest(self, op, req, outgoingData);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Hooked websocket open
|
||||
// ------------------------------------------------------------
|
||||
|
||||
void __fastcall hookedWebSocketOpen(
|
||||
QWebSocket* self,
|
||||
const QNetworkRequest& req
|
||||
QWebSocket* self,
|
||||
const QNetworkRequest& req
|
||||
)
|
||||
{
|
||||
if (!originalWebSocketOpen) {
|
||||
return;
|
||||
}
|
||||
if (!originalWebSocketOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString host = req.url().host();
|
||||
if (shouldPatchURL(host)) {
|
||||
QUrl newUrl = req.url();
|
||||
newUrl.setHost(QString::fromStdString(gConfiguredHost));
|
||||
newUrl.setPort(gConfiguredPort);
|
||||
const QString host = req.url().host();
|
||||
if (shouldPatchURL(host)) {
|
||||
QUrl newUrl = req.url();
|
||||
newUrl.setHost(QString::fromStdString(gConfiguredHost));
|
||||
newUrl.setPort(gConfiguredPort);
|
||||
|
||||
QNetworkRequest newReq(req);
|
||||
newReq.setUrl(newUrl);
|
||||
QNetworkRequest newReq(req);
|
||||
newReq.setUrl(newUrl);
|
||||
|
||||
Log("[WS PATCHED] " + host.toStdString() + " -> " + newUrl.toString().toStdString());
|
||||
Log("[WS PATCHED] " + host.toStdString() + " -> " + newUrl.toString().toStdString());
|
||||
|
||||
originalWebSocketOpen(self, newReq);
|
||||
return;
|
||||
}
|
||||
originalWebSocketOpen(self, newReq);
|
||||
return;
|
||||
}
|
||||
|
||||
originalWebSocketOpen(self, req);
|
||||
originalWebSocketOpen(self, req);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------
|
||||
|
||||
void* ResolveExport(HMODULE module, const char* symbol)
|
||||
{
|
||||
void* addr = (void*)GetProcAddress(module, symbol);
|
||||
void* addr = (void*)GetProcAddress(module, symbol);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
Log("[ERROR] Failed to resolve symbol");
|
||||
Log(symbol);
|
||||
}
|
||||
if (!addr)
|
||||
{
|
||||
Log("[ERROR] Failed to resolve symbol");
|
||||
Log(symbol);
|
||||
}
|
||||
|
||||
return addr;
|
||||
return addr;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// InstallHooks
|
||||
// ------------------------------------------------------------
|
||||
|
||||
void InstallHooks()
|
||||
{
|
||||
LoadConfig();
|
||||
LoadConfig();
|
||||
|
||||
// std::string logPath = GetLogPath();
|
||||
// std::string message = "Proxy Hook Started.\nLog file: " + logPath;
|
||||
// MessageBoxA(NULL, message.c_str(), "reMarkable Proxy", MB_OK | MB_ICONINFORMATION);
|
||||
Log("[*] Initializing MinHook");
|
||||
|
||||
Log("[*] Initializing MinHook");
|
||||
if (MH_Initialize() != MH_OK)
|
||||
{
|
||||
Log("[ERROR] MH_Initialize failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (MH_Initialize() != MH_OK)
|
||||
{
|
||||
Log("[ERROR] MH_Initialize failed");
|
||||
return;
|
||||
}
|
||||
HMODULE qtNetwork = nullptr;
|
||||
HMODULE qtWebSockets = nullptr;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Wait for Qt DLLs
|
||||
// --------------------------------------------------------
|
||||
while (!qtNetwork)
|
||||
{
|
||||
qtNetwork = GetModuleHandleA("Qt6Network.dll");
|
||||
Sleep(100);
|
||||
}
|
||||
|
||||
HMODULE qtNetwork = nullptr;
|
||||
HMODULE qtWebSockets = nullptr;
|
||||
while (!qtWebSockets)
|
||||
{
|
||||
qtWebSockets = GetModuleHandleA("Qt6WebSockets.dll");
|
||||
Sleep(100);
|
||||
}
|
||||
|
||||
while (!qtNetwork)
|
||||
{
|
||||
qtNetwork = GetModuleHandleA("Qt6Network.dll");
|
||||
Sleep(100);
|
||||
}
|
||||
Log("[+] Qt DLLs loaded");
|
||||
|
||||
while (!qtWebSockets)
|
||||
{
|
||||
qtWebSockets = GetModuleHandleA("Qt6WebSockets.dll");
|
||||
Sleep(100);
|
||||
}
|
||||
void* createRequestAddr = ResolveExport(
|
||||
qtNetwork,
|
||||
"?createRequest@QNetworkAccessManager@@MEAAPEAVQNetworkReply@@W4Operation@1@AEBVQNetworkRequest@@PEAVQIODevice@@@Z"
|
||||
);
|
||||
|
||||
Log("[+] Qt DLLs loaded");
|
||||
void* webSocketOpenAddr = ResolveExport(
|
||||
qtWebSockets,
|
||||
"?open@QWebSocket@@QEAAXAEBVQNetworkRequest@@@Z"
|
||||
);
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Resolve symbols
|
||||
// --------------------------------------------------------
|
||||
if (!createRequestAddr || !webSocketOpenAddr)
|
||||
{
|
||||
Log("[ERROR] Failed to resolve one or more symbols");
|
||||
return;
|
||||
}
|
||||
|
||||
void* createRequestAddr = ResolveExport(
|
||||
qtNetwork,
|
||||
"?createRequest@QNetworkAccessManager@@MEAAPEAVQNetworkReply@@W4Operation@1@AEBVQNetworkRequest@@PEAVQIODevice@@@Z"
|
||||
);
|
||||
Log("[+] Symbols resolved");
|
||||
|
||||
void* webSocketOpenAddr = ResolveExport(
|
||||
qtWebSockets,
|
||||
"?open@QWebSocket@@QEAAXAEBVQNetworkRequest@@@Z"
|
||||
);
|
||||
|
||||
if (!createRequestAddr || !webSocketOpenAddr)
|
||||
{
|
||||
Log("[ERROR] Failed to resolve one or more symbols");
|
||||
return;
|
||||
}
|
||||
if (MH_CreateHook(
|
||||
createRequestAddr,
|
||||
&hookedCreateRequest,
|
||||
reinterpret_cast<void**>(&originalCreateRequest)
|
||||
) != MH_OK)
|
||||
{
|
||||
Log("[ERROR] Failed to hook createRequest");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("[+] Hooked createRequest");
|
||||
}
|
||||
|
||||
Log("[+] Symbols resolved");
|
||||
if (MH_CreateHook(
|
||||
webSocketOpenAddr,
|
||||
&hookedWebSocketOpen,
|
||||
reinterpret_cast<void**>(&originalWebSocketOpen)
|
||||
) != MH_OK)
|
||||
{
|
||||
Log("[ERROR] Failed to hook QWebSocket::open");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("[+] Hooked QWebSocket::open");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Hook QNetworkAccessManager::createRequest
|
||||
// --------------------------------------------------------
|
||||
|
||||
if (MH_CreateHook(
|
||||
createRequestAddr,
|
||||
&hookedCreateRequest,
|
||||
reinterpret_cast<void**>(&originalCreateRequest)
|
||||
) != MH_OK)
|
||||
{
|
||||
Log("[ERROR] Failed to hook createRequest");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("[+] Hooked createRequest");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Hook QWebSocket::open
|
||||
// --------------------------------------------------------
|
||||
|
||||
if (MH_CreateHook(
|
||||
webSocketOpenAddr,
|
||||
&hookedWebSocketOpen,
|
||||
reinterpret_cast<void**>(&originalWebSocketOpen)
|
||||
) != MH_OK)
|
||||
{
|
||||
Log("[ERROR] Failed to hook QWebSocket::open");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("[+] Hooked QWebSocket::open");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Enable all hooks
|
||||
// --------------------------------------------------------
|
||||
|
||||
if (MH_EnableHook(MH_ALL_HOOKS) != MH_OK)
|
||||
{
|
||||
Log("[ERROR] Failed to enable hooks");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("[+] Hooks enabled");
|
||||
}
|
||||
if (MH_EnableHook(MH_ALL_HOOKS) != MH_OK)
|
||||
{
|
||||
Log("[ERROR] Failed to enable hooks");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("[+] Hooks enabled");
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
#include "common.h"
|
||||
|
||||
DWORD WINAPI DelayedHelloThread(LPVOID lpParam)
|
||||
void LoadOriginalDllFunctions()
|
||||
{
|
||||
Sleep(10000); // 10 seconds
|
||||
MessageBox(0, "Hello :)", "Proxy", MB_OK | MB_ICONINFORMATION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LoadOriginalDllFunctions() {
|
||||
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");
|
||||
@@ -60,30 +54,33 @@ void LoadOriginalDllFunctions() {
|
||||
paho_mqtt3as.OrignalThread_unlock_mutex = GetProcAddress(paho_mqtt3as.dll, "Thread_unlock_mutex");
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
{
|
||||
DisableThreadLibraryCalls(hModule);
|
||||
|
||||
paho_mqtt3as.dll = LoadLibrary("paho-mqtt3as_orig.dll");
|
||||
if (paho_mqtt3as.dll == NULL)
|
||||
case DLL_PROCESS_ATTACH:
|
||||
{
|
||||
MessageBox(0, "Cannot load original paho_mqtt3as.dll library", "Proxy", MB_ICONERROR);
|
||||
ExitProcess(0);
|
||||
DisableThreadLibraryCalls(hModule);
|
||||
|
||||
paho_mqtt3as.dll = LoadLibrary("paho-mqtt3as_orig.dll");
|
||||
if (paho_mqtt3as.dll == NULL)
|
||||
{
|
||||
MessageBox(0, "Cannot load original paho_mqtt3as.dll library", "Proxy", MB_ICONERROR);
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
LoadOriginalDllFunctions();
|
||||
InstallHooks();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
LoadOriginalDllFunctions();
|
||||
InstallHooks();
|
||||
case DLL_PROCESS_DETACH:
|
||||
{
|
||||
FreeLibrary(paho_mqtt3as.dll);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DLL_PROCESS_DETACH:
|
||||
{
|
||||
FreeLibrary(paho_mqtt3as.dll);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user