summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README1
-rwxr-xr-xmempoke/build.sh4
-rw-r--r--mempoke/mempoke-injector.c153
-rw-r--r--mempoke/mempoke-target.c67
4 files changed, 225 insertions, 0 deletions
diff --git a/README b/README
index 794c0e2..132e2b1 100644
--- a/README
+++ b/README
@@ -10,6 +10,7 @@ guaranteed to be correct, performant, safe, or (generally speaking) good.
aarch64 AArch64 assembly experiments
ffmpeg-shadertoy Basic offline Shadertoy renderer
+mempoke Read/write to another process's memory
vk-cube Spinning cube Vulkan demo
win-resize-mt Drawing while resizing on Windows (multi threaded)
win-resize-st Drawing while resizing on Windows (single threaded)
diff --git a/mempoke/build.sh b/mempoke/build.sh
new file mode 100755
index 0000000..ed4c5a8
--- /dev/null
+++ b/mempoke/build.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+mkdir -p bin
+cc -o bin/mempoke-target -Wall -Wextra -Wpedantic -O0 -g ./mempoke-target.c
+cc -o bin/mempoke-injector -Wall -Wextra -Wpedantic -O0 -g ./mempoke-injector.c
diff --git a/mempoke/mempoke-injector.c b/mempoke/mempoke-injector.c
new file mode 100644
index 0000000..2d1c4a9
--- /dev/null
+++ b/mempoke/mempoke-injector.c
@@ -0,0 +1,153 @@
+// ================================================================================================
+// Example showing how to read/write process memory remotely.
+//
+// License:
+// Copyright (c) 2026 Hunter Kvalevog
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE.
+// ================================================================================================
+
+#define _GNU_SOURCE
+
+#ifdef _WIN32
+# include <windows.h>
+# include <psapi.h>
+# include <tlhelp32.h>
+#endif
+
+#ifdef __linux__
+# include <dirent.h>
+# include <sys/uio.h>
+#endif
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct ProcessHandle ProcessHandle;
+struct ProcessHandle
+{
+ int pid;
+ uintptr_t base;
+#ifdef _WIN32
+ HANDLE handle;
+#endif
+};
+
+static inline void open_target_process(ProcessHandle *p)
+{
+#ifdef _WIN32
+ // ref: https://docs.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes
+ HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ assert(snap != INVALID_HANDLE_VALUE);
+
+ PROCESSENTRY32 pe = { .dwSize = sizeof(PROCESSENTRY32) };
+ Process32First(snap, &pe);
+
+ do {
+ if (!strcmp(pe.szExeFile, "mempoke-target.exe")) {
+ p->pid = pe.th32ProcessID;
+ p->handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, p->pid);
+ assert(p->handle && "failed to open process");
+
+ // First module is always the exe itself
+ HMODULE exe = 0;
+ DWORD needed = 0;
+ if (!EnumProcessModules(p->handle, &exe, sizeof(exe), &needed)) {
+ assert(0 && "EnumProcessModules failed");
+ }
+ p->base = (uintptr_t)exe;
+
+ return;
+ }
+ } while (Process32Next(snap, &pe));
+#endif
+#ifdef __linux__
+ DIR *proc = opendir("/proc");
+
+ struct dirent *d;
+ while ((d = readdir(proc))) {
+ pid_t pid = (pid_t)atoi(d->d_name);
+ if (pid <= 0)
+ continue;
+
+ char comm[256];
+ snprintf(comm, sizeof(comm), "/proc/%d/comm", pid);
+
+ FILE *f = fopen(comm, "r");
+ if (!f)
+ continue;
+
+ if (fgets(comm, sizeof(comm), f) && !strncmp(comm, "mempoke-target", 14)) {
+ p->pid = pid;
+
+ char maps_path[64];
+ snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps", p->pid);
+
+ FILE *maps = fopen(maps_path, "r");
+ fscanf(maps, "%" SCNxPTR "-", &p->base);
+ fclose(maps);
+ fclose(f);
+ return;
+ }
+
+ fclose(f);
+ }
+#endif
+ assert(0 && "couldn't find process");
+}
+
+int main(int argc, const char **argv)
+{
+ const uintptr_t offset = (argc > 1) ? strtoull(argv[1], 0, 0) : 0;
+ if (!offset) {
+ printf("Usage: %s <offset>\n example: %s 0x1234\n", argv[0], argv[0]);
+ return 1;
+ }
+
+ ProcessHandle p;
+ open_target_process(&p);
+
+ printf("target process: pid=%d base=0x%" PRIxPTR "\n", p.pid, p.base);
+
+ int num = 0;
+ uintptr_t tgt = p.base + offset;
+
+#ifdef _WIN32
+ if (!ReadProcessMemory(p.handle, (LPCVOID)tgt, &num, sizeof(num), 0)) {
+ assert(0 && "ReadProcessMemory failed");
+ }
+#endif
+#ifdef __linux__
+ struct iovec iov_rsrc = { .iov_base = (void *)tgt, .iov_len = sizeof(num) };
+ struct iovec iov_rdst = { .iov_base = &num, .iov_len = sizeof(num) };
+ if (process_vm_readv(p.pid, &iov_rdst, 1, &iov_rsrc, 1, 0) != sizeof(num)) {
+ assert(0 && "process_vm_readv failed");
+ }
+#endif
+
+ printf("tgtnum = %d\n", num);
+
+ num += 1;
+
+#ifdef _WIN32
+ if (!WriteProcessMemory(p.handle, (LPVOID)tgt, &num, sizeof(num), 0)) {
+ assert(0 && "WriteProcessMemory failed");
+ }
+#endif
+#ifdef __linux__
+ struct iovec iov_wsrc = { .iov_base = &num, .iov_len = sizeof(num) };
+ struct iovec iov_wdst = { .iov_base = (void *)tgt, .iov_len = sizeof(num) };
+ if (process_vm_writev(p.pid, &iov_wsrc, 1, &iov_wdst, 1, 0) != sizeof(num)) {
+ assert(0 && "process_vm_writev failed");
+ }
+#endif
+}
+
diff --git a/mempoke/mempoke-target.c b/mempoke/mempoke-target.c
new file mode 100644
index 0000000..2c34780
--- /dev/null
+++ b/mempoke/mempoke-target.c
@@ -0,0 +1,67 @@
+// ================================================================================================
+// Example showing how to read/write process memory remotely.
+//
+// License:
+// Copyright (c) 2026 Hunter Kvalevog
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE.
+// ================================================================================================
+
+#ifdef _WIN32
+# include <windows.h>
+#endif
+
+#ifdef __linux__
+# include <unistd.h>
+#endif
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+
+int tgtnum = 1;
+
+static inline uintptr_t get_process_base_address(void)
+{
+ uintptr_t base = 0;
+#ifdef _WIN32
+ base = (uintptr_t)GetModuleHandleW(0);
+#endif
+#ifdef __linux__
+ FILE *maps = fopen("/proc/self/maps", "r");
+ fscanf(maps, "%" SCNxPTR "-", &base);
+ fclose(maps);
+#endif
+ assert(base);
+ return base;
+}
+
+static inline void sleep_ms(unsigned ms)
+{
+#ifdef _WIN32
+ Sleep(ms);
+#endif
+#ifdef __linux__
+ usleep(ms * 1000);
+#endif
+}
+
+int main(int argc, const char **argv)
+{
+ (void)argc;
+ (void)argv;
+
+ printf("offset: 0x%" PRIxPTR "\n", (uintptr_t)&tgtnum - get_process_base_address());
+ sleep_ms(3000);
+
+ while (1) {
+ sleep_ms(1000);
+ printf("tgtnum: %d\n", tgtnum);
+ }
+}
+