版本:3 | 许可:GPLv2+ | 平台:Windows
Binmay 是一个轻量级命令行工具,用于在文件或流中搜索二进制字符串,并可选择性地替换为另一个字符串。通过灵活的掩码机制,支持精确到位(bit)的匹配与替换控制。适用于固件修改、二进制补丁、日志清理、注册表分析等场景。

近期,DeepSeek 编程能力增强了,我令其以 资深编程专家角色 经几十次深度优化源码,结果喜人。
原作者:Sean Loaring,Slore
原地址:d1dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6L8r3!0J5k6h3I4W2k6g2)9J5c8X3u0A6L8X3#2S2P5b7`.`.
新版本对源码 binmay.c 进行了近乎重写级别的深度优化,在保持原有设计思想的基础上,修复了大量隐性缺陷,大幅提升了健壮性、性能以及可维护性。以下是按维度划分的详尽分析。
优化版从底层修复了原代码的编译错误、内存泄漏、线程安全无关的全局状态污染、不安全文件操作等硬伤,并通过临时文件原子写、智能搜索加速、灵活掩码语法、跨平台二进制模式支持、完整错误恢复链等改进,将一个小巧但粗糙的工具提升到工业生产级质量。
Binmay 由 Sean Loaring 原创,Slore 和 Hook 后续改进。
许可证:GNU GPL version 2 或更高版本。
| 原始代码 |
优化版本 |
改进意义 |
f_malloc 中错误地强制转换为 (struct input*)(未定义结构) |
完全移除无意义的转换,使用统一的 xmalloc(void *p) |
消除编译错误与未定义行为 |
未处理 Windows 文本模式可能导致 \n -> \r\n 转换 |
引入 set_binary_mode(),对 stdin/stdout/stderr 设置 O_BINARY |
保证二进制数据在 Windows 下正确读写 |
自定义 getopt 存在状态机 bug,且返回 -1 非标准 |
保留自定义 getopt,但修复了错误返回逻辑(统一 return '?'),并增强错误信息 |
命令行解析更健壮,行为更符合预期 |
| 原始代码 |
优化版本 |
全局变量泛滥:verbose、matches、pukebuf、plen 等大量使用全局,资源释放困难 |
所有配置集中到动态分配的 struct app_config,通过指针传递,main 末尾统一 free |
内存泄漏:buffered_handle、file_handle、masked_string 等通过 f_malloc 分配后从未释放 |
实现完备的 *_free 系列函数,main 采用 goto 清理链,无论成功或失败路径均会释放所有已分配资源 |
缓冲区固定 1024 字节,且 masked_string_setstr 中上游长度检查形同虚设(u: 格式会加倍) |
缓冲区扩大至 IO_LEN = 128 * 1024(128KB),大幅减少 I/O 次数;长度检查前置至 process_string_default 内部,防止越界 |
| 原始代码 |
优化版本 |
多数函数内部直接 exit(1) 或 abort(),无法由调用者优雅恢复,且资源泄露 |
所有函数返回错误码或 LEN_UNSET,错误层层上传,main 统一处理并跳转清理,确保文件、内存等资源被释放 |
fwrite 未处理短写(short write) |
实现 fwrite_all,循环写入直到全部字节发送完毕,避免大数据量时写不完整 |
-u/-U 原地更新方式极不安全:直接 rename 原文件为 .org,然后打开原文件名写入。若程序中途崩溃,原文件已消失,数据不可恢复 |
采用临时文件 + 原子重命名模式:先用 mkstemp(POSIX)或 _mktemp_s(Windows)创建安全临时文件,所有输出写入临时文件,成功后才执行重命名。失败时保留临时文件并给出明确恢复提示。若已备份*.0rg则不覆盖,保留初始版本,零数据丢失风险 |
| 掩码长度不一致时仅警告并截断,可能产生非预期结果 |
严格校验掩码长度与字符串长度必须相等,否则报错退出 |
| 空搜索字符串或非法前缀无检查 |
增加 search->length == 0 检查,非法前缀返回错误 |
buffered_skip 中偏移量 coffset 可能溢出(原为 int) |
coffset 改为 long long,并在跳过前检查溢出 |
| 原始代码 |
优化版本 |
masked_string_seek 对每个位置无差别调用 masked_string_match(O(n*m) 的朴素匹配) |
引入快速跳跃过滤:在掩码设置后预先计算出前两个非通配字节的位置 first_nonwild、second_nonwild,以及最后一个非通配字节(可选)。搜索时先检查这三个关键字节是否匹配,不匹配则立即跳过,只有通过快速筛选后才执行完整的逐字节掩码匹配。对于存在大量通配符的掩码(如仅固定首尾字节),性能提升一个数量级 |
| 缓冲区大小为 2KB,搜索时滑动窗口小,频繁 I/O |
缓冲区提升至 256KB(IO_LEN * 2),单次读入更多数据,大幅减少系统调用和文件读取次数 |
| 原始代码 |
优化版本 |
掩码只能通过 -S/-R 指定十六进制字节序列(如 00ff),表达能力有限 |
新增三种掩码语法: • 正常模式:保留原有十六进制对,同时支持 ?? 或 .. 作为通配符(对应掩码字节 0x00) • 忽略列表 !:如 !0,2-4,表示仅忽略指定字节,其余字节必须完全匹配 • 匹配列表 =:如 =0,2-4,表示仅匹配指定字节,其余字节忽略 并且实现了索引列表的范围(-)和逗号分隔解析,极大提升了二进制补丁的便捷性 |
| 无全通配掩码检查,设置一个全为 0x00 的掩码会导致永远匹配但毫无意义 |
检测掩码是否全部为 0(全通配),并报错拒绝 |
| 原始代码 |
优化版本 |
DEBUG 宏混在代码中,大量无用原型声明(如 open_outfile)存在 |
彻底清除冗余代码 |
函数定义分散,混杂在大量注释括号 {{ }} 中,风格老旧 |
采用现代 C 风格,static 限制所有内部函数作用域,头文件仅包含必要项,清晰划分“数据结构-前向声明-实现”模块 |
main 中文件打开、参数解析、搜索、puke 的逻辑纠缠在一起 |
将参数解析抽离为 parse_args,原地更新逻辑独立处理,main 仅负责流程调度和清理 |
返回匹配数量使用全局变量 matches |
改为 do_search_replace 返回 long long,同时支持 64 位大文件统计 |
hexdumpline 输出无分隔符,可读性差 |
格式化输出为空格分隔的十六进制对(printf("%s%02x", i?" ":"", byte)) |
2026-8-07 更新:优化了编程提示词,提高了代码质量和性能,消灭了一些潜在bug。增加 32bit.exe
2026-7-30 更新:性能、安全提升。防止全通配掩码替换死循环;64位匹配计数,适合处理GB级大文件。
2026-7-09 更新:新增灵活支持掩码 ?? .. 以及 忽略/匹配索引语法。
2026-6-30 发布:binmay2 修复原版所有bug,优化架构,增强性能。
---
MinGW 编译参数:
gcc -march=x86-64-v3 -std=c17 -O2 -pipe -flto -fstack-protector-strong -fstack-clash-protection -fcf-protection=full -fstrict-flex-arrays=3 -D_FORTIFY_SOURCE=3 -Wall -Wextra -Wpedantic -Wconversion -Wimplicit-fallthrough -Wformat=2 -Wtrampolines -Wl,--nxcompat,--dynamicbase,--high-entropy-va,--gc-sections -ffunction-sections -static -s %*
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于 2天前
被真难取编辑
,原因: