From f651edd7404bc6e1ce7ee686b12c7282ee6a0587 Mon Sep 17 00:00:00 2001 From: IChooseYou Date: Sun, 22 Feb 2026 11:36:24 -0700 Subject: [PATCH] feat: remove nonce/bootstrap from remote process IPC, use PID-only naming Shared memory names simplified to Local\RCX_SHM_, no bootstrap handshake needed. Payload uses CreateTimerQueueTimer (10ms poll) instead of a dedicated server thread. --- .../RemoteProcessMemoryPlugin.cpp | 94 ++------- .../RemoteProcessMemoryPlugin.h | 2 +- .../payload/rcx_payload.cpp | 184 +++++++----------- .../RemoteProcessMemory/rcx_rpc_protocol.h | 38 ++-- .../tests/test_rpc_client.cpp | 36 ++-- .../tests/test_rpc_host.cpp | 62 +----- 6 files changed, 124 insertions(+), 292 deletions(-) diff --git a/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.cpp b/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.cpp index 7701ebb..4ee7f78 100644 --- a/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.cpp +++ b/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.cpp @@ -5,9 +5,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -65,12 +63,12 @@ struct IpcClient { /* ── connect / disconnect ──────────────────────────────────────── */ - bool connect(uint32_t pid, const QByteArray& nonce, int timeoutMs = 5000) + bool connect(uint32_t pid, int timeoutMs = 5000) { char shmName[128], reqName[128], rspName[128]; - rcx_rpc_shm_name(shmName, sizeof(shmName), pid, nonce.constData()); - rcx_rpc_req_name(reqName, sizeof(reqName), pid, nonce.constData()); - rcx_rpc_rsp_name(rspName, sizeof(rspName), pid, nonce.constData()); + rcx_rpc_shm_name(shmName, sizeof(shmName), pid); + rcx_rpc_req_name(reqName, sizeof(reqName), pid); + rcx_rpc_rsp_name(rspName, sizeof(rspName), pid); #ifdef _WIN32 /* poll for shared memory to appear (payload creating it) */ @@ -373,51 +371,6 @@ static QString payloadPath() #endif } -/* Create bootstrap shared memory with the nonce */ -static bool createBootstrapShm(uint32_t pid, const QByteArray& nonce) -{ - char bootName[128]; - rcx_rpc_boot_name(bootName, sizeof(bootName), pid); - -#ifdef _WIN32 - HANDLE hBoot = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, - PAGE_READWRITE, 0, RCX_RPC_BOOT_SIZE, - bootName); - if (!hBoot) return false; - - auto* view = static_cast( - MapViewOfFile(hBoot, FILE_MAP_WRITE, 0, 0, RCX_RPC_BOOT_SIZE)); - if (!view) { CloseHandle(hBoot); return false; } - - memset(view, 0, RCX_RPC_BOOT_SIZE); - view->nonceLength = (uint32_t)nonce.size(); - memcpy(view->nonce, nonce.constData(), qMin(nonce.size(), 59)); - - UnmapViewOfFile(view); - /* keep hBoot open until payload reads it (payload unlinks after reading) */ - /* leak intentional: closed when process exits or payload consumes it */ - return true; -#else - int fd = shm_open(bootName, O_CREAT | O_RDWR, 0600); - if (fd < 0) return false; - if (ftruncate(fd, RCX_RPC_BOOT_SIZE) != 0) { close(fd); return false; } - - void* view = mmap(nullptr, RCX_RPC_BOOT_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - close(fd); - if (view == MAP_FAILED) return false; - - auto* boot = static_cast(view); - memset(boot, 0, RCX_RPC_BOOT_SIZE); - boot->nonceLength = (uint32_t)nonce.size(); - memcpy(boot->nonce, nonce.constData(), qMin(nonce.size(), 59)); - - munmap(view, RCX_RPC_BOOT_SIZE); - /* payload unlinks after consuming */ - return true; -#endif -} - #ifdef _WIN32 /* ── Windows injection: CreateRemoteThread + LoadLibraryA ─────────── */ @@ -717,24 +670,23 @@ bool RemoteProcessMemoryPlugin::canHandle(const QString& target) const std::unique_ptr RemoteProcessMemoryPlugin::createProvider(const QString& target, QString* errorMsg) { - /* target = "rpm:{pid}:{nonce}:{name}" */ + /* target = "rpm:{pid}:{name}" */ QStringList parts = target.split(':'); - if (parts.size() < 4 || parts[0] != QStringLiteral("rpm")) { + if (parts.size() < 3 || parts[0] != QStringLiteral("rpm")) { if (errorMsg) *errorMsg = QStringLiteral("Invalid target: ") + target; return nullptr; } bool ok; - uint32_t pid = parts[1].toUInt(&ok); - QString nonce = parts[2]; - QString name = parts.mid(3).join(':'); /* name may contain colons */ + uint32_t pid = parts[1].toUInt(&ok); + QString name = parts.mid(2).join(':'); /* name may contain colons */ if (!ok || pid == 0) { if (errorMsg) *errorMsg = QStringLiteral("Invalid PID in target."); return nullptr; } - auto ipc = getOrCreateConnection(pid, nonce, errorMsg); + auto ipc = getOrCreateConnection(pid, errorMsg); if (!ipc) return nullptr; return std::make_unique(pid, name, ipc); @@ -745,7 +697,7 @@ uint64_t RemoteProcessMemoryPlugin::getInitialBaseAddress(const QString& target) /* Read imageBase directly from the shared-memory header -- zero IPC cost. The payload filled it at init from PEB->Ldr (Win) / /proc/self/maps (Linux). */ QStringList parts = target.split(':'); - if (parts.size() < 3 || parts[0] != QStringLiteral("rpm")) + if (parts.size() < 2 || parts[0] != QStringLiteral("rpm")) return 0; bool ok; @@ -793,35 +745,17 @@ bool RemoteProcessMemoryPlugin::selectTarget(QWidget* parent, QString* target) QAbstractButton* clicked = box.clickedButton(); if (clicked == injectBtn) { - /* generate nonce */ - QString nonce = QUuid::createUuid().toString(QUuid::Id128).left(16); - QByteArray nonceUtf8 = nonce.toUtf8(); - - /* create bootstrap, inject */ - if (!createBootstrapShm(pid, nonceUtf8)) { - QMessageBox::critical(parent, QStringLiteral("Error"), - QStringLiteral("Failed to create bootstrap shared memory.")); - return false; - } - QString injectErr; if (!injectPayload(pid, &injectErr)) { QMessageBox::critical(parent, QStringLiteral("Injection Failed"), injectErr); return false; } - *target = QStringLiteral("rpm:%1:%2:%3").arg(pid).arg(nonce, name); + *target = QStringLiteral("rpm:%1:%2").arg(pid).arg(name); return true; } else if (clicked == connectBtn) { - bool ok; - QString nonce = QInputDialog::getText(parent, - QStringLiteral("Connect to Payload"), - QStringLiteral("Enter the payload nonce:"), - QLineEdit::Normal, QString(), &ok); - if (!ok || nonce.isEmpty()) return false; - - *target = QStringLiteral("rpm:%1:%2:%3").arg(pid).arg(nonce, name); + *target = QStringLiteral("rpm:%1:%2").arg(pid).arg(name); return true; } @@ -903,7 +837,7 @@ QVector RemoteProcessMemoryPlugin::enumerateProcesses() std::shared_ptr RemoteProcessMemoryPlugin::getOrCreateConnection( - uint32_t pid, const QString& nonce, QString* errorMsg) + uint32_t pid, QString* errorMsg) { QMutexLocker lock(&m_connectionsMutex); @@ -912,7 +846,7 @@ RemoteProcessMemoryPlugin::getOrCreateConnection( return *it; auto ipc = std::make_shared(); - if (!ipc->connect(pid, nonce.toUtf8())) { + if (!ipc->connect(pid)) { if (errorMsg) *errorMsg = QStringLiteral("Failed to connect IPC to PID %1.\n" "Is the payload running?").arg(pid); diff --git a/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.h b/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.h index e487e5a..6edd16c 100644 --- a/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.h +++ b/plugins/RemoteProcessMemory/RemoteProcessMemoryPlugin.h @@ -77,7 +77,7 @@ public: private: std::shared_ptr getOrCreateConnection( - uint32_t pid, const QString& nonce, QString* errorMsg); + uint32_t pid, QString* errorMsg); mutable QMutex m_connectionsMutex; QHash> m_connections; diff --git a/plugins/RemoteProcessMemory/payload/rcx_payload.cpp b/plugins/RemoteProcessMemory/payload/rcx_payload.cpp index dad4ad2..68d7c8d 100644 --- a/plugins/RemoteProcessMemory/payload/rcx_payload.cpp +++ b/plugins/RemoteProcessMemory/payload/rcx_payload.cpp @@ -2,9 +2,8 @@ * rcx_payload -- injected into target process. * * Pure Win32 / POSIX, NO Qt, minimal footprint. - * Reads a nonce from bootstrap shared memory, creates the main IPC - * channel (shared memory + events/semaphores), and runs a server - * thread that handles RPC commands from the editor plugin. + * Creates the main IPC channel (shared memory + events/semaphores) + * using PID-only naming and uses a timer queue for polling. */ #include "../rcx_rpc_protocol.h" @@ -18,11 +17,12 @@ #include /* ── globals ──────────────────────────────────────────────────────── */ -static HANDLE g_hShm = nullptr; -static void* g_mappedView = nullptr; -static HANDLE g_hReqEvent = nullptr; -static HANDLE g_hRspEvent = nullptr; -static HANDLE g_hThread = nullptr; +static HANDLE g_hShm = nullptr; +static void* g_mappedView = nullptr; +static HANDLE g_hReqEvent = nullptr; +static HANDLE g_hRspEvent = nullptr; +static HANDLE g_hTimerQueue = nullptr; +static HANDLE g_hTimer = nullptr; static volatile LONG g_shutdown = 0; /* ── memory safety via VirtualQuery ────────────────────────────────── */ @@ -167,63 +167,59 @@ static void handle_enum_modules(RcxRpcHeader* hdr, uint8_t* data) hdr->status = RCX_RPC_STATUS_OK; } -/* ── server thread ────────────────────────────────────────────────── */ +/* ── timer callback (replaces server thread) ─────────────────────── */ -static DWORD WINAPI ServerThread(LPVOID) +static VOID CALLBACK PollCallback(PVOID, BOOLEAN) { - auto* hdr = static_cast(g_mappedView); + if (InterlockedCompareExchange(&g_shutdown, 0, 0)) + return; + + DWORD rc = WaitForSingleObject(g_hReqEvent, 0); + if (rc != WAIT_OBJECT_0) + return; + + auto* hdr = static_cast(g_mappedView); auto* data = reinterpret_cast(g_mappedView) + RCX_RPC_DATA_OFFSET; - /* signal readiness */ - InterlockedExchange(reinterpret_cast(&hdr->payloadReady), 1); + hdr->status = RCX_RPC_STATUS_OK; - while (!InterlockedCompareExchange(&g_shutdown, 0, 0)) { - DWORD rc = WaitForSingleObject(g_hReqEvent, 250); - if (rc == WAIT_TIMEOUT) - continue; - if (rc != WAIT_OBJECT_0) - break; - - hdr->status = RCX_RPC_STATUS_OK; - - switch (static_cast(hdr->command)) { - case RPC_CMD_READ_BATCH: handle_read_batch(hdr, data); break; - case RPC_CMD_WRITE: handle_write(hdr, data); break; - case RPC_CMD_ENUM_MODULES: handle_enum_modules(hdr, data); break; - case RPC_CMD_PING: break; - case RPC_CMD_SHUTDOWN: - InterlockedExchange(&g_shutdown, 1); - break; - default: - hdr->status = RCX_RPC_STATUS_ERROR; - break; - } - - SetEvent(g_hRspEvent); - - if (static_cast(hdr->command) == RPC_CMD_SHUTDOWN) - break; + switch (static_cast(hdr->command)) { + case RPC_CMD_READ_BATCH: handle_read_batch(hdr, data); break; + case RPC_CMD_WRITE: handle_write(hdr, data); break; + case RPC_CMD_ENUM_MODULES: handle_enum_modules(hdr, data); break; + case RPC_CMD_PING: break; + case RPC_CMD_SHUTDOWN: + InterlockedExchange(&g_shutdown, 1); + break; + default: + hdr->status = RCX_RPC_STATUS_ERROR; + break; } - /* mark not-ready so the host process can detect shutdown */ - InterlockedExchange(reinterpret_cast(&hdr->payloadReady), 0); - return 0; + SetEvent(g_hRspEvent); } /* ── cleanup ──────────────────────────────────────────────────────── */ -static void Cleanup(bool waitThread) +static void Cleanup() { InterlockedExchange(&g_shutdown, 1); - /* wake the thread if it's blocked on REQ */ - if (g_hReqEvent) SetEvent(g_hReqEvent); - - if (waitThread && g_hThread) { - WaitForSingleObject(g_hThread, 2000); + if (g_hTimer) { + DeleteTimerQueueTimer(g_hTimerQueue, g_hTimer, INVALID_HANDLE_VALUE); + g_hTimer = nullptr; + } + if (g_hTimerQueue) { + DeleteTimerQueueEx(g_hTimerQueue, INVALID_HANDLE_VALUE); + g_hTimerQueue = nullptr; + } + + if (g_mappedView) { + auto* hdr = static_cast(g_mappedView); + InterlockedExchange(reinterpret_cast(&hdr->payloadReady), 0); + UnmapViewOfFile(g_mappedView); + g_mappedView = nullptr; } - if (g_hThread) { CloseHandle(g_hThread); g_hThread = nullptr; } - if (g_mappedView){ UnmapViewOfFile(g_mappedView); g_mappedView = nullptr; } if (g_hShm) { CloseHandle(g_hShm); g_hShm = nullptr; } if (g_hReqEvent) { CloseHandle(g_hReqEvent); g_hReqEvent = nullptr; } if (g_hRspEvent) { CloseHandle(g_hRspEvent); g_hRspEvent = nullptr; } @@ -231,36 +227,16 @@ static void Cleanup(bool waitThread) /* ── DllMain ──────────────────────────────────────────────────────── */ -BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID reserved) +BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH) { uint32_t pid = GetCurrentProcessId(); - /* ── read nonce from bootstrap shm ── */ - char bootName[128]; - rcx_rpc_boot_name(bootName, sizeof(bootName), pid); - - HANDLE hBoot = OpenFileMappingA(FILE_MAP_READ, FALSE, bootName); - if (!hBoot) return TRUE; /* no bootstrap = nothing to do */ - - auto* bootView = static_cast( - MapViewOfFile(hBoot, FILE_MAP_READ, 0, 0, RCX_RPC_BOOT_SIZE)); - if (!bootView) { CloseHandle(hBoot); return TRUE; } - - char nonce[64] = {}; - uint32_t nLen = bootView->nonceLength; - if (nLen > 59) nLen = 59; - memcpy(nonce, bootView->nonce, nLen); - nonce[nLen] = '\0'; - - UnmapViewOfFile(bootView); - CloseHandle(hBoot); - - /* ── create main shared memory ── */ + /* ── create main shared memory (PID-only naming) ── */ char shmName[128], reqName[128], rspName[128]; - rcx_rpc_shm_name(shmName, sizeof(shmName), pid, nonce); - rcx_rpc_req_name(reqName, sizeof(reqName), pid, nonce); - rcx_rpc_rsp_name(rspName, sizeof(rspName), pid, nonce); + rcx_rpc_shm_name(shmName, sizeof(shmName), pid); + rcx_rpc_req_name(reqName, sizeof(reqName), pid); + rcx_rpc_rsp_name(rspName, sizeof(rspName), pid); g_hShm = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, RCX_RPC_SHM_SIZE, shmName); @@ -273,27 +249,36 @@ BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID reserved) auto* hdr = static_cast(g_mappedView); hdr->version = RCX_RPC_VERSION; - /* image base from PEB: gs:[0x60] → PEB, +0x18 → Ldr, Flink → first entry, +0x30 → DllBase */ + /* image base from PEB: gs:[0x60] -> PEB, +0x18 -> Ldr, Flink -> first entry, +0x30 -> DllBase */ { uint64_t peb; asm volatile("mov %%gs:0x60, %0" : "=r"(peb)); uint64_t ldr = *reinterpret_cast(peb + 0x18); - uint64_t firstLink = *reinterpret_cast(ldr + 0x10); /* InLoadOrderModuleList.Flink */ - hdr->imageBase = *reinterpret_cast(firstLink + 0x30); /* DllBase */ + uint64_t firstLink = *reinterpret_cast(ldr + 0x10); + hdr->imageBase = *reinterpret_cast(firstLink + 0x30); } /* ── create events ── */ g_hReqEvent = CreateEventA(nullptr, FALSE, FALSE, reqName); g_hRspEvent = CreateEventA(nullptr, FALSE, FALSE, rspName); - if (!g_hReqEvent || !g_hRspEvent) { Cleanup(false); return TRUE; } + if (!g_hReqEvent || !g_hRspEvent) { Cleanup(); return TRUE; } - /* ── start server thread (payloadReady set by the thread) ── */ - g_hThread = CreateThread(nullptr, 0, ServerThread, nullptr, 0, nullptr); - if (!g_hThread) { Cleanup(false); return TRUE; } + /* ── start timer queue (10ms poll interval) ── */ + g_hTimerQueue = CreateTimerQueue(); + if (!g_hTimerQueue) { Cleanup(); return TRUE; } + + if (!CreateTimerQueueTimer(&g_hTimer, g_hTimerQueue, + PollCallback, nullptr, 0, 10, + WT_EXECUTEDEFAULT)) { + Cleanup(); + return TRUE; + } + + /* signal readiness */ + InterlockedExchange(reinterpret_cast(&hdr->payloadReady), 1); } else if (reason == DLL_PROCESS_DETACH) { - /* reserved != NULL → process is terminating (threads already dead) */ - Cleanup(reserved == nullptr); + Cleanup(); } return TRUE; @@ -529,37 +514,14 @@ static void payload_init() { uint32_t pid = (uint32_t)getpid(); - /* ── read nonce from bootstrap shm ── */ - char bootName[128]; - rcx_rpc_boot_name(bootName, sizeof(bootName), pid); - - int bootFd = shm_open(bootName, O_RDONLY, 0); - if (bootFd < 0) return; - - void* bootView = mmap(nullptr, RCX_RPC_BOOT_SIZE, PROT_READ, - MAP_SHARED, bootFd, 0); - close(bootFd); - if (bootView == MAP_FAILED) return; - - auto* boot = static_cast(bootView); - char nonce[64] = {}; - uint32_t nLen = boot->nonceLength; - if (nLen > 59) nLen = 59; - memcpy(nonce, boot->nonce, nLen); - nonce[nLen] = '\0'; - munmap(bootView, RCX_RPC_BOOT_SIZE); - - /* one-shot, unlink bootstrap */ - shm_unlink(bootName); - /* ── open /proc/self/mem for safe access ── */ g_memFd = open("/proc/self/mem", O_RDWR); if (g_memFd < 0) return; - /* ── create main shared memory ── */ - rcx_rpc_shm_name(g_shmName, sizeof(g_shmName), pid, nonce); - rcx_rpc_req_name(g_reqName, sizeof(g_reqName), pid, nonce); - rcx_rpc_rsp_name(g_rspName, sizeof(g_rspName), pid, nonce); + /* ── create main shared memory (PID-only naming) ── */ + rcx_rpc_shm_name(g_shmName, sizeof(g_shmName), pid); + rcx_rpc_req_name(g_reqName, sizeof(g_reqName), pid); + rcx_rpc_rsp_name(g_rspName, sizeof(g_rspName), pid); g_shmFd = shm_open(g_shmName, O_CREAT | O_RDWR, 0600); if (g_shmFd < 0) return; diff --git a/plugins/RemoteProcessMemory/rcx_rpc_protocol.h b/plugins/RemoteProcessMemory/rcx_rpc_protocol.h index a012f5a..c20f6d8 100644 --- a/plugins/RemoteProcessMemory/rcx_rpc_protocol.h +++ b/plugins/RemoteProcessMemory/rcx_rpc_protocol.h @@ -15,7 +15,6 @@ #define RCX_RPC_HEADER_SIZE 4096 #define RCX_RPC_DATA_OFFSET RCX_RPC_HEADER_SIZE #define RCX_RPC_DATA_SIZE (RCX_RPC_SHM_SIZE - RCX_RPC_DATA_OFFSET) -#define RCX_RPC_BOOT_SIZE 64 /* status codes */ #define RCX_RPC_STATUS_OK 0 @@ -83,47 +82,32 @@ struct RcxRpcHeader { uint8_t _pad[RCX_RPC_HEADER_SIZE - 48]; }; -/* Bootstrap shm -- 64 bytes, carries the nonce from plugin to payload */ -struct RcxRpcBootHeader { - uint32_t nonceLength; - char nonce[60]; -}; +/* ── name formatting helpers (PID-only, no nonce) ─────────────────── */ -/* ── name formatting helpers ───────────────────────────────────────── */ - -static inline void rcx_rpc_boot_name(char* buf, int n, uint32_t pid) { +static inline void rcx_rpc_shm_name(char* buf, int n, uint32_t pid) { #ifdef _WIN32 - snprintf(buf, n, "Local\\RCX_BOOT_%u", pid); + snprintf(buf, n, "Local\\RCX_SHM_%u", pid); #else - snprintf(buf, n, "/rcx_boot_%u", pid); + snprintf(buf, n, "/rcx_shm_%u", pid); #endif } -static inline void rcx_rpc_shm_name(char* buf, int n, uint32_t pid, const char* nonce) { +static inline void rcx_rpc_req_name(char* buf, int n, uint32_t pid) { #ifdef _WIN32 - snprintf(buf, n, "Local\\RCX_SHM_%u_%s", pid, nonce); + snprintf(buf, n, "Local\\RCX_REQ_%u", pid); #else - snprintf(buf, n, "/rcx_shm_%u_%s", pid, nonce); + snprintf(buf, n, "/rcx_req_%u", pid); #endif } -static inline void rcx_rpc_req_name(char* buf, int n, uint32_t pid, const char* nonce) { +static inline void rcx_rpc_rsp_name(char* buf, int n, uint32_t pid) { #ifdef _WIN32 - snprintf(buf, n, "Local\\RCX_REQ_%u_%s", pid, nonce); + snprintf(buf, n, "Local\\RCX_RSP_%u", pid); #else - snprintf(buf, n, "/rcx_req_%u_%s", pid, nonce); -#endif -} - -static inline void rcx_rpc_rsp_name(char* buf, int n, uint32_t pid, const char* nonce) { -#ifdef _WIN32 - snprintf(buf, n, "Local\\RCX_RSP_%u_%s", pid, nonce); -#else - snprintf(buf, n, "/rcx_rsp_%u_%s", pid, nonce); + snprintf(buf, n, "/rcx_rsp_%u", pid); #endif } #ifdef __cplusplus -static_assert(sizeof(RcxRpcHeader) == RCX_RPC_HEADER_SIZE, "Header must be 4096 bytes"); -static_assert(sizeof(RcxRpcBootHeader) <= RCX_RPC_BOOT_SIZE, "Boot header must fit 64 bytes"); +static_assert(sizeof(RcxRpcHeader) == RCX_RPC_HEADER_SIZE, "Header must be 4096 bytes"); #endif diff --git a/plugins/RemoteProcessMemory/tests/test_rpc_client.cpp b/plugins/RemoteProcessMemory/tests/test_rpc_client.cpp index 8d6b70d..1719f0b 100644 --- a/plugins/RemoteProcessMemory/tests/test_rpc_client.cpp +++ b/plugins/RemoteProcessMemory/tests/test_rpc_client.cpp @@ -4,7 +4,7 @@ * * Usage: * test_rpc_client (auto-spawn host) - * test_rpc_client [testbuf_hex testlen] + * test_rpc_client [testbuf_hex testlen] */ #include "../rcx_rpc_protocol.h" @@ -45,12 +45,12 @@ struct TestIpcClient { void* view = nullptr; bool ok = false; - bool connect(uint32_t pid, const char* nonce, int timeoutMs = 5000) + bool connect(uint32_t pid, int timeoutMs = 5000) { char shmName[128], reqName[128], rspName[128]; - rcx_rpc_shm_name(shmName, sizeof(shmName), pid, nonce); - rcx_rpc_req_name(reqName, sizeof(reqName), pid, nonce); - rcx_rpc_rsp_name(rspName, sizeof(rspName), pid, nonce); + rcx_rpc_shm_name(shmName, sizeof(shmName), pid); + rcx_rpc_req_name(reqName, sizeof(reqName), pid); + rcx_rpc_rsp_name(rspName, sizeof(rspName), pid); #ifdef _WIN32 ULONGLONG deadline = GetTickCount64() + (ULONGLONG)timeoutMs; @@ -268,7 +268,7 @@ static pid_t g_hostPid = 0; #endif static FILE* g_hostPipe = nullptr; -static bool spawn_host(uint32_t* outPid, char* outNonce, +static bool spawn_host(uint32_t* outPid, uint64_t* outTestBuf, uint32_t* outTestLen) { /* resolve path to test_rpc_host next to ourselves */ @@ -302,11 +302,11 @@ static bool spawn_host(uint32_t* outPid, char* outNonce, return false; } - /* parse: READY pid=X nonce=Y testbuf=0xZ testlen=N */ + /* parse: READY pid=X testbuf=0xZ testlen=N */ unsigned long long tbuf = 0; unsigned tlen = 0; - if (sscanf(line, "READY pid=%u nonce=%63s testbuf=0x%llx testlen=%u", - outPid, outNonce, &tbuf, &tlen) < 2) { + if (sscanf(line, "READY pid=%u testbuf=0x%llx testlen=%u", + outPid, &tbuf, &tlen) < 1) { fprintf(stderr, "ERROR: cannot parse host output: %s\n", line); return false; } @@ -341,30 +341,28 @@ static void print_fail(const char* name) { printf(" [FAIL] %s\n", name); exit(1 int main(int argc, char** argv) { uint32_t pid = 0; - char nonce[64] = {}; uint64_t testBuf = 0; uint32_t testLen = 0; bool autoMode = false; - if (argc >= 3) { + if (argc >= 2) { pid = (uint32_t)atoi(argv[1]); - strncpy(nonce, argv[2], 63); - if (argc >= 5) { - testBuf = (uint64_t)strtoull(argv[3], nullptr, 0); - testLen = (uint32_t)atoi(argv[4]); + if (argc >= 4) { + testBuf = (uint64_t)strtoull(argv[2], nullptr, 0); + testLen = (uint32_t)atoi(argv[3]); } } else { autoMode = true; printf("Auto-spawning test_rpc_host...\n"); - if (!spawn_host(&pid, nonce, &testBuf, &testLen)) return 1; + if (!spawn_host(&pid, &testBuf, &testLen)) return 1; } - printf("Connecting to PID=%u nonce=%s testbuf=0x%llx testlen=%u\n\n", - pid, nonce, (unsigned long long)testBuf, testLen); + printf("Connecting to PID=%u testbuf=0x%llx testlen=%u\n\n", + pid, (unsigned long long)testBuf, testLen); /* ── connect ── */ TestIpcClient ipc; - if (!ipc.connect(pid, nonce)) { + if (!ipc.connect(pid)) { fprintf(stderr, "ERROR: IPC connect failed\n"); if (autoMode) cleanup_host(); return 1; diff --git a/plugins/RemoteProcessMemory/tests/test_rpc_host.cpp b/plugins/RemoteProcessMemory/tests/test_rpc_host.cpp index 04e87d5..066e805 100644 --- a/plugins/RemoteProcessMemory/tests/test_rpc_host.cpp +++ b/plugins/RemoteProcessMemory/tests/test_rpc_host.cpp @@ -1,7 +1,7 @@ /* * test_rpc_host -- loads rcx_payload in-process, acts as the "target". * - * Usage: test_rpc_host [nonce] + * Usage: test_rpc_host * * Prints a READY line (machine-parseable), then waits for the payload * to shut down (RPC_CMD_SHUTDOWN from the client). @@ -68,50 +68,11 @@ static int payload_path(char* out, int outLen) return 0; } -/* Create bootstrap shared memory with the nonce */ -static int create_bootstrap(uint32_t pid, const char* nonce) -{ - char bootName[128]; - rcx_rpc_boot_name(bootName, sizeof(bootName), pid); - -#ifdef _WIN32 - HANDLE h = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, - PAGE_READWRITE, 0, RCX_RPC_BOOT_SIZE, bootName); - if (!h) return -1; - void* v = MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, RCX_RPC_BOOT_SIZE); - if (!v) { CloseHandle(h); return -1; } - - RcxRpcBootHeader* boot = (RcxRpcBootHeader*)v; - memset(boot, 0, RCX_RPC_BOOT_SIZE); - boot->nonceLength = (uint32_t)strlen(nonce); - strncpy(boot->nonce, nonce, 59); - - UnmapViewOfFile(v); - /* keep h open for payload to read */ - return 0; -#else - int fd = shm_open(bootName, O_CREAT | O_RDWR, 0600); - if (fd < 0) return -1; - if (ftruncate(fd, RCX_RPC_BOOT_SIZE) != 0) { close(fd); return -1; } - void* v = mmap(nullptr, RCX_RPC_BOOT_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - close(fd); - if (v == MAP_FAILED) return -1; - - RcxRpcBootHeader* boot = (RcxRpcBootHeader*)v; - memset(boot, 0, RCX_RPC_BOOT_SIZE); - boot->nonceLength = (uint32_t)strlen(nonce); - strncpy(boot->nonce, nonce, 59); - munmap(v, RCX_RPC_BOOT_SIZE); - return 0; -#endif -} - /* Open the main shared memory (read-only, just to monitor payloadReady) */ -static void* open_main_shm(uint32_t pid, const char* nonce) +static void* open_main_shm(uint32_t pid) { char shmName[128]; - rcx_rpc_shm_name(shmName, sizeof(shmName), pid, nonce); + rcx_rpc_shm_name(shmName, sizeof(shmName), pid); #ifdef _WIN32 HANDLE h = nullptr; @@ -142,21 +103,14 @@ static uint8_t g_testBuf[65536]; /* ── main ─────────────────────────────────────────────────────────── */ -int main(int argc, char** argv) +int main(int, char**) { - const char* nonce = (argc > 1) ? argv[1] : "test0001"; uint32_t pid = current_pid(); /* fill test buffer with known pattern */ for (int i = 0; i < (int)sizeof(g_testBuf); ++i) g_testBuf[i] = (uint8_t)(i & 0xFF); - /* create bootstrap shm */ - if (create_bootstrap(pid, nonce) != 0) { - fprintf(stderr, "ERROR: failed to create bootstrap shm\n"); - return 1; - } - /* load payload */ char plPath[1024]; if (payload_path(plPath, sizeof(plPath)) != 0) { @@ -180,7 +134,7 @@ int main(int argc, char** argv) #endif /* open main shm and wait for payloadReady */ - void* shmView = open_main_shm(pid, nonce); + void* shmView = open_main_shm(pid); if (!shmView) { fprintf(stderr, "ERROR: failed to open main shared memory\n"); return 1; @@ -197,8 +151,8 @@ int main(int argc, char** argv) } /* print READY line for the client to parse */ - printf("READY pid=%u nonce=%s testbuf=0x%llx testlen=%u\n", - pid, nonce, + printf("READY pid=%u testbuf=0x%llx testlen=%u\n", + pid, (unsigned long long)(uintptr_t)g_testBuf, (unsigned)sizeof(g_testBuf)); fflush(stdout); @@ -210,7 +164,7 @@ int main(int argc, char** argv) printf("Payload shut down, exiting.\n"); #ifdef _WIN32 - /* give the server thread a moment to exit */ + /* give the timer queue a moment to drain */ Sleep(200); FreeLibrary(hPayload); if (shmView) UnmapViewOfFile(shmView);