daisvke/woody-woodpacker

GitHub: daisvke/woody-woodpacker

一个用 C 语言实现的 ELF 64 位二进制打包器,通过对原始代码加密并注入运行时解包 stub 来转换可执行文件格式。

Stars: 0 | Forks: 0

# **woody-woodpacker** ## **目录** * [概述](#overview) * [执行流程](#execution-flow) * [运行时行为](#runtime-behavior) * [ELF 64-bit 文件结构](#elf-64-bit-file-structure) * [注入机制](#injection-mechanism) * [Stub 补丁系统](#stub-patch-system) * [入口点重定向](#entry-point-redirection) * [加密系统](#encryption-system) * [命令](#commands) * [自动化测试套件](#automated-test-suite) * [调试工具](#debug-tools) * [兼容性说明](#compatibility-notes) # **概述** 本项目实现了一个**支持运行时解包的 64-bit ELF packer**。 它将一个 ELF 二进制文件转换为一个新的可执行文件(`woody`),其中嵌入了: * 原始可执行 segment 的加密副本 * 注入到二进制文件中的自定义解包 stub 在运行时,stub 会在内存中重建原始程序并将执行权交还给它,从而在仅改变二进制表示形式的同时,保持完全一致的行为。 # **执行流程** ``` Original ELF → Parse ELF headers & program headers → Locate executable PT_LOAD segment → Extract segment (includes .text region) → Encrypt payload (XOR + additive cipher) → Inject stub into executable segment region (padding or shifting mode) → Patch stub with metadata (offsets, sizes, key) → Modify ELF entry point (e_entry → stub) → Generate "woody" Runtime: → Kernel loads ELF → Execution starts in stub → Stub decrypts payload in memory → Restores original code → Prints: ....WOODY..... → Jumps to original entry point → Original program executes ``` # **运行时行为** 在执行阶段,一个打包后的 `/bin/whoami` 表现如下: ``` ....WOODY..... username ``` 除了打印出的字符串外,最终行为与原始二进制文件保持完全一致。 # **ELF 64-bit 文件结构** ``` +----------------------------------------------------+ | ELF Header | | (64 bytes, ELF64_Ehdr structure) | +----------------------------------------------------+ | Program Header Table | | (Variable size, ELF64_Phdr entries) | | describes memory-mapped segments | +----------------------------------------------------+ | Text Section | | (Executable code inside PT_LOAD segment) | +----------------------------------------------------+ | Data Section | | (Initialized data inside PT_LOAD segment) | +----------------------------------------------------+ | Symbol Table Section | | (ELF64_Sym entries) | +----------------------------------------------------+ | String Table Section | | ("name1\0name2\0...") | +----------------------------------------------------+ | Section Header Table (optional) | | (ELF64_Shdr entries) | +----------------------------------------------------+ ``` # **注入机制** stub 被注入到**可执行 PT_LOAD segment**中,该 segment 通常包含: * `.text` * 可执行数据页 * segment 之间的 padding 它不限于仅在 `.text` 中,而是放置在**可执行内存映射区域**内部。 ## **1. Padding 注入(首选模式)** 当可执行 segment 内部存在足够空间时使用。 条件: ``` sizeof_stub ≤ padding_size ``` 其中: ``` padding_size = next_segment.p_offset - injection_offset ``` 行为: * stub 直接写入到现有的 padding 中 * 无需 ELF 重定位 * 无需对 headers 进行结构性修改 ``` [ PT_LOAD (code + .text) | padding | STUB ] ``` ## **2. 位移注入(后备模式)** 当 padding 空间不足或被明确要求时使用。 ### 步骤 1 — 扩展 segment 大小 ``` p_filesz += sizeof_stub; p_memsz += sizeof_stub; ``` ### 步骤 2 — 重定位 ELF 结构 注入点之后的所有 headers 都会被更新: * 程序头偏移量发生位移 * 节头偏移量发生位移 * ELF 节头表偏移量被更新 ### 步骤 3 — 重建二进制布局 ``` [ original ELF ] [ STUB inserted ] [ shifted ELF data ] ``` # **Stub 补丁系统** 注入的 stub 会使用运行时元数据进行 patch: ``` ww_t_patch patch; ``` 这包括: * 从 stub 到原始入口点的偏移量 * `.text` 节相对于 stub 的偏移量 * `.text` 大小 * segment 重定位偏移量 二进制文件内部的布局: ``` [ STUB ........ PATCH ........ KEY ] ``` # **入口点重定向** ELF 入口点被修改: ``` e_entry = injection_addr; ``` 结果: ``` Before: entry → main() After: entry → stub → main() ``` # **加密系统** ## XOR 加密 * 对称加密 * 加密和解密使用相同的函数 ## 加法层 在 XOR 之前应用: ``` byte → +offset → XOR → encrypted encrypted → XOR → -offset → original ``` # **命令** ## 基本用法 ``` make # Build project ./woody_woodpacker [OPTIONS] # 使用默认选项 make 并运行 packer,然后运行打包后的二进制文件,全程使用 valgrind make run # 示例:以 verbose 模式、padding injection 模式运行 packer,然后运行该二进制文件 ./woody_woodpacker /bin/ls -i p -v && ./woody ``` ## 选项 ### 详细模式 显示有关以下内容的详细信息: * ELF 解析 * 注入偏移量 * padding 分析 * 重定位操作 * stub patch 值 ``` -v --verbose ``` ### 注入模式:padding 尝试将 stub 注入到现有的可执行 segment padding 中。 ``` -i p --injection-type padding ``` * 最安全的模式 * 无需重构 ELF * 需要足够的可用空间 ### 注入模式:shift 当 padding 不足或被明确选择时,强制对 ELF 进行结构性修改。 ``` -i s --injection-type shift ``` * 重写 ELF 布局 * 位移 headers 和 segments * 保证注入成功 以下是一个可以直接放入的整洁 README 部分,符合你的项目基调和该测试脚本: ## 自动化测试套件 该项目包含一个简单的验证脚本,用于跨多个 ELF 二进制文件测试打包行为。 它会构建项目,在一组示例二进制文件上运行 packer,并验证执行输出的一致性。 ### 测试内容 对于 `resources/` 目录中的每个二进制文件,脚本会: * 使用 `make` 构建项目 * 在两种模式下执行 packer: * `-i s`(shift 注入模式) * `-i p`(padding 注入模式) * 运行生成的打包后二进制文件(`./woody`) * 检查执行输出中是否包含成功签名: ....WOODY.... * 报告每个二进制文件和模式的结果: * 🟢 PASS → 检测到正确的执行 * 🔴 FAIL → 输出缺失或无效 ### 如何运行测试 ``` chmod +x test.sh ./test.sh ``` ### 预期结构 ``` project/ ├── resources/ │ ├── bin1 │ ├── bin2 │ └── ... ├── woody_woodpacker ├── woody └── test.sh ``` # **调试工具** ``` readelf -l [filename] # Check program headers of the file readelf -S [filename] # Check section headers hexdump -C [filename] # Check the file in hex format vimdiff [filename 1] [filename 2] # Check the difference between two files # 从 code.o 中提取 .text section objcopy --dump-section .text=code-raw code.o # 以十六进制格式打印地址 0x401040 处加载的文件内容 gdb ./woody run (or r) x/16xw 0x401040 # 如果基地址为 0x4011ad,则在相对地址 11ad 处添加 breakpoint b *0x4011ad # 显示 ELF entrypoint gdb ./woody starti info files info proc map # 产生一个 trace trap 以在当前位置停止执行(在调试时很有用) int3 ``` # 兼容性说明 虽然该工具适用于许多标准的 Linux x86-64 ELF 二进制文件,但某些二进制文件可能无法打包,或者在打包后可能变得不稳定。 ## 为什么某些二进制文件会失败 并非所有 ELF 二进制文件都具有相同的布局特征。shift 注入模式取决于能否安全地: * 扩展 `PT_LOAD` segment(`p_filesz` / `p_memsz`) * 正确位移所有后续的程序头和节头 * 保留 segment 之间有效的页面对齐内存映射 * 在入口点重定向后保持一致的执行流程 由于以下原因,某些二进制文件无法安全满足这些条件: * **紧凑的 segment 打包** * `PT_LOAD` segment 之间没有足够的连续空间 * Stub 溢出越过相邻的映射区域 * **复杂或加固的 ELF 布局** * 多个重叠或紧密耦合的 `PT_LOAD` segment * 特殊用途的 loader 假设(例如 glibc,ld-linux 优化) * **RELRO / TLS / 动态 loader 约束** * 如果位移不正确,`GNU_RELRO` 和 `DYNAMIC` segment 可能会损坏 * 运行时 loader 期望严格的偏移量以进行重定位和链接 * **PIE + ASLR 边缘情况** * 某些二进制文件依赖于 segment 之间精确的相对布局 * 布局中的微小改变都可能使运行时假设失效 # **额外想法** - 支持 32-bit - 自定义 key 参数 - 支持 PE / Mach-O - 压缩
标签:ELF文件, 二进制处理, 加壳工具, 壳代码, 客户端加密, 密码学应用, 系统底层