TELEMETRY UPLINK
DECRYPTING GRID CORE
STREAMING BUFFER CHUNKS // 60 FPS SYNC

STREAMING BUFFER CHUNKS // 60 FPS SYNC
STREAMING BUFFER CHUNKS // 60 FPS SYNC
Embedding dynamic scripting environments directly into foreign host processes is one of the most powerful techniques in low-level systems engineering, runtime security instrumentation, and binary reverse engineering. By sideloading an isolated Google V8 JavaScript virtual machine into a running foreign executable, I can hook native symbols, inspect heap structures, dynamically patch memory, and stream runtime telemetry.
However, operating inside a foreign process's virtual address space is unforgiving. A single unhandled page fault, unaligned memory access, or loader lock contention will immediately terminate the host process.
This technical log breaks down the mechanics of manual PE mapping, ASLR delta rebasing, thread execution hijacking, custom memory integrity checks, V8 isolate lifecycle management, and encrypted gRPC control streams.
Operating within a foreign process requires solving several critical low-level problems:
LdrLoadDll), the OS kernel does not initialize the C runtime (CRT) or TLS directories. Sideloaded code must manually parse the TLS directory, allocate thread-local slots, and invoke initialization callbacks without locking LdrpLoaderLock.PAGE_READWRITE) to executable (PAGE_EXECUTE_READ) or read-only (PAGE_READONLY) without leaving long-lived RWX (read-write-execute) pages in the target process.Injecting a dynamic link library using standard OS APIs like CreateRemoteThread + LoadLibraryA is easily intercepted by endpoint protection and leaves filesystem traces on disk.
To achieve in-memory execution, I developed a Reflective PE Loader that parses Portable Executable (PE32+) headers in memory and maps raw binary sections directly into the target process's virtual address space:
| 1 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPid); |
| 2 | |
| 3 | LPVOID remoteBase = VirtualAllocEx( |
| 4 | hProcess, |
| 5 | nullptr, |
| 6 | ntHeaders->OptionalHeader.SizeOfImage, |
| 7 | MEM_COMMIT | MEM_RESERVE, |
| 8 | PAGE_READWRITE |
| 9 | ); |
| 10 | |
| 11 | uintptr_t delta = reinterpret_cast<uintptr_t>(remoteBase) - ntHeaders->OptionalHeader.ImageBase; |
| 12 | |
| 13 | /* [CENSORED: In-memory PE header sanitization and signature scrubbing] */ |
| 14 | WriteProcessMemory(hProcess, remoteBase, localBuffer, ntHeaders->OptionalHeader.SizeOfHeaders, nullptr); |
| 15 | |
| 16 | for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++) { |
| 17 | IMAGE_SECTION_HEADER* sec = IMAGE_FIRST_SECTION(ntHeaders) + i; |
| 18 | LPVOID dest = reinterpret_cast<uint8_t*>(remoteBase) + sec->VirtualAddress; |
| 19 | LPVOID src = localBuffer + sec->PointerToRawData; |
| 20 | WriteProcessMemory(hProcess, dest, src, sec->SizeOfRawData, nullptr); |
| 21 | } |
Because Address Space Layout Randomization (ASLR) assigns dynamic base addresses in virtual memory, all absolute pointers in the injected payload must be rebased:
| 1 | IMAGE_DATA_DIRECTORY relocDir = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; |
| 2 | IMAGE_BASE_RELOCATION* reloc = reinterpret_cast<IMAGE_BASE_RELOCATION*>(localBuffer + relocDir.VirtualAddress); |
| 3 | |
| 4 | while (reloc->VirtualAddress > 0) { |
| 5 | DWORD count = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); |
| 6 | WORD* list = reinterpret_cast<WORD*>(reloc + 1); |
| 7 | |
| 8 | for (DWORD i = 0; i < count; i++) { |
| 9 | if ((list[i] >> 12) == IMAGE_REL_BASED_DIR64) { |
| 10 | uintptr_t* patchAddr = reinterpret_cast<uintptr_t*>( |
| 11 | localBuffer + reloc->VirtualAddress + (list[i] & 0x0FFF) |
| 12 | ); |
| 13 | *patchAddr += delta; |
| 14 | } |
| 15 | } |
| 16 | reloc = reinterpret_cast<IMAGE_BASE_RELOCATION*>(reinterpret_cast<uint8_t*>(reloc) + reloc->SizeOfBlock); |
| 17 | } |
To execute the mapped payload inside the target process, I implemented three selectable execution transfer strategies:
1. Thread Context Hijacking: Suspends an active worker thread (SuspendThread), queries its register state (GetThreadContext), pushes the original instruction pointer (RIP) onto the stack, modifies RIP to point to the payload entry point (0x████████), and resumes the thread (SetThreadContext + ResumeThread).
2. Asynchronous Procedure Call (APC) Queuing: Uses QueueUserAPC to queue an execution stub to an existing host thread, which executes seamlessly whenever that thread enters an alertable wait state (SleepEx, WaitForSingleObjectEx).
3. Dedicated Remote Thread: Invokes CreateRemoteThread as a fallback when worker thread states cannot be safely hijacked.
Operating inside an active host process requires continuous defensive verification to prevent memory corruption, detect unauthorized hook modifications, and isolate potential crashes.
To detect runtime memory corruption, hook tampering, or external memory scanner modifications, a dedicated worker thread computes running hashes across all mapped .text sections and detour trampolines:
| 1 | uint32_t compute_page_checksum(const void* page_base, size_t length) { |
| 2 | const uint8_t* data = static_cast<const uint8_t*>(page_base); |
| 3 | uint32_t hash = 0x811c9dc5; |
| 4 | for (size_t i = 0; i < length; i++) { |
| 5 | hash ^= data[i]; |
| 6 | hash *= 0x01000193; |
| 7 | } |
| 8 | return hash; |
| 9 | } |
| 10 | |
| 11 | bool verify_code_integrity(uintptr_t code_base, size_t code_len, uint32_t golden_hash) { |
| 12 | MEMORY_BASIC_INFORMATION mbi; |
| 13 | if (VirtualQuery(reinterpret_cast<void*>(code_base), &mbi, sizeof(mbi)) == 0) { |
| 14 | return false; |
| 15 | } |
| 16 | if ((mbi.Protect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE)) == 0) { |
| 17 | return false; |
| 18 | } |
| 19 | return compute_page_checksum(reinterpret_cast<void*>(code_base), code_len) == golden_hash; |
| 20 | } |
When hooking host API functions, I install 14-byte absolute jumps (FF 25 00 00 00 00 [REDACTED_TARGET_ADDR]). The integrity engine periodically scans installed hook addresses to confirm that another tool or security agent has not stomped or corrupted the detour.
Before any guest JavaScript script attempts to read or write host memory, the native binding verifies page permissions via VirtualQuery:
| 1 | bool is_memory_range_readable(uintptr_t address, size_t length) { |
| 2 | uintptr_t current = address; |
| 3 | uintptr_t end = address + length; |
| 4 | |
| 5 | while (current < end) { |
| 6 | MEMORY_BASIC_INFORMATION mbi; |
| 7 | if (VirtualQuery(reinterpret_cast<void*>(current), &mbi, sizeof(mbi)) == 0) { |
| 8 | return false; |
| 9 | } |
| 10 | |
| 11 | DWORD prot = mbi.Protect; |
| 12 | if ((prot & (PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE)) == 0 || |
| 13 | (prot & PAGE_GUARD) != 0 || (prot & PAGE_NOACCESS) != 0) { |
| 14 | return false; |
| 15 | } |
| 16 | |
| 17 | current = reinterpret_cast<uintptr_t>(mbi.BaseAddress) + mbi.RegionSize; |
| 18 | } |
| 19 | return true; |
| 20 | } |
To prevent guest script errors from crashing the entire host process, I register a top-level Vectored Exception Handler via AddVectoredExceptionHandler:
| 1 | LONG WINAPI IsolatedExceptionHandler(PEXCEPTION_POINTERS exception_info) { |
| 2 | if (exception_info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { |
| 3 | uintptr_t fault_addr = exception_info->ExceptionRecord->ExceptionInformation[1]; |
| 4 | if (IsInsideV8GuestRegion(fault_addr)) { |
| 5 | RecoverV8ExecutionState(exception_info->ContextRecord); |
| 6 | return EXCEPTION_CONTINUE_EXECUTION; |
| 7 | } |
| 8 | } |
| 9 | return EXCEPTION_CONTINUE_SEARCH; |
| 10 | } |
Once execution begins inside the target process, the payload bootstraps a sandboxed Google V8 instance. V8 organizes its execution environment into three distinct layers:
v8::Local<T>).| 1 | void InitializeV8Runtime() { |
| 2 | v8::V8::InitializeICUDefaultLocation(nullptr); |
| 3 | v8::V8::InitializeExternalStartupData(nullptr); |
| 4 | |
| 5 | std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform(); |
| 6 | v8::V8::InitializePlatform(platform.get()); |
| 7 | v8::V8::Initialize(); |
| 8 | |
| 9 | v8::Isolate::CreateParams create_params; |
| 10 | create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); |
| 11 | |
| 12 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 13 | { |
| 14 | v8::Isolate::Scope isolate_scope(isolate); |
| 15 | v8::HandleScope handle_scope(isolate); |
| 16 | |
| 17 | v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); |
| 18 | RegisterNativeInspectionBindings(isolate, global); |
| 19 | |
| 20 | v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global); |
| 21 | v8::Context::Scope context_scope(context); |
| 22 | |
| 23 | ExecuteScript(isolate, context, "console.log('V8 Isolate active inside target memory space');"); |
| 24 | } |
| 25 | } |
To allow JavaScript scripts running inside V8 to inspect host process memory safely, native C++ function callbacks are exposed via v8::FunctionTemplate:
| 1 | void ReadProcessMemoryCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 2 | v8::Isolate* isolate = args.GetIsolate(); |
| 3 | |
| 4 | if (args.Length() < 2 || !args[0]->IsBigInt() || !args[1]->IsNumber()) { |
| 5 | isolate->ThrowException(v8::String::NewFromUtf8Literal(isolate, "Invalid argument types")); |
| 6 | return; |
| 7 | } |
| 8 | |
| 9 | uintptr_t address = static_cast<uintptr_t>(args[0].As<v8::BigInt>()->Uint64Value()); |
| 10 | int32_t length = args[1].As<v8::Int32>()->Value(); |
| 11 | |
| 12 | if (!is_memory_range_readable(address, length)) { |
| 13 | isolate->ThrowException(v8::String::NewFromUtf8Literal(isolate, "Target memory page not readable")); |
| 14 | return; |
| 15 | } |
| 16 | |
| 17 | std::vector<uint8_t> buffer(length); |
| 18 | memcpy(buffer.data(), reinterpret_cast<void*>(address), length); |
| 19 | |
| 20 | v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, buffer.size()); |
| 21 | memcpy(ab->Data(), buffer.data(), buffer.size()); |
| 22 | args.GetReturnValue().Set(v8::Uint8Array::New(ab, 0, buffer.size())); |
| 23 | } |
This bridge allows dynamic scripts to inspect data structures, trace pointers, and decode in-memory tables safely without recompiling native code.
Rather than writing output to standard I/O (which can be intercepted or disrupt host console operations), the injected engine establishes an out-of-band, encrypted gRPC communication channel over TLS:
v8::ScriptCompiler::CompileUnboundScript.While native process memory injection is effective for desktop environments, it carries severe operational friction: OS-specific APIs, antivirus false positives, and elevated permission requirements.
This friction directly motivated my work on the 64-bit Kei x86/x64 in-browser emulator: