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的源码。

版权声明:本文为m0_37924639原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_37924639/article/details/78790046