From 5701acfdc5922c06fdc7682e99a0d20ab8810467 Mon Sep 17 00:00:00 2001 From: Antero Guy <46616092+antroguy@users.noreply.github.com> Date: Wed, 6 May 2026 15:34:05 -0400 Subject: [PATCH] Updated signatures, helper scripts, and README --- README.md | 101 +++++++++++++++--- .../pe_signature_finder.cpython-312.pyc | Bin 0 -> 36410 bytes cdp_enable_bof.c | 92 ++++++++++++++-- find_cdp_inputs.py | 11 +- find_start_server.py | 63 +++++------ requirements.txt | Bin 0 -> 1230 bytes 6 files changed, 203 insertions(+), 64 deletions(-) create mode 100644 __pycache__/pe_signature_finder.cpython-312.pyc create mode 100644 requirements.txt diff --git a/README.md b/README.md index 3bf0658..62aaa8a 100644 --- a/README.md +++ b/README.md @@ -58,23 +58,27 @@ Cause: Edge version mismatch - signatures are version-specific Solution: See "Finding New Signatures" below Tested Chrome Version: 147.0.7727.102 -Tested Edge Version: 147.0.3912.72 +Tested Edge Version: 147.0.3912.98 ## Finding New Signatures -The finder scripts are still included because they are useful for: +These scripts are used to find new signatures and supporting runtime inputs for +updated browser versions: -- validating new signatures -- checking symbol drift across browser versions -- reverse engineering / troubleshooting +- `StartRemoteDebuggingServer` +- `operator new` +- `TCPServerSocketFactory::CreateForHttpServer` +- derived `TCPServerSocketFactory` vtable information ### Pull the DLLs -Typical browser DLL locations: +Copy the DLL from the browser build you are targeting into the working +directory. On this machine, the installed DLLs live under versioned +subdirectories: ```powershell -Copy-Item "$env:ProgramFiles\\Google\\Chrome\\Application\\chrome.dll" . -Copy-Item "${env:ProgramFiles(x86)}\\Microsoft\\Edge\\Application\\msedge.dll" . +Copy-Item "C:\Program Files\Google\Chrome\Application\147.0.7727.138\chrome.dll" . +Copy-Item "C:\Program Files (x86)\Microsoft\Edge\Application\147.0.3912.98\msedge.dll" . ``` ### Pull the matching PDBs @@ -96,23 +100,86 @@ symchk /v /ocx edge-symchk.txt /s SRV*c:\symbols*https://msdl.microsoft.com/down Find `StartRemoteDebuggingServer`: ```powershell -python .\find_start_server.py .\chrome.dll .\chrome.dll.pdb -python .\find_start_server.py .\msedge.dll .\msedge.dll.pdb +python .\find_start_server.py .\chrome.dll +python .\find_start_server.py .\msedge.dll ``` +Example Edge output: + +```json +{ + "binary": "C:\\Users\\antrovmp\\Desktop\\CDP-Enable-BOF\\msedge.dll", + "pdb": "c:\\symbols\\msedge.dll.pdb\\23DB3E4A1AB4F3B44C4C44205044422E1\\msedge.dll.pdb", + "symbol": "?StartRemoteDebuggingServer@DevToolsAgentHost@content@@SAXV?$unique_ptr@VDevToolsSocketFactory@content@@U?$default_delete@VDevToolsSocketFactory@content@@@__Cr@std@@@__Cr@std@@AEBVFilePath@base@@1W4RemoteDebuggingServerMode@12@@Z", + "signature_rva": "0x0321EAC2", + "signature_va": "0x000000018321EAC2", + "signature_length": 30, + "signature_hex": "41 57 41 56 41 54 56 57 55 53 48 83 EC 50 44 89 CD 4C 89 C3 48 89 D7 48 89 CE 48 8B 05 5D", + "signature_c_initializer": "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, 0x5D", + "copy_to_code": "Use this for EDGE_START_SIG if analyzing msedge.dll, or CHROME_START_SIG if analyzing chrome.dll.", + "text_hits": 1 +} +``` + +Copy `signature_c_initializer` into the start signature array in +`cdp_enable_bof.c`: + +- Edge: `EDGE_START_SIG` at line 168 +- Edge mask: `EDGE_START_MASK` at line 181 +- Chrome: `CHROME_START_SIG` at line 194 +- Chrome mask: `CHROME_START_MASK` at line 207 + +For Edge, replace the bytes in `EDGE_START_SIG` with the new +`signature_c_initializer`. For Chrome, do the same with `CHROME_START_SIG`. + Find the supporting inputs: ```powershell -python .\find_cdp_inputs.py .\chrome.dll .\chrome.dll.pdb -python .\find_cdp_inputs.py .\msedge.dll .\msedge.dll.pdb +python .\find_cdp_inputs.py .\chrome.dll +python .\find_cdp_inputs.py .\msedge.dll ``` -These scripts emit: +Example Edge output: -- `StartRemoteDebuggingServer` -- `operator new` -- `TCPServerSocketFactory::CreateForHttpServer` -- derived `TCPServerSocketFactory` vtable +```json +{ + "binary": "msedge.dll", + "operator_new": { + "rva": "0x036335D8", + "va": "0x00000001836335D8", + "signature": "40 53 48 83 EC 20 48 8B D9 EB ?? 48 8B CB E8 ?? ?? ?? ?? 85 C0 74 ?? 48 8B CB", + "copy_to_code": "OPERATOR_NEW_SIG" + }, + "tcp_server_socket_factory": { + "create_for_http_server": { + "rva": "0x016C7310", + "va": "0x00000001816C7310", + "signature_hex": "48 89 D0 0F B7 51 08 48", + "signature_c_initializer": "0x48, 0x89, 0xD0, 0x0F, 0xB7, 0x51, 0x08, 0x48", + "copy_to_code": "EDGE_ENTRY1_SIG", + "vtable_rva": "0x0FBDCA70", + "vtable_va": "0x000000018FBDCA70" + }, + "layout": { + "size": 16, + "vtable_offset": 0, + "port_offset": 8 + } + } +} +``` + +Important fields and where they go: + +- `operator_new.copy_to_code` -> `OPERATOR_NEW_SIG` at line 221 +- `tcp_server_socket_factory.create_for_http_server.copy_to_code` -> + `EDGE_ENTRY1_SIG` at line 242 or `CHROME_ENTRY1_SIG` at line 246 +- `tcp_server_socket_factory.layout` is a sanity check for the object layout the + BOF expects: size `16`, vtable offset `0`, port offset `8` + +The script also derives the TCPServerSocketFactory vtable location. The BOF +still resolves that through the `VTABLE_ENTRY0_SIG` / `VTABLE_ENTRY0_MASK` +helper at lines 228 and 234 if that helper drifts. ## Notes diff --git a/__pycache__/pe_signature_finder.cpython-312.pyc b/__pycache__/pe_signature_finder.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbde98e9661ea07a262f2c979eefd6e4a0e997af GIT binary patch literal 36410 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

RwT`uEg63;I8Er(!K%2t39jm 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 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) { + if (count == 0 && first_hit) { + *first_hit = (uintptr_t)(start + offset + i); + } + count++; + } + } + offset += chunk; + } + + HeapFree(GetProcessHeap(), 0, buf); + return count; +} + 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) { @@ -561,17 +633,23 @@ static BOOL ResolveSymbolsRuntime(HANDLE hProcess, uintptr_t module_base, const void* entry1 = NULL; uint64_t destr_addrs[32]; int destr_count = 0; + int start_hits = 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) { + start_hits = CountRemoteSignatureHits( + hProcess, text_base, text_size, target->start_sig, target->start_mask, target->start_sig_len, start_server); + if (start_hits == 0 || !*start_server) { BeaconPrintf(CALLBACK_ERROR, "[-] StartRemoteDebuggingServer signature not found"); return FALSE; } + if (start_hits != 1) { + BeaconPrintf(CALLBACK_ERROR, "[-] StartRemoteDebuggingServer signature was not unique (hits=%d)", start_hits); + 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) { @@ -602,7 +680,7 @@ static BOOL ResolveSymbolsRuntime(HANDLE hProcess, uintptr_t module_base, const 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) }, + { "edge", L"msedge.exe", L"msedge.dll", EDGE_START_SIG, EDGE_START_MASK, 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; @@ -797,4 +875,4 @@ void go(char* args, int len) { HeapFree(GetProcessHeap(), 0, local_page); VirtualFreeEx(hProcess, remote_mem, 0, MEM_RELEASE); CloseHandle(hProcess); -} +} \ No newline at end of file diff --git a/find_cdp_inputs.py b/find_cdp_inputs.py index 61bc2a4..c40c4a9 100644 --- a/find_cdp_inputs.py +++ b/find_cdp_inputs.py @@ -13,7 +13,7 @@ import pefile ROOT = Path(__file__).resolve().parent sys.path.insert(0, str(ROOT)) -from pe_signature_finder import analyze_symbols, demangle_symbol # type: ignore +from pe_signature_finder import analyze_symbols, demangle_symbol, format_bytes # type: ignore # From the current CDP-Enabler project: the Chrome/Edge allocator used for the @@ -47,6 +47,10 @@ def find_unique_regex(pattern: bytes, data: bytes) -> int: return matches[0] +def format_c_initializer(data: bytes) -> str: + return ", ".join(f"0x{b:02X}" for b in data) + + def choose_tcp_factory_entry1(pe_path: Path, pdb_path: Path): with contextlib.redirect_stderr(io.StringIO()): results = list( @@ -114,15 +118,20 @@ def main(): "rva": f"0x{operator_new_rva:08X}", "va": f"0x{image_base + operator_new_rva:016X}", "signature": OPERATOR_NEW_SIGNATURE, + "copy_to_code": "OPERATOR_NEW_SIG", }, "tcp_server_socket_factory": { "create_for_http_server": { "rva": f"0x{entry1_rva:08X}", "va": f"0x{entry1_va:016X}", + "signature_hex": format_bytes(entry1_sig), + "signature_c_initializer": format_c_initializer(entry1_sig), + "copy_to_code": "EDGE_ENTRY1_SIG" if args.pe.name.lower() == "msedge.dll" else "CHROME_ENTRY1_SIG", "derived_vtable_entry1_candidates": [f"0x{candidate:08X}" for candidate in vtable_candidates], "vtable_entry1_rva": f"0x{vtable_entry1_rva:08X}", "vtable_rva": f"0x{vtable_entry1_rva - 8:08X}", "vtable_va": f"0x{image_base + vtable_entry1_rva - 8:016X}", + "note": "The script emits the CreateForHttpServer function signature and derives the vtable location. The PIC code still uses VTABLE_ENTRY0_SIG / VTABLE_ENTRY0_MASK for vtable matching if that helper drifts.", }, "layout": { "size": 16, diff --git a/find_start_server.py b/find_start_server.py index 8fa9766..abc9cdf 100644 --- a/find_start_server.py +++ b/find_start_server.py @@ -14,16 +14,9 @@ sys.path.insert(0, str(ROOT)) from pe_signature_finder import analyze_symbols, format_bytes # type: ignore -KNOWN_START_SERVER_SIGNATURES = { - "msedge.dll": bytes.fromhex( - "41 57 41 56 41 54 56 57 55 53 48 83 EC 50 " - "44 89 CD 4C 89 C3 48 89 D7 48 89 CE 48 8B 05 9D" - ), - "chrome.dll": bytes.fromhex( - "41 57 41 56 56 57 55 53 48 83 EC 48 " - "44 89 CD 4D 89 C6 48 89 D3 48 89 CE 48 8B 05 41" - ), -} + +def format_c_initializer(data: bytes) -> str: + return ", ".join(f"0x{b:02X}" for b in data) def load_text_section(pe_path: Path): @@ -48,25 +41,8 @@ def main(): args = parser.parse_args() pe, _text_section, text_data = load_text_section(args.pe) - - known_sig = KNOWN_START_SERVER_SIGNATURES.get(args.pe.name.lower()) - if known_sig is None: - raise SystemExit(f"no built-in signature for {args.pe.name}; provide a PDB for ground truth") - - pattern_hits = [m.start() for m in re.finditer(re.escape(known_sig), text_data)] - if len(pattern_hits) != 1: - raise SystemExit(f"expected exactly one StartRemoteDebuggingServer signature hit, got {len(pattern_hits)}") - - matched_offset = pattern_hits[0] - matched_rva = _text_section.VirtualAddress + matched_offset - output = { "binary": str(args.pe), - "signature_rva": f"0x{matched_rva:08X}", - "signature_va": f"0x{pe.OPTIONAL_HEADER.ImageBase + matched_rva:016X}", - "signature_length": len(known_sig), - "signature_hex": format_bytes(known_sig), - "text_hits": 1, } if args.pdb: @@ -77,21 +53,30 @@ def main(): str(args.pdb), "*StartRemoteDebuggingServer*", min_sig=8, - max_sig=64, + max_sig=80, ) ) good = [r for r in results if r.signature and r.match_count == 1] - if good: - result = good[0] - signature = result.signature - assert signature is not None - output["pdb"] = str(args.pdb) - output["symbol"] = result.symbol.name - output["pdb_rva"] = f"0x{result.symbol.rva:08X}" - output["pdb_va"] = f"0x{pe.OPTIONAL_HEADER.ImageBase + result.symbol.rva:016X}" - output["pdb_signature_hex"] = format_bytes(signature) - output["pdb_signature_text_hits"] = verify_unique(signature, text_data) - output["signature_matches_pdb_rva"] = matched_rva == result.symbol.rva + if not good: + raise SystemExit("failed to derive a unique StartRemoteDebuggingServer signature from the PDB") + + result = good[0] + signature = result.signature + assert signature is not None + output["pdb"] = str(args.pdb) + output["symbol"] = result.symbol.name + output["signature_rva"] = f"0x{result.symbol.rva:08X}" + output["signature_va"] = f"0x{pe.OPTIONAL_HEADER.ImageBase + result.symbol.rva:016X}" + output["signature_length"] = len(signature) + output["signature_hex"] = format_bytes(signature) + output["signature_c_initializer"] = format_c_initializer(signature) + output["copy_to_code"] = ( + "Use this for EDGE_START_SIG if analyzing msedge.dll, or CHROME_START_SIG if analyzing chrome.dll." + ) + output["text_hits"] = verify_unique(signature, text_data) + + else: + raise SystemExit("a matching PDB is required") print(json.dumps(output, indent=2)) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..19f269d350962698621a30a58afed03df7baa6d5 GIT binary patch literal 1230 zcmZux!ES<35ZrTwLjl>W`_r840!?B+1=UM!N1?7 zmADSp+7c(Xl|{J!ScT`to^Va<&gM81yR{pjN<_EFU~G!_v8AJEi|o3~Ol;=ykei`q zjri8qILuE_RVY?UaIr^*_<&&cP+^D6%sxH)0q@y6?XnKG1Fsq!_c(z&E6+Ma?US`Y zZ5&rwfj63j)rL+5=8F3ou}>le3QW*KpUivZj)}Y6NAD95r#?S1R*x^D5p-&x$(X5T z*a~G;NY~vnP9(%TX;H!Sd zWaI=zBKt;-5`5pGX_#c}i3$p{#?pO32M{%QhVd1mrL)OAnshUV-CkliOYVDM?+t#5 zKb4{uQsFXd0Uq>+6lagA?^W8qfXB1F+3Tp5cu;xPYN0vRiolm&OpfO-M|1zg%D?B> zy&d68DlH9K&ydrYG55_fM@pu6Cxz%h_9o*s4)3M!k243Y)6q_9^jJZS$oUS4!Pjye z_2U;`gPC-cCfo}3Q{U^D;B{%N{|ap(awi0r1#blw@+jvS=Pc0w)M?qAxlugmMs+$u PYkvFOVA)Tv4GQrO;6=2$ literal 0 HcmV?d00001