标题: BinDiff二进制比较简介 创建: 2021-09-13 13:35 更新: 2022-04-16 11:09 链接: https://scz.617.cn/misc/202109131335.txt -------------------------------------------------------------------------- 目录: ☆ 背景介绍 ☆ 环境准备 ☆ 符号迁移 ☆ 寻找匹配代码逻辑 -------------------------------------------------------------------------- ☆ 背景介绍 分析过old.so,做了大量繁琐的逆向工程,比如自定义了很多数据结构、重命名了很 多函数。现在有new.so,想充分利用old.so的已有逆向成果。比如,想从old.i64迁 移一批名为"Private_*"的函数名到new.i64中。再比如,已经卸掉old.so中的过期时 间检查,想快速定位new.so中匹配代码,对之进行静态Patch。二进制比较工具正是 为解决此类需求而生的。有相当大的应用场景是补丁分析,以此研究某个未公开细节 漏洞的技术原理。 网上有很多BinDiff的使用介绍,本文没有新意。最近重新用到它,好久不用,手有 些生,做点笔记。 ☆ 环境准备 以Win10+IDA 7.6.1+BinDiff 7为例。 -------------------------------------------------------------------------- https://www.zynamics.com/bindiff.html https://www.zynamics.com/bindiff/manual/index.html (在线文档) https://www.zynamics.com/software.html https://storage.googleapis.com/bindiff-releases/updated-20210607/bindiff7.msi -------------------------------------------------------------------------- bindiff7.msi缺省安装到 C:\Program Files\BinDiff\ 大多数Windows快捷方式的右键属性中可以看到真实目标程序,但有些快捷方式右键 属性其目标栏是灰掉的,不可编辑,也看不出真实目标程序,比如"BinDiff 7"安装 后的快捷方式就是这样。若对此有好奇心,参看: 《Standard Shortcuts/Advertised Shortcuts》 https://scz.617.cn/windows/202109091619.txt 与BinDiff 4.2不同,BinDiff 7自带JRE环境 $ "C:\Program Files\BinDiff\jre\bin\java.exe" -version openjdk version "16" 2021-03-16 OpenJDK Runtime Environment Zulu16.28+11-CA (build 16+36) OpenJDK 64-Bit Server VM Zulu16.28+11-CA (build 16+36, mixed mode) 启动BinDiff 7,实际执行 "C:\Program Files\BinDiff\jre\bin\javaw.exe" -Xms128m -Xmx48987m -jar "C:\Program Files\BinDiff\bin\bindiff.jar" ☆ 符号迁移 假设已有old.i64、new.i64,上述GUI可以直接对二者进行比较,生成比较结果。该 过程不再依赖IDA,但还得用IDA打开保存后的比较结果,以进行有效查看。可以用上 述GUI生成比较结果,也可以直接在IDA中用BinDiff插件生成比较结果,显然更推荐 后一种用法。 为了在IDA中加载前述比较结果,需要向IDA安装相应BinDiff插件。我的IDA是绿色处 理过的,注册表里没相应项,安装bindiff7.msi时不会自动向IDA目录复制BinDiff插 件。 $ dir /B "C:\Program Files\BinDiff\Plugins\IDA Pro" bindiff7_ida.dll bindiff7_ida64.dll binexport12_ida.dll binexport12_ida64.dll 将这四个插件复制到 X:\Green\IDA\plugins\ 这是两组插件,前两个是BinDiff插件。 参看 《IoT设备逆向工程中的函数识别》 https://scz.617.cn/misc/201905081756.txt 上文介绍的技术主要用于识别库函数,本文原始需求有所不同。假设old.i64中含有 大量重命名过的函数,new.i64对应一个较新版本二进制,现在想充分利用old.i64中 已有信息,利用二进制比较技术进行函数识别、符号迁移。07到09年间,我们部门用 的是hume开发的nsdiff,现在我只能用BinDiff了。关于BinDiff 4.2的使用,参看: 《浅析Nessus、Nmap针对MS17-010的远程精确扫描方案》 https://scz.617.cn/windows/201706221521.txt BinDiff 7与BinDiff 4.2差别不大。先用IDA打开new.i64 Edit->Plugins->BinDiff (Ctrl-6)->Diff Database->选中old.i64 BinDiff这个功能不支持中文路径,放在无空格英文路径里保险些。 比较结束后会打开四个窗口 -------------------------------------------------------------------------- Matched Functions 在new.i64、old.i64中相匹配的函数 Statistics Primary Unmatched 在new.i64中存在,但在old.i64中未找到匹配 Secondary Unmatched 在old.i64中存在,但在new.i64中未找到匹配 -------------------------------------------------------------------------- 我一般将它们全关掉,然后在适当的子区域打开想查看的结果窗口。绝大多数情况下, 只需要关心"Matched Functions"。 View->BinDiff->Matched functions -------------------------------------------------------------------------- 单行颜色 越绿表示相似度最高,越红表示相似度越低 Similarity [0,1]区间的值,指明new与old的相似度。1表示完全一样。 缺省对Similarity降序排列显示 Confidence [0,1]区间的值,指明Similarity的可信度,值越接近1表示Similarity可信度越 高。Confidence实际代表了识别算法的强弱。 高Similarity、低Confidence表示一个弱算法找到一个强匹配 低Similarity、高Confidence表示一个强算法找到一个弱匹配 显然,强算法找到的强匹配是最佳匹配 Change "-------"表示new与old相比没有变化,其他可取值见下 G Graph there have been structural changes in the function. Either the number of basic blocks or the number of edges differs or unmatched edges exist. I Instruction either the number of instructions differs or at least one mnemonic has changed. O Operand not yet implemented J Jump indicates a branch inversion. E Entrypoint the entry point basic blocks have not been matched or are different. L Loop the number of loops has changed. C Call at least one of the call targets hasn't been matched. EA Primary / Name Primary new.i64中地址、函数名 EA Secondary / Name Secondary old.i64中地址、函数名 Algorithm 识别算法 -------------------------------------------------------------------------- 假设old.i64中有大量重命名为"Private_*"的函数,对"Name Secondary"列排序后可 以集中看到它们。 "Matched Functions"窗口右键菜单->Modify filters (Ctrl-Shift-F) 可以设置多种过滤规则,比如"Name Secondary"以"Private"开头、大小写敏感。之 后批量选中待迁移函数所在行,在右键菜单中选"Import symbols/comments",将从 old.i64迁移函数名、注释到new.i64。 另有"Import symbols/comments as external library",区别是迁移过来的符号被 当作库函数,在反汇编窗口显示的颜色不同。一般没啥用。 为了避免误导,只从old.i64迁移那些绿色、高相似度、高可信度的函数名到new.i64。 可以在过滤之后对Change列排序,优先迁移Change列显示为"-------"的函数名。 用BinDiff迁移符号的要点是,先打开new,再比较old,最后右键Import符号。从前 hume的nsdiff可以双向迁移符号,所以一直习惯性先打开old,但这个习惯会导致 BinDiff迁移符号失败。 ☆ 寻找匹配代码逻辑 假设已记录了old.i64中的虚拟地址,对应某个感兴趣的代码逻辑,现在想快速找到 new.i64中相应虚拟地址。 假设old.i64中有如下代码片段,现在想找出new.i64中对应点。 -------------------------------------------------------------------------- 00007FFFED29922C mov rax, cs:off_7FFFED4ABA20 00007FFFED299233 mov rbx, [rax+rdx*8] 00007FFFED299237 mov rax, cs:off_7FFFED4ABA30 -------------------------------------------------------------------------- 用IDA打开old.i64,检查0x7fffed29922c所在函数名,假设为Private_func_a()。关 闭old.i64,否则后面的BinDiff操作无法继续。 用IDA打开new.i64,与old.i64比较。在BinDiff的"Matched Functions"中找到 Private_func_a()所在行,右键"View flow graphs"。 参看"BinDiff Manual"的"Function Flowgraphs"及"A basic walk-through Analyzing a Microsoft Patch"小节 -------------------------------------------------------------------------- BinDiff displays functions from IDA as highlighted flowgraphs with colors indicating special properties of edges and code blocks. Green arrows represent conditional branches that need to satisfy a certain condition to be taken Red arrows represent conditional branches if the branch condition is not met as well as unconditional branches. Green nodes indicate basic blocks that have identical instruction mnemonics in both executables (although operands to individual assembly instructions might have changed) Red nodes indicate basic blocks to which our comparison algorithms were unable to find equivalents. Yellow nodes indicates nodes for which the algorithms could find equivalents, but which had some instructions changed between versions. -------------------------------------------------------------------------- 在secondary中输入7fffed29922c,不要输0x前缀,选中0x7fffed29922c,此时所在 block变色。选中block,右键"Zoom to Basicblock",放大所在block。 点击"Fit Graph Content"可以恢复原大小,或者 Graphs->Fit Graph (Ctrl-Shift-M) 点击"Toogle Proximity Browsing"可以切换显示模式,此时只显示目标块附近的一 些块,缺省显示所有块。对于复杂函数,这种切换有助于聚焦目标块。或者 Mode->Proximity Browsing (F6) 在primary中选中匹配block,右键"Copy Basic Block Address",假设是 0x7fffed08d4a8。在new.i64中查看0x7fffed08d4a8附近代码 -------------------------------------------------------------------------- 00007FFFED08D4A8 mov rax, cs:off_7FFFED2AA3E8 00007FFFED08D4AF mov rbx, [rax+rdx*8] 00007FFFED08D4B3 mov rax, cs:off_7FFFED2AA3F8 -------------------------------------------------------------------------- 上例0x7fffed29922c正好位于block边界上,若不在block边界上呢? 假设old.i64中某处检查时间的代码逻辑位于Private_func_b()中 -------------------------------------------------------------------------- 00007FFFED3383B1 mov rdx, [rcx+200h] 00007FFFED3383B8 add rdx, 86400 00007FFFED3383BF cmp rdx, rax -------------------------------------------------------------------------- BinDiff中图形化比较Private_func_b() Search->Jump to Secondary Address (Ctrl-Shift-J) 输入7fffed3383b1,不要输0x前缀。然后在primary中查看匹配block,与 0x7fffed3383b1对应的地址是0x7fffed130b20,new.i64中匹配代码是 -------------------------------------------------------------------------- 00007FFFED130B20 mov rdx, [rcx+200h] 00007FFFED130B27 add rdx, 86400 00007FFFED130B2E cmp rdx, rax --------------------------------------------------------------------------