0x355/sigscan

GitHub: 0x355/sigscan

基于 Rust 编写的 Windows x64 进程与 PE 文件特征码扫描器,帮助逆向工程师在不同版本和 ASLR 环境下稳定定位函数和代码结构。

Stars: 0 | Forks: 0

# sigscan 一个快速、只读的**签名/特征码扫描器**,用于 Windows x64 进程和 PE 模块,使用 Rust 编写。 专为逆向工程研究而构建,无注入、无写入、无 shellcode。 ``` [+] Process : Notepad.exe (PID 22224) [+] Module : Notepad.exe [+] Range : 00007FF7CB3B0000 - 00007FF7CB6B1000 (3076 KiB) [MATCH] address : 00007FF7CB3B1123 offset : +0x1123 bytes : E8 18 A5 14 00 [+] Total matches: 1 ``` ## 什么是签名扫描? **签名**(或**特征码**)是一小段字节序列,用于唯一标识已编译二进制文件中的特定指令或代码结构。逆向工程师使用签名在不同版本的二进制文件中重新定位函数或数据结构,与其硬编码一个随每次更新而改变的地址,不如搜索周围的字节模式,这要稳定得多。 游戏研究中的一个典型用例: 1. 在反汇编器(IDA、Ghidra、Binary Ninja...)中找到一个感兴趣的函数。 2. 从函数序言或唯一指令中提取一段简短且独特的字节序列。 3. 使用 `sigscan` 在实时进程的运行中定位该序列,无论 ASLR 或版本如何变化,都能为您提供当前地址。 ## 通配符匹配 真实代码通常包含直接嵌入在指令中的绝对地址或相对偏移量。这些操作数字节在不同的运行(ASLR)或不同的版本之间会发生变化。 **通配符**允许您编写一个模式来匹配不变的操作码字节,同时忽略可变的操作数字节。 语法采用 IDA 风格的十六进制,使用 `??` 表示通配符: ``` 48 8B ?? ?? ?? 89 ^^ ^^ ^^ <- exact bytes, must match precisely ^^ ^^ ^^ <- wildcards, match any byte ``` | 标记 | 含义 | |-------|---------| | `48` | 精确匹配字节 `0x48` | | `??` | 匹配任意单个字节 | | `?` | `??` 的别名 | 在内部,解析器将其转换为 `Vec>`: - `Some(0x48)` -> 精确字节 - `None` -> 通配符 然后,扫描器遍历模块缓冲区并在每个字节位置测试该模式。 ## 模块相对偏移量 Windows 会在运行时由 ASLR 选择的一个**基地址**加载 DLL。 像 `0x00007FF812341234` 这样的**绝对地址**在重启后是毫无意义的。 **模块相对偏移量**(`+0x1234`)告诉您匹配项位于模块内部的深度。要在运行时转换回绝对地址: ``` absolute_address = module_base + relative_offset ``` `sigscan` 会同时打印两者,以便您选择记录哪一个。 ### 如何构建 **前置条件** - Rust 稳定版工具链(`rustup toolchain install stable`) - Windows x64 目标(此 crate 受 `cfg(target_os = "windows")` 保护) ``` git clone https://github.com/0x355/sigscan cd sigscan cargo build --release ``` 发布版二进制文件最终位于 `target\release\sigscan.exe`。 **运行测试**(模式解析器和扫描器单元测试可在任何平台上运行): ``` cargo test ``` ## 用法 ``` sigscan [OPTIONS] ``` | 参数 / 选项 | 描述 | |-------------------|---------------------------| | `TARGET` | 进程名称(`notepad.exe`)、数字 PID,**或**磁盘上 PE 文件的路径(任何包含 `/`、`\` 或 `:` 的内容将被视为文件路径) | | `PATTERN` | IDA 风格的十六进制模式,例如 `"48 8B ?? ?? ?? 89"` | | `-m`, `--module` | 将扫描限制在一个模块内(例如 `--module user32.dll`) | | `-f`, `--first` | 在第一次匹配后停止(当使用 `--patterns` 时,应用于每个模式) | | `-n`, `--count N` | 在 N 次匹配后停止(当使用 `--patterns` 时,应用于每个模式) | | `--all-sections` | 扫描每个节,而不仅仅是可执行节(默认只扫描 `IMAGE_SCN_MEM_EXECUTE`,例如 `.text`) | | `--patterns FILE` | 在单次遍历中扫描多个签名;与位置参数 `PATTERN` 互斥 | | `--disasm` | 打印从每个匹配地址开始的 Intel 语法反汇编代码(使用 `iced-x86`;位数根据目标的 WOW64 状态自动检测) | | `--disasm-count N`| 在每次匹配后要反汇编的指令数量(默认 5) | | `--disasm-before N`| 通过流同步在匹配项**之前**显示的指令数量(默认 2;设为 0 以禁用)。匹配行用 `>>` 标记 | ### 磁盘文件扫描 如果 `TARGET` 包含路径分隔符(`/`、`\`)或驱动器号冒号,`sigscan` 将从磁盘读取 PE 文件,而不是附加到实时进程。位数(`x86` vs `x64`)和首选的 `ImageBase` 取自 PE Optional Header,因此报告的绝对地址在运行时受 ASLR 影响是正确的。模块相对偏移量(`+0x...`)在两种模式下都是稳定的。 ``` sigscan C:\Windows\System32\notepad.exe "48 89 5C 24" --first --disasm sigscan ./packed.exe "55 8B EC" --all-sections ``` 此模式也适用于 Linux/macOS——适用于从非 Windows 主机分析 Windows 二进制文件。`--module` 标志与文件目标不兼容(文件本身就是单个模块)。 ### `--patterns` 文件格式 每行一个签名。`#` 表示注释,持续到行尾。空行将被忽略。每个非空行要么是一个纯粹的模式,要么是 `LABEL = PATTERN`;标签将代替输出中的 `MATCH` 使用,否则未标记的模式将被命名为 `pattern 1`、`pattern 2` 等。 ``` # 示例 signatures Prologue = 48 89 5C 24 NearCall = E8 ?? ?? ?? ?? LeaRipRelative = 48 8D 0D ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? # unlabeled ``` ## 限制 - **实时进程扫描仅限 Windows。** Toolhelp32 / ReadProcessMemory API 是 Windows 特有的。磁盘文件扫描可在任何操作系统(Linux/macOS/Windows)上运行。模式解析器、PE 解析器、扫描器和反汇编器单元测试在 CI 的所有平台上运行。 - **只读** `sigscan` 从不写入目标进程。它仅使用 `PROCESS_VM_READ | PROCESS_QUERY_INFORMATION` 打开句柄。 - **无内核模式扫描。** 仅扫描通过 `ReadProcessMemory` 可访问的用户模式页面。 - **受保护的进程 (PPL)。** 反作弊软件和某些操作系统进程(`csrss.exe`、`smss.exe`)使用内核强制保护级别,即使以管理员身份运行,也会阻止 `OpenProcess` 成功。 - **混淆/加壳模块** 如果模块的内存中布局与其磁盘上的 PE 不同(例如自定义加载器、运行时加壳),报告的模块大小可能不准确。 - **性能** 扫描器是一个简单的 O(n * m) 线性搜索。对于非常大的模块(> 200 MB)和长模式,请考虑使用 `--module` 缩小范围。 - **支持 WOW64(32 位)进程:** 其 32 位模块通过 `TH32CS_SNAPMODULE32` 枚举,进程架构显示在头部中。 ## TODO 计划的改进,大致按影响力排序: - [x] **SIMD 加速的首字节搜索。** 将 `scanner::scan` 中手动逐字节跳过替换为 `memchr::memchr`,以向量化候选查找循环。在大型模块上通常快 5-20 倍。 - [x] **PE 感知扫描。** 解析 PE 头,默认将扫描限制在 `IMAGE_SCN_MEM_EXECUTE` 节(例如 `.text`)。减少字符串/资源数据的误报,并缩小搜索空间。添加 `--all-sections` 以恢复当前行为。 - [x] **单次遍历多模式扫描。** 接受 `--patterns sigs.txt`(每行一个模式,带有可选标签),这样可以在无需重新枚举模块和重新读取内存 N 次的情况下定位多个签名。 - [ ] **`--json` 输出模式。** 用于管道传输到其他工具(IDA 脚本、自动化、已知偏移量的 CI 检查)的机器可读输出。 - [x] **将 `scanner.rs` 中的 `unreachable!` 替换为 `debug_assert!` + 提前返回。** 如今,假设的解析器错误会导致 release 模式下的 panic;软回退更为安全。 - [x] **每次匹配时的反汇编上下文。** 可选的 `--disasm` 标志,使用 `iced-x86` 打印匹配地址处的指令(及其前后几条),以帮助确认命中是否符合预期。 - [x] **向后反汇编上下文。** 扩展 `--disasm` 以通过在候选流偏移量上同步(尝试 `match-1..match-15`,选择最深处能干净地落在匹配地址上的结果)来显示匹配之前的 K 条指令。有助于确认匹配是否位于函数序言边界。*(通过 `--disasm-before` 向后迭代一条指令 K 次;默认 2。)* - [x] **扫描磁盘上的 PE 文件。** 允许 `sigscan ` 加载并扫描 PE,而无需附加到实时进程,这在目标无法运行或反调试严密时非常有用。*(文件模式也适用于 Linux/macOS——二进制文件不再在非 Windows 上硬性失败;只有实时进程扫描仍限 Windows。)* - [ ] **针对长模式的 Aho-Corasick / Boyer-Moore。** 当前扫描为 O(n·m)。对于长模式或多模式,更智能的算法将摊销成本。 ## 许可证 MIT
标签:Conpot, DAST, Ghidra, IDA Pro, PE模块, Rust, Windows安全, x64, 二进制分析, 云安全运维, 云资产清单, 内存扫描, 十六进制搜索, 反ASLR, 只读扫描, 可视化界面, 地址定位, 字典生成, 字节序列, 恶意软件分析, 无注入, 模式扫描, 游戏安全, 游戏逆向, 特征码扫描, 端点可见性, 网络流量审计, 进程扫描, 逆向工程, 通知系统, 通配符匹配