BAP——一个二进制程序分析平台
BAP主页
BAP是一个编写程序分析工具的框架,它的特点和优势如下:
- 针对二进制程序
- 用Ocaml编写,提供了C, Python, Rust等语言接口
- 工作流程为 二进制程序->汇编指令->BIL/BIR中间表达->利用BAP插件或接口进行分析
- BIL语法经过正式定义,清晰明确
- 插件的编写和安装十分简单
- 提供对于汇编语言标志位的分析
BAP安装
安装BAP
从deb包安装
wget https://github.com/BinaryAnalysisPlatform/bap/releases/download/v1.3.0/{bap,libbap,libbap-dev}_1.3.0.deb
sudo dpkg -i {bap,libbap,libbap-dev}_1.3.0.deb
从源码安装
1.挑一个网络畅通,网速较快的日子
2.安装OPAM
sudo apt-get install opam
3.安装BAP
opam init --comp=4.03 # install the compiler
opam repo add bap git://github.com/BinaryAnalysisPlatform/opam-repository
eval `opam config env` # activate opam environment
opam depext --install bap # install bap
编译器版本为4.03.0
4.在.bashrc中添加‘bap’命令
alias bap='/path/to/opam/4.03.0/bin/bap'
此处/path/to/opam代指opam所在路径
安装BAP python bindings
sudo pip install bap
BAP使用
Shell命令
BAP命令格式为:bap FILE OPTION
其中FILE为二进制文件,OPTION有以下选项,用法可通过bap --help
查看,例如:
-d,以特定格式将工程打印到目标文件中,BAP能够将工程打印为调用图,控制流图,BIL,BIR等格式,具体支持哪些格式可用命令bap --list-formats
查看,例如将二进制文件test转换为BIL形式打印到test.il中的命令为bap test -d bil:test.il
BAP中使用插件的命令格式为:bap PLUGIN_OPTION FILE
插件的编写及安装
BAP的插件需用Ocaml语言编写,编写和安装步骤如下:
- 创建一个新文件夹(两个插件不能放在同一目录下)
- 在此目录下创建一个ml文件,如test.ml,利用BAP提供的库函数,编写我们想要实现的功能
- 在此目录下执行bapbuild test.plugin
- 在此目录下执行bapbundle install test.plugin
- 然后即可在任意目录下使用插件
现有插件的使用
- objdump:
bap --symbolizer=objdump file -d format:output_file
利用objdump获得二进制程序的符号表
Python接口
利用python接口获取程序信息
bap.run()
def run(path, args=[], bap='bap', parser=adt_project_parser):
此函数的作用是运行bap,其中参数args为插件参数,参数parser为我们希望得到的中间表达的形式,例如bir,bil,cfg等,而函数的返回值为字符串格式的转换结果。
例如,我们可以通过以下代码实现shell中的dump功能:
>>> import bap
>>> proj = bap.run('file', parser={'format':'expected format'})
>>> print proj
其中expected format即为输出格式(bir, bil, cfg等)
bir.loads()
以默认参数执行bap.run()函数会将待分析的二进制文件转换为bap的中间表达(bir)形式,而bir形式的代码会以adt(algebraic data type)格式输出。
例如对于汇编指令subq $0x10, %rsp
,转换后的bir如下所示:
而将bir以adt格式输出,结果如下所示:
bap的python接口就是对adt格式的bir进行处理,从而得到程序信息的。
从图中可以看出,adt格式的bir是一个由元组组成的层级结构,每一层都可以看作一个元组,bap的python库定义了一个ADT类,将所有这些元组视为ADT类的实例。bir.loads()的作用即为将bap.run()得到的字符串格式的bir.adt转换为这样的层级元组格式进行存储。
bir的层级结构
图中每个非叶结点表示以结点名为类名的类的一个实例。
具体到每个类,类Project代表整个用bir表示的二进制程序,它往下一层分为attrs,sections,memmap,program四个部分,attrs是一个属性元组,存储整个程序的一些属性;sections是一个段元组,存储二进制程序段(例如elf文件中的.text, .init, .fini等)的一些信息;memmap是存储地址与程序段映射的元组;program为bir程序元组,存储了由二进制程序转换的所有bir语句,这些bir语句又被分为sub和blk两个层级。
bir中定义的所有类都是ADT的子类,BAP的python接口就是利用特定的关键字加上类名构造出了接口函数的函数名,再利用接口函数实现我们所需要的功能,函数体由用户自己编写。
Visitor.run()
BAP中定义了Visitor类来对以上ADT结构进行访问。以以下代码为例:
import bap
from bap.adt import Visitor
class Counter(Visitor) :
def __init__(self):
self.jmps = 0
self.total = 0
def enter_Jmp(self,jmp):
self.jmps += 1
def enter_Term(self,t):
self.total += 1
proj = bap.run('/bin/true')
count = Counter()
count.run(proj.program)
print("ratio = {0}/{1} = {2}".format(count.jmps, count.total,
count.jmps/float(count.total)))
上面代码实现了求跳转指令占函数中所有指令百分比的功能,其中用户定义的类Counter继承了类Visitor,函数enter_Jmp()和enter_Term()是按照Visitor中给出的函数构造规则构造出的函数,Visitor.run()函数能够遍历整个bir程序中的所有ADT,当函数访问到类Jmp时就调用enter_Jmp()函数,实现用户定义的功能,函数访问到类Term(一个表示bir语句的ADT)时也是如此。
简而言之,BAP没有提供明确的接口API,然而为我们抽取程序信息提供了相当大的灵活性,还有些功能有待发掘,需要细读BAP python bindings的源码。