程序安全分析算是一个相对成熟的领域,无论是学术界还是工业界都有很多非常有价值的工作和成果。作为一名刚入门的萌新,由于各种大中小型分析平台、框架显得眼花缭乱,因此,想以此文记录一下部分涉及安全领域的分析平台的原理。作者水平有限,难免有疏漏,如果您有其他更好的想法,期待与您交流。
中间语言
绝大多数程序安全分析平台有一个底层共性,那就是中间语言(Intermediate Representation)。这个概念源自编译器,将源代码编译为目标代码/机器语言的过程中,会先将源代码转换为一个或多个的中间表述,这样有利于编译器做优化、分析。而在二进制分析的过程中,平台往往需要支持多种多样的架构,比如 x86, ARM, MIPS 以及一些小众的架构。为了起到重复使用分析代码的效果,他们通过 IR 来达到这个目的。
可以注意到上图这个方向也有反过来的时候,特别是涉及到二进制安全领域时,相反的过程有助于对闭源目标代码做程序分析,例如安全领域的常见方向:控制流/数据流分析,符号执行,污点追踪等。
下面举几个应用了这种思想的例子:llvm,qemu,vex 等。以他们为基础,又延伸出了其他很多有价值的工作。
LLVM
llvm 是一个范围很广的项目集合统称,包含一系列模块化的编译器组件和工具链(llvm IR,debugger,c++ library)。原理如下图:
clang 作为前端接收 c/c++/Objective-c 源代码,将接收到的源代码转化成 llvm IR 中间语言,之后再转化成可运行的目标语言则是 llvm 后端的工作。在前端到中间语言的这个过程中,可以做很多有意思的程序分析工作。
应用举例
在前端层面,例如 clang static analysis,使用 clang 针对源代码做静态分析。静态分析是程序分析的另一个大坑,这里不赘述。这里列举两个值得参考的链接:
在 IR 的加持上,llvm 提供了各种功能的 Pass,例如应用在 libfuzzer 上SanitizerCoverage 。这是一个 llvm 自带的统计代码覆盖率的插桩方法。
Libfuzzer
libfuzzer 是一个 coverage-based fuzzer,想要获得程序的覆盖率信息,就必须利用某种技术来获取程序的执行信息,可从硬件层面实现,如 Intel pt ;也可从 IR 层面实现插桩,即在程序中插入一些小代码段,来实现一些搜集程序执行信息、改变程序行为的功能。插桩是程序分析一个重要的技术,llvm 自带了插桩功能的库,即 llvm pass framework,也是体现 llvm 的强大之处之一。
llvm pass 提供了各种功能的“插桩”方式:ModulePass , CallGraphSCCPass, FunctionPass , LoopPass, RegionPass。其中 SanitizerCoverage 使用的是 ModulePass,原理是在程序控制流图的每条边都加上 __sanitizer_cov_trace_pc_guard 这么一个小函数来记录触发次数。
Klee
klee 是一个符号执行平台。使用符号执行分析一个程序时,该程序会将输入符号化,而非一般执行程序时使用的具体值。在某处目标代码时,分析器可以得到相应的路径约束,然后通过约束求解器来得到可以触发目标代码的具体值。简单地说,就是一种设变量解方程的思想,来求解为了达到某处代码所需要的输入值。
klee 接受的是 llvm IR 并直接分析 IR 进行符号执行。当然 klee 的实现原理除了基于 llvm IR 这一点,还有很多其他细节,例如路径选择、状态调度等,感兴趣可参考 klee 的官网及其论文列表:http://klee.github.io/
以上是“ llvm 系”的一点记录,特点就是以从源代码到 IR 的方向做程序分析。下面从 qemu 开始,介绍相反方向的一些分析平台。
Qemu
Qemu 是一个功能齐全的虚拟机,可执行全系统仿真和用户模式仿真,包含各种架构和硬件设备的模拟执行和虚拟化。这里只提他的一部分功能,即利用了 IR 思想,涉及到指令执行的 TCG 模块。如下图。
原理也很容易理解,为了模拟执行已经编译好的某种架构的程序,qemu 把目标代码转化为IR中间语言,再把 IR 翻译为本机架构可执行的机器语言,实现跨架构模拟执行。与上述 llvm 一样,在这个翻译的过程中,又可以做很多有意思的程序分析工作;而且与 llvm 是互补的关系,llvm 需要从源码翻译为 IR ,而 qemu 多用于没有源码的二进制程序分析。
另外一点不同在于,clang 涉及的是前半部分,从源代码翻译到IR的过程,因此它的使用方式往往只需要用抽象的高级语言,因为无论 llvm 前端对程序做了多复杂的变化,对后端来说只需要照常把IR翻译成机器语言即可;而使用 qemu 分析二进制程序,例如做类似插桩的工作时,失去了高级语言的抽象表达,则不得不与更底层的部分打交道,即 CPU、寄存器、虚拟内存等。
所以基于 qemu 可以做怎样的二进制分析工作呢,以下举几个例子:
应用举例
Bitblaze
Bitblaze 是较早的二进制程序安全分析项目,很有代表性。其中的 temu 是其基于 qemu 的动态分析工具,主打作污点分析。由于年代较为久远,这里不过多叙述,可参考:http://bitblaze.cs.berkeley.edu/
Unicorn
Unicorn 是相对较新的也更易用、更友好的二进制仿真执行框架。qemu 提供的是一整个完整的工具,目的是为了给人直接拿来用,实现速度更快功能更健全的仿真执行;而 Unicorn 作为一个框架,可以显示出程序模拟执行过程中的各种 API(例如单步执行、访问/修改寄存器/内存),是为了方便开发者实现自己的程序分析功能。
不过 Unicorn 和 qemu 不是一个量级的平台,Unicorn 只是基于上述提到的 qemu tcg 模块的一小部分功能,实现模拟 CPU 执行程序的过程。具体原理是,Unicorn 只保留了 qemu 的 tcg 动态翻译、执行部分,将其他的功能通通删掉(系统调用、硬件虚拟化等),单纯地实现 CPU 模拟执行,并且在执行过程中增加了分析接口,即可中途方便地访问某一个寄存器/内存,对某个地址或指令做 hook 等,且提供各种语言的 bindings 方便调用。
Unicorn在安全领域学术界有不少应用,可参考:https://github.com/unicorn-engine/unicorn/wiki/Papers-use-Unicorn-emulator
Usercorn/Qiling
这两个框架原理类似,都是基于 Unicorn 开发的更完善的分析框架。Unicorn 相当于一个 CPU 模拟器,而 qiling framework 相当于把计算机除了 CPU 剩下的部分都实现了一遍,例如二进制加载、系统调用、文件系统等模块的模拟。最终实现跨平台、跨架构地执行、分析程序。
其他有意思的应用可参考:
除了上述 qemu、llvm 的应用,还有不少别的类似的工作:
IDA microcode
上述 qemu TCG 模块实现了从机器语言翻译为中间语言实现模拟执行、二进制分析的功能;从机器语言翻译到源代码这一过程,即反编译,以著名的 IDA Pro 为代表,也是利用中间语言这一思想实现的。使用中间语言来做反编译的好处也很明显,可以摆脱复杂的设计不同处理器的指令,使得反编译可移植、适配性更广。
IDA 自身的 IR 为 microcode,在 IDA 7.1 的时候开源。开源之前 IDA 一直在对这套 IR 做完善和优化,开源之后提供了相应的 API,更方便使用者利用 microcode 来开发插件做反编译的分析工作,例如应对花指令、混淆之类的情况。可参考:
其他反编译相关技术可参考这篇论文:https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_andriesse.pdf
BAP
CMU 开发的二进制分析框架 BinaryAnalysisPlatform,使用的是相对小众 OCaml 实现,CGC 里的冠军 Mayhem 便是基于此实现的。BAP 的原理也是通过 IR 来作二进制分析,可参考:
https://github.com/BinaryAnalysisPlatform/bap
VEX IR
又是一套中间语言。使用它的是 Valgrind 插桩框架工具;上面提到的 llvm/qemu 其实本身并不是以安全分析为出发点的平台,只是因为他们过于完善和强大,所以有很多基于他们的改进工作来做程序安全分析。而 Valgrind 则是以安全为出发点开发的插桩框架,也相对成熟流行;同类的产品还有 pin、DynamoRIO、dyninst ,这里篇幅有限,就不再详加赘述了。
组合应用
Fuzzing
上面提到了 libfuzzer,难免要提 AFL 。AFL 主要分 llvm_mode 和 qemu_mode,llvm_mode ,原理类似 libfuzzer,只不过使用的是自行开发的 llvm pass 模块获取覆盖率,而不是 llvm 自带的 SanitizerCoverage ;qemu_mode 针对无源码的二进制程序,patch 了 qemu 的 TCG 代码,在翻译 TCG IR 时做插桩来获取覆盖率。
更多 AFL 的变种与应用,例如结合 pin、DynamoRIO、klee 等,可参考:https://github.com/google/fuzzing/blob/master/docs/afl-based-fuzzers-overview.md
Symbolic Execution
S2E
S2E 是另一个有意思的组合应用例子,与 klee 一样它是一个符号执行工具,不一样的地方在于,klee 基于 llvm IR 进行符号执行,因此需要使用 llvm 从源代码翻译到IR的这一过程。而 s2e 可直接在二进制程序上进行符号执行,实现原理组合了 qemu 和 klee 的功能,即修改 qemu 的解析翻译二进制代码部分,实现将二进制代码翻译为 llvm IR 这一过程,如下图:
如此便可移植 klee 针对 llvm IR 的符号执行引擎,同时结合 qemu 对二进制代码的具体执行(concrete exection),是一个挺完善的安全分析框架,在 CGC 上应用很多。下图是其架构图,可参考官网及其论文列表:https://s2e.systems/
Angr
Angr 是较新的符号执行框架。符号执行时使用的是上述提到的 VEX IR,类似 klee 针对 llvm IR 做符号执行,angr 使用 simuvex 对 VEX IR 做符号执行。
Angr 也集成了 unicorn 执行引擎,可选用来做不带符号化的具体执行(concrete exection)。Angr 是一个非常模块化的工具,除了上述用来执行二进制的部分,其他部分功能也是各个独立模块的集合,相对易上手,例如 CLE,Claripy 等。
Others: Real world application
最近读到的一篇阿里科研组发的 ccs19 的论文:Different is Good: Detecting the Use of Uninitialized Variables through Differential Replay。
论文使用了基于 qemu 的 PANDA 及其 record/replay 功能,分析未初始化变量;加上 simuvex 转化为 VEX IR 做符号执行,找到了 windows kernel 的不少 cve,可能是应用得比较好的例子。
总结
基于IR这种简单的思想,可发展出很多分析技术,也开发出了不少值得学习的平台。这次总结主要解决了我对它们在实现原理及区别方面的困惑。当然以上只是很浅显的介绍,略过了巨量技术细节,希望能在理解工作原理和参考学习路线上起到一点帮助。
星阑科技各大岗位热招中,福利多多,礼品多多,工资不设上限!大家可以关注公众号,点击菜单栏右下角 “加入我们”,直达招聘栏目,为自己喜爱的岗位奋力一搏!
星阑科技微信公众号北京星阑科技有限公司(简称星阑科技)是一家以安全技术为核心、AI技术为驱动的网络安全科技公司,致力于提供高级攻防服务和智能化网络安全解决方案,以应对政府、企业所面临的日益严峻的网络安全威胁,让网络空间更加安全与智慧。
目前,星阑科技提供攻防对抗、APT防御、高级渗透等安全服务,为客户全面梳理威胁矩阵、进行安全赋能。产品包括攻击诱捕系统、邮件攻击一体化系统、基于前沿的图神经网络的智能边界防护引擎以及AI自动化漏洞挖掘系统,能全面提升客户的安全防护能力并有效降低安全运维成本。
相关内容:
招聘 | 入住某司1500W豪宅的真相竟然是这样....(AirPods等你拿!)mp.weixin.qq.com 渗透测试笔记:从一个app开始的渗透测试之路mp.weixin.qq.com PHP 开源白盒审计工具初探(上)mp.weixin.qq.com PHP 开源白盒审计工具初探(下)mp.weixin.qq.com 模糊测试初探 | ProtoFuzz vs MySql Xmp.weixin.qq.com