diabloidyobane/PEReconstruct
GitHub: diabloidyobane/PEReconstruct
恢复运行时被擦除 PE 头的手动映射 DLL,伪造合成头使其能被 IDA 正常静态分析。
Stars: 0 | Forks: 0
# PEReconstruct
恢复并静态分析在运行时被擦除 PE 头的**手动映射 DLL**。适用于任何你可以调用 `OpenProcess()` 的进程。无需驱动程序、调试器或符号。纯 stdlib Python 3 实现。
常见目标:恶意软件加载器、进程内植入物、加壳程序、反作弊模块,以及任何由自定义用户模式加载器而非 Windows 加载器映射的代码。
## 何时需要此工具
手动映射的 DLL 通常会:
1. 由自定义的用户模式加载器加载(而不是 Windows 加载器)
2. 在 `DllMain` 之后擦除其 `MZ` / `PE\0\0` 头,这样常规的内存扫描就不会发现它们
3. 有时也会擦除节表,只留下原始的 RX 页面
普通的转储工具(`scylla`、`pe-sieve`)在头信息丢失时,要么难以发现它们,要么根本无法找到。PEReconstruct 通过运行时特征(可执行 + 私有)找到这些内存区域,并伪造一个合成的 PE 头,以便 IDA 能够像分析普通 DLL 一样分析这些转储。
## 流水线
```
┌─────────────────────────────────┐
│ Target process (running) │
│ PID = whatever │
└────────────┬────────────────────┘
│
┌────────────────────────┼────────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────────┐ ┌────────────────────┐
│ scan_exec_ │ │ scan_pe_deep.py │ │ dump_manualmap.py │
│ private.py │ │ │ │ │
│ │ │ Aggressive PE-sig │ │ Looks for MZ at │
│ Lists every RX │ │ sweep — finds wiped │ │ region start (the │
│ MEM_PRIVATE │ │ DLLs by their PE\0\0 │ │ "well-behaved" │
│ region. The big │ │ at +e_lfanew. │ │ case where headers │
│ one (~1-10 MB, │ │ Catches anti-dumping │ │ survive). │
│ entropy ~7) is │ │ tricks where MZ is │ │ │
│ usually it. │ │ zeroed but PE isn't. │ │ │
└────────┬─────────┘ └──────────┬───────────┘ └─────────┬──────────┘
│ │ │
└───────────────────────┼────────────────────────┘
│ (you pick the right region from the surveys)
▼
┌────────────────────────────────┐
│ raw .bin file on disk │
│ "execpriv__ │
│ 0x.bin" │
│ filename encodes runtime VA │
│ you'll feed to step 3. │
└────────────┬───────────────────┘
│
▼
┌────────────────────────────────┐
│ rebuild_headerless.py │
│ │
│ Forges a synthetic PE header │
│ in the first 0x1000 bytes: │
│ - DOS stub │
│ - PE32+ NT headers │
│ - Single .text section RX │
│ covering the whole image. │
└────────────┬───────────────────┘
│
▼
┌────────────────────────────────┐
│ _reconstructed.dll │
│ (loadable PE — open in IDA) │
└────────────┬───────────────────┘
│
▼
┌────────────────────────────────┐
│ analyze_hooks.py │
│ resolve_exports.py │
│ │
│ Reconstruct the IAT from │
│ absolute addresses found in │
│ the dump; name every API │
│ call by module+offset. │
└────────────────────────────────┘
```
## 快速开始
```
# 1. 找到你的目标进程
tasklist | findstr /i ""
# 记下 PID
# 2. 调查可执行私有区域(无头捕获器)
py 01_acquire/scan_exec_private.py --pid --dump
# 2b. 可选地也可尝试深度 PE-sig 扫描
py 01_acquire/scan_pe_deep.py --pid
# 2c. 如果目标具有有效的 MZ 头(对于隐藏模块很罕见),这是最快的方法
py 01_acquire/dump_manualmap.py --pid
# 输出:位于 C:\path\to\execprivate_\ 的 .bin 文件夹
# 通过大小 (1-10 MB) 和熵值 (~6-7) 识别候选项。
# 文件名模式: execpriv__0x.bin
# 3. 重构 PE — 从文件名传入运行时基址
py 02_reconstruct/rebuild_headerless.py \
execprivate_/execpriv__.bin \
0x \
target_reconstructed.dll
# 4. 在 IDA 中打开 target_reconstructed.dll。自动分析。完成。
# 5. 可选:用导入 + 命名的 API 调用进行丰富
py 03_enrich/analyze_hooks.py --pid \
--target target_reconstructed.dll \
--target-base 0x \
--out target_imports.md
```
## 目录布局
| 文件夹 | 脚本 | 用途 |
|---|---|---|
| `01_acquire/` | `scan_exec_private.py` | **主要工具。** 列出所有 RX MEM_PRIVATE 区域。优先使用此工具——适用于无头的转储。 |
| `01_acquire/` | `scan_pe_deep.py` | 跨所有已提交内存的激进 PE 签名扫描。能捕获 `MZ` 被清零但 `PE\0\0` 未被清零的转储。 |
| `01_acquire/` | `dump_manualmap.py` | 严格的 `MZ` 位于区域起始处的检测器。仅在目标保留头信息时使用。 |
| `01_acquire/` | `dump_contiguous_image.py` | 将目标中的连续 VA 范围拼接成单个转储,对未映射的间隙进行零填充。 |
| `02_reconstruct/` | `rebuild_headerless.py` | **核心工具。** 在原始转储的前 0x1000 字节上伪造 DOS+NT+节表。 |
| `03_enrich/` | `analyze_hooks.py` | 遍历跳板页,通过绝对地址模式识别被 Hook 的函数。生成 markdown 报告。 |
| `03_enrich/` | `resolve_exports.py` | 将 `module.dll:0xOFFSET` 解析为 `kernel32!CreateFileA+0x12` 这样的名称。 |
## 重建注意事项
**无节恢复。** `rebuild_headerless.py` 会声明一个覆盖整个映像 RX 的 `.text` 节。原始文件可能包含独立的 `.rdata` / `.data` / `.pdata` 节——它们将变得与代码无法区分。副作用:
- IDA 会尝试将数据区域作为代码反汇编(看起来像乱码)
- 高熵的嵌入数据(加密块、压缩资产)会显示为一段“无效指令”——这没关系,只要不尝试将它们作为函数解释即可
- `.idata` 导入表已消失——除非重建导入(使用 `analyze_hooks.py`),否则无法按名称跟踪 API 调用
**无重定位表。** 重建的 DLL 硬编码了原始的运行时 `ImageBase`。如果你尝试通过 `LoadLibrary` 实际加载它,ASLR 会对其重定位,从而导致转储内部的绝对指针失效。**只能将输出用于 IDA 中的静态分析**,不能用于重新注入。
**被擦除的填充。** 如果加载器将转储内部的区域(PE 头填充、已释放的分配)清零,这些字节将永久丢失。重建无法恢复它们——IDB 中过去存在数据的地方将出现零填充的间隙。
**加固的进程。**
当目标安装了内核回调(例如 `ObRegisterCallbacks`)时,`OpenProcess(PROCESS_VM_READ)` 会被阻止。你将收到 `ERROR_ACCESS_DENIED (5)` 或一个被剥离权限的句柄。要继续操作:
- 在启动前暂停保护服务(仅适用于作为独立用户模式服务运行的保护)
- 或者使用内核模式的读取原语——通过 `kdmapper` 使用你自己的驱动程序,或任何基于 `\Device\PhysicalMemory` 的辅助工具。这里的转储脚本都假设使用用户模式 RPM,否则会静默失败。
## Python 要求
仅使用标准库。在 Windows 上通过 CPython 3.12 测试。无需 pip 安装。
## 结合 Claude Code 使用
PEReconstruct 专为通过编码代理驱动而设计——每个脚本都基于 `argparse`,打印结构化输出,并写入确定性的文件名。一个典型的 Claude Code 会话如下所示:
有用的提示词:
- *"调查 PID `` 中隐藏的模块,并告诉我哪些区域看起来像手动映射的 DLL。"*
- *"在基址 `0x ` 处重建 ``,并通过 IDA-Pro MCP 服务器在 IDA 中打开它。"*
- *"对重建的 DLL 运行 `analyze_hooks.py`,并总结它从哪些 Win32 模块导入。"*
提示:这与 [ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) 配合使用效果很好。
重建后,将 Claude 指向 IDA MCP 服务器,以通过 MCP 驱动反汇编、反编译和符号命名。
## 安装为 Claude Code 技能
此仓库中的 `skills/pe-reconstruct/` 目录是一个可直接使用的 [Claude Code skill](https://docs.claude.com/en/docs/claude-code/skills)。
安装后,当你询问有关转储、恢复或分析手动映射/无头 DLL 的问题时,Claude Code 会自动调用它。
```
# 为当前用户安装(所有项目)
mkdir -p ~/.claude/skills
cp -r skills/pe-reconstruct ~/.claude/skills/
# 或者仅为一个项目安装
mkdir -p .claude/skills
cp -r skills/pe-reconstruct .claude/skills/
```
该技能的 `SKILL.md` 会准确地告诉 Claude 针对哪个任务运行哪个脚本以及如何将它们串联起来。
## 许可证
MIT — 请参阅 [LICENSE](LICENSE)。
标签:API接口, Claude Code, DAST, IDA Pro分析, OpenProcess, PE头抹除恢复, PE重建, SecList, Windows内存扫描, 云安全监控, 云资产清单, 免杀分析, 内存取证, 加载器分析, 反作弊对抗, 安全对抗, 安全工具开发, 恶意软件分析, 手动映射DLL, 数字取证, 无驱动无调试器, 端点可见性, 纯标准库Python, 自动化脚本, 进程内存读取, 逆向工程, 静态分析