Magnifier:交互式反编译的实验
反编译在Trail of Bits的实践
Trail of Bits正在开发多个与程序反编译相关的开源项目:Remill、Anvill、Rellic以及现在的Magnifier。Trail of Bits的反编译策略是通过多层中间表示(IR)逐步提升编译程序;Remill、Anvill和Rellic协同工作实现这一目标。这种多阶段方法有助于将问题分解为更小的组件:
- Remill将机器指令表示为LLVM IR
- Anvill将机器代码函数转换为LLVM函数
- Rellic通过Clang AST将LLVM IR转换为C代码
理论上,程序可以在任何流水线阶段进行转换,Magnifier验证了这一理论。使用Magnifier,研究人员可以交互式地转换Anvill的LLVM IR,并即时查看Rellic生成的C代码。
从REPL开始
Magnifier最初是一个命令行读取-求值-打印循环(REPL),允许用户使用简洁的命令执行各种LLVM IR转换。这些转换包括:
- 使用LLVM进行函数优化
- 函数内联
- 带/不带常量折叠的值替换
- 函数指针去虚拟化
Magnifier的第一个目标是描述被转换的目标;根据转换类型,这些目标可以是指令、函数或其他对象。为了统一描述这些目标并隐藏某些实现细节,Magnifier为所有函数、函数参数、基本块和IR指令分配了唯一的不透明ID。
Magnifier的下一个重要目标是跟踪转换过程中的指令来源,并了解操作如何影响指令。为此,它引入了额外的源ID(对于未修改的函数,源ID与当前ID相同)。然后在每次转换期间,创建一个新函数来传播源ID,但生成新的唯一当前ID。此解决方案确保没有函数被原地修改,便于在跟踪来源的同时进行转换前后的比较。
最后,对于值替换等转换,Magnifier能够以常量折叠的形式执行额外转换。这些额外转换通常是可取的。为适应不同用例,Magnifier以通用替换接口的形式提供对每个转换的精细控制。该接口允许用户监控所有转换,并根据需要选择允许、拒绝或修改替换步骤。
MagnifierUI:更直观的界面
虽然共享库加REPL的组合是一个简单灵活的解决方案,但对于只想将Magnifier作为反编译二进制文件工具的研究人员来说,这并不是最理想的设置。这就是MagnifierUI的用武之地。
MagnifierUI包含Vue.js前端和C++后端,并使用多会话WebSocket促进两者之间的通信。MagnifierUI不仅暴露了Magnifier提供的大部分功能,还集成了LLVM IR到C代码的反编译器Rellic,以显示并排的C代码反编译结果。
与使用REPL相比,MagnifierUI更加可视化和直观。特别是并排视图和指令高亮显示使代码阅读更加容易。
利用LLVM优化捕获标志
如上简要演示,我们可以以各种方式利用LLVM库,包括其花哨的IR优化来简化代码。这个例子展示了Magnifier在简化逆向工程过程和使研究人员生活更轻松方面的巨大潜力。通过利用LLVM优化背后的所有工程智慧,Magnifier甚至可以将包含循环和条件语句的相对复杂函数(如"fibIter")简化为常量。
展望Magnifier的未来
我希望这篇博文能够从高层次上阐明Trail of Bits如何应对程序反编译挑战,并展示交互式编译器通过Magnifier项目可以实现的目标。
Magnifier当然需要额外的工作,从添加转换类型支持(希望最终能表达完整的补丁集)到将MagnifierUI与Anvill等工具集成以直接摄取二进制文件。尽管如此,我为自己在项目中取得的成就感到非常自豪,并期待Magnifier的未来发展。