动态 API 解析链还原
1. 入口信号
text
Import Table 很少或为空
字符串里没有 WinAPI 名称,但行为明显调用网络/进程/注册表
Ghidra 看到 fs:[0x30] / gs:[0x60] / PEB_LDR_DATA
循环遍历 export name table 并计算 hash
大量常量形如 0xEC0E4E8E、0x7C0DFCAA目标是把 hash/函数指针恢复成 API 名称,并给后续 x64dbg/Frida 断点提供真实函数表。
2. 静态定位
powershell
python scripts/misc/ai_tool.py run triage_pe -- samples/app.exe --out exports/pe/app/triage.json
python scripts/misc/ai_tool.py run ghidra_headless_analyze -- samples/app.exe --out exports/pe/app/ghidra
rg -n "GetProcAddress|LoadLibrary|VirtualAlloc|WinHttp|RegSetValue" exports/pe/appGhidra 里优先找这些形态:
c
// PEB walk
peb = *(longlong *)(in_GS_OFFSET + 0x60);
ldr = *(longlong *)(peb + 0x18);
// export parser
names = image + export->AddressOfNames;
ordinals = image + export->AddressOfNameOrdinals;
functions = image + export->AddressOfFunctions;
// hash loop
hash = ror(hash, 13);
hash += tolower(ch);3. Hash 反解脚本
python
import pefile
def ror32(v, n):
return ((v >> n) | (v << (32 - n))) & 0xffffffff
def api_hash(name: str) -> int:
h = 0
for ch in name:
h = ror32(h, 13)
h = (h + ord(ch.lower())) & 0xffffffff
return h
targets = {0xec0e4e8e, 0x7c0dfcaa}
for dll in ["C:/Windows/System32/kernel32.dll", "C:/Windows/System32/advapi32.dll", "C:/Windows/System32/wininet.dll"]:
pe = pefile.PE(dll)
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
if not exp.name:
continue
name = exp.name.decode(errors="ignore")
h = api_hash(name)
if h in targets:
print(hex(h), dll, name)把匹配结果回填到 Ghidra:
text
api_hash_0xec0e4e8e -> VirtualAlloc
api_hash_0x7c0dfcaa -> CreateThread4. 动态断点
text
bp kernel32.LoadLibraryA
bp kernel32.LoadLibraryW
bp kernel32.GetProcAddress
bp ntdll.LdrGetProcedureAddress
bp kernel32.VirtualAlloc
bp kernel32.WriteProcessMemory如果样本手写 EAT parser,不会命中 GetProcAddress。改断 hash compare:
text
findallmem 8E4E0EEC
bp <hash_compare_addr>命中后记录:
text
module base:
export name candidate:
computed hash:
matched constant:
resolved function pointer:5. 路径分叉
| 解析结果 | 下一跳 |
|---|---|
VirtualAlloc/VirtualProtect | unpack / shellcode dump |
CreateRemoteThread/WriteProcessMemory | injection 行为链 |
WinHttpSendRequest/InternetOpenUrl | C2 / protocol |
RegSetValueEx/CreateService | persistence / IOC |
BCrypt/CryptDecrypt | crypto replay |
5.1 校验函数联动(keygen 场景)
许可证校验函数常动态解析比较/哈希 API(02-validation-function-location 的导入表 信号往往被解析链隐藏)。处置:
- 先解析出实际调用的 API 名(本节动态断点法)
- 在解析结果上追加两条分叉:
| 解析结果 | 下一跳 |
|---|---|
lstrcmpW/CompareStringW/memcmp 对比点 | 常量表对比 → 10-license-keygen/03 算法还原 |
BCryptVerifySignature 或自实现模幂 | 签名链 → 10-license-keygen/04 公钥替换 |
校验函数本身可用 10-license-keygen/02 的三步定位法(字符串 xrefs 反查)直接命中, 无需等待解析链——两条路径互为备份。
6. 攻击链 / 工作流
text
Import Table 稀疏 / 行为与导入不匹配
→ Ghidra 找 PEB walk、EAT parser 或 hash loop
→ 用脚本反解 hash 常量并回填 API 名称
→ x64dbg 断 LoadLibrary/GetProcAddress 或 hash compare
→ 建立函数指针表
→ 按解析结果跳到 unpack、network、injection、persistence 或 crypto7. Evidence
| 项 | 记录内容 |
|---|---|
| Hash 算法 | rotate、大小写、初始值、DLL 名是否参与 |
| 常量表 | hash 常量、匹配 API、DLL |
| 函数指针表 | table VA/RVA、slot、写入位置 |
| 动态命中 | GetProcAddress 参数或 hash compare |
| 下一跳 | unpack、IOC、crypto、patch |
8. MCP 工具映射
| 步骤 | MCP 工具 | 用途 |
|---|---|---|
| 初筛 | triage_pe | 判断 import 稀疏、节区异常 |
| 静态图 | ghidra_headless_analyze | 定位 PEB walk 和 hash loop |
| 断点 | make_x64dbg_breakpoint_script | 生成 LoadLibrary/GetProcAddress 断点 |
| 知识路由 | kb_router | 按 api hash、PEB、resolver 查下一篇 |