不能直接用 std::string::find 搜二进制特征码,因其将 \0 视为字符串结尾导致截断,且 find 对含 \0 模式串行为不可靠;应使用字节数组(如 std::vector)配合 kmp 等字节匹配算法。

为什么不能直接用 std::string::find 搜二进制特征码
因为 std::string 把 \0 当作字符串结尾,而二进制文件里大量存在 \0 字节,一读进来就截断。哪怕你用 std::string_view 或构造带长度的 std::string,其 find 内部仍按字符语义处理,对含 \0 的模式串行为不可靠,尤其在 MSVC 下可能崩溃。
真正要搜的是字节数组(std::vector<uint8_t></uint8_t> 或裸指针 + 长度),不是文本字符串。
- 特征码本质是字节序列,比如
{ 0x48, 0x8b, 0x05, 0xcc, 0xcc, 0xcc, 0xcc },不是 C 风格字符串 - 所有标准库“字符串查找”函数都不保证对非文本数据的正确性,别图省事硬套
- 如果硬用
std::string存二进制数据,必须确保构造时传入长度,且全程避免调用任何隐式转 C 字符串的操作(如.c_str())
用 KMP 实现字节流匹配的关键三步
KMP 的核心价值是避免回退文件指针或重复读内存 —— 对大文件或 mmap 映射区域特别重要。但得自己写,标准库没提供 std::search 的字节安全重载(C++20 std::search 可用,但要注意迭代器类型)。
实操上只关注三个函数:构建 lps 数组、主匹配循环、边界处理。
-
lps[i]表示模式串pattern[0..i]的最长真前缀同时也是后缀的长度;它决定了匹配失败时模式串往右滑多少,不依赖目标数据 - 主循环中,用两个下标
i(文本位置)、j(模式位置),仅当j == pattern.size()才算找到一次匹配 - 每次读文件要用
std::ifstream::read配合gcount()判断实际读取字节数,避免 EOF 后read失败却继续匹配
简短示意(不完整,仅突出关键逻辑):
void build_lps(const std::vector<uint8_t>& pattern, std::vector<int>& lps) {
int len = 0;
lps[0] = 0;
for (int i = 1; i <h3>Windows 上用 <code>CreateFileMapping</code> + <code>MapViewOfFile</code> 加速大文件搜索</h3>
<p>读几百 MB 的 PE 文件时,逐块 <code>read()</code> + KMP 是可行的,但 mmap 更快且代码更干净 —— 尤其你要反复搜多个特征码。不过 Windows 的内存映射有坑。</p>
<ul>
<li>必须用 <code>GENERIC_READ</code> 打开文件,且 <code>CreateFileMapping</code> 的 <code>flProtect</code> 设为 <code>PAGE_READONLY</code>,否则 <code>MapViewOfFile</code> 可能返回 <code>NULL</code>
</li>
<li>映射视图大小不能超过文件实际大小;用 <code>GetFileSize</code> 获取真实尺寸,别信文件系统返回的“分配大小”</li>
<li>映射后得到的是 <code>LPVOID</code>,需 <code>static_cast<const uint8_t></const></code> 转成可索引的字节指针,KMP 就能在该指针上直接跑</li>
<li>别忘了 <code>UnmapViewOfFile</code> 和 <code>CloseHandle</code>,否则小工具跑几次就句柄泄漏</li>
</ul>
<h3>常见误判:对齐、通配符和多字节指令干扰</h3>
<p>特征码不是正则表达式,KMP 本身不支持通配符(如 <code>??</code> 表示任意字节)。很多工具里写的 <code>48 ?? 05 ?? ?? ?? ??</code> 是解析层做的预处理,不是 KMP 本体干的活。</p>
<ul>
<li>如果你搜的是 x64 指令,得考虑 RIP 相对寻址的 4 字节偏移字段是动态变化的,直接固定写死会漏匹配;得把那几位替换成通配符再做“模糊 KMP”,或者改用 Boyer-Moore 变种</li>
<li>某些特征码要求地址对齐(如 16 字节对齐的 AVX 数据),但 KMP 只管字节连续性,对齐得在匹配到位置后额外检查 <code>(pos % 16) == 0</code>
</li>
<li>PE 文件中资源段、重定位表等区域可能含大量相似字节序列,导致假阳性;建议限定搜索范围(如只扫 <code>.text</code> 节),用 <code>IMAGE_SECTION_HEADER</code> 解析节表再计算偏移</li>
</ul>
<p>最麻烦的永远不是算法本身,而是你拿到的“特征码”到底能不能稳定对应目标指令 —— 这得靠调试器反复验证,不是 KMP 能解决的。</p></int></uint8_t>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











