源码二进制比对工具Pigaios

Pigaios原理简介#

目的: 为了解决符号表问题

两种方案:

  1. 编译源码,使用 Diaphora (or BinDiff) 导出符号表,导入符号表缺失样本
  2. 从源码题符号表,导入符号表缺失样本

第二种方法好处在于不需要完全编译,因为对于旧的源码,有的很难编译成功

工作流程#

  • 解析源代码,并从每个函数的抽象语法树(AST)中提取信息。
  • IDA导入从源代码提取的包含源码信息的数据库。
  • 查找在C源代码和IDA数据库中找到的函数匹配。
  • 找到没有误报的初始匹配项后,从调用图中找到更多匹配项。
  • 使用“专家系统”和“基于机器学习”的系统进行评分。
  • 同样,将给定代码库的所有结构和枚举导入IDA数据库(这在IDA中并不简单)。

解析函数#

二进制:通过IDA

源码:通过CLang,提取

  • 字符串常量
  • 循环次数、条件判断、函数调用、全局变量
  • switch信息
  • 函数调用信息
  • 是否为递归函数

特征匹配#

常规方法#

完全匹配#

首先是百分百匹配:为了达到零误报的目的

image-20200501145915448

作者实验结果为在4817个函数中找到了90完全匹配,虽然数目少,但胜在百分百确认,没有误报,不会对分析造成误导。

启发式方法#

为了找到更多的匹配,就需要一些启发式信息了

启发式:

  1. 调用图
  2. 临近函数
  3. 特殊的callee
  4. 少见的常量特征

机器学习方法#

多分类,使用以下算法

image-20200501150640570

综合考量#

当常规算法和ML结果都表现很好时,就说明匹配度very very good,两者可相互印证

Pigaios使用指北#

Pigaios是一款源码二进制比对工具,在实际二进制样本分析中,带符号表的样本极其少见,这个时候工具的作用就显现出来了。

安装#

在提取源码特征的机器(例如ubuntu)上安装依赖如下:

1
$ apt-get install clang python-clang-5.0 libclang-5.0-dev python-colorama python-sklearn

在装有IDA的机子上也同时安装以下:

1
$ pip install clang-5 colorama scikit-learn

源码特征提取过程#

下以https://zlib.net/zlib-1.2.11.tar.gz为例

生成项目信息文件#

在源码根目录下

1
$ python /path/to/pigaios/srcbindiff.py -create

默认生成文件sbd.project,内容为CXX,LLVM等配置信息,以及源码信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
####################################################
# Default Source-Binary-Differ project configuration
####################################################
[GENERAL]
includes = /usr/lib/llvm-3.8/bin/../lib/clang/3.8.0/include
inlines = 0

[PROJECT]
cflags = -I. -I./include
cxxflags = -I. -I./include
export-file = zlib-1.2.11.sqlite
export-header = zlib-1.2.11-exported.h
export-indent = clang-format -i

[FILES]
contrib/masmx64/inffas8664.c = 1
contrib/iostream/test.cpp = 1
contrib/iostream/zfstream.cpp = 1
contrib/testzlib/testzlib.c = 1
contrib/minizip/minizip.c = 1
contrib/minizip/ioapi.c = 1
contrib/minizip/iowin32.c = 1
contrib/minizip/zip.c = 1
contrib/minizip/miniunz.c = 1
contrib/minizip/mztools.c = 1
contrib/minizip/unzip.c = 1
contrib/iostream2/zstream_test.cpp = 1
contrib/inflate86/inffas86.c = 1
contrib/infback9/infback9.c = 1
contrib/infback9/inftree9.c = 1
contrib/puff/pufftest.c = 1
contrib/puff/puff.c = 1
contrib/untgz/untgz.c = 1
contrib/blast/blast.c = 1
contrib/iostream3/test.cc = 1
contrib/iostream3/zfstream.cc = 1
examples/gzappend.c = 1
examples/gun.c = 1
examples/zpipe.c = 1
examples/gzjoin.c = 1
examples/fitblk.c = 1
examples/zran.c = 1
examples/gzlog.c = 1
examples/enough.c = 1
test/minigzip.c = 1
test/infcover.c = 1
test/example.c = 1
inftrees.c = 1
gzlib.c = 1
inflate.c = 1
adler32.c = 1
gzclose.c = 1
deflate.c = 1
infback.c = 1
crc32.c = 1
zutil.c = 1
inffast.c = 1
trees.c = 1
uncompr.c = 1
gzread.c = 1
compress.c = 1
gzwrite.c = 1

由于实际上exampletest等目录包含的是测试样例,可以选择去除对这些文件的分析

提取源码特征#

1
$ python /path/to/pigaios/srcbindiff.py -export

结果存储在.sqlite文件中,在处理过程中会有库文件缺失,以及一些其他的error和fetal,但是没关系,Pigaios都会处理

可以简要查看一下存了哪些信息,如下:

image-20200501141010098

包括调用图信息,常量信息,函数信息,源文件信息

调用图信息

image-20200501141115387

常量信息

image-20200501141136179

都是字符串常量

函数信息

巨长,这里以tsv格式给出第94行和第127行两条记录,具体字段如下,提取了条件信息,switchcase信息,调用信息等

image-20200501141728005
1
2
94		44176	inflate	__int64 __fastcall()		289		21	["invalid block type", "incorrect data check", "too many length or distance symbols", "invalid stored block lengths", "invalid literal/length code", "unknown compression method", "incorrect header check", "header crc mismatch", "invalid distances set", "invalid code lengths set", 595056260442243601, "(I", "unknown header flags set", "invalid window size", "invalid distance too far back", "invalid distance code", "incorrect length check", "invalid literal/lengths set", "invalid bit length repeat", "invalid code -- missing end-of-block", "`\u0007"]	1	1	[[31, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]]]	6	22	{".crc32": 12, ".adler32": 4, "inflate_table": 3, ".memcpy": 5, ".__stack_chk_fail": 1, "inflate_fast": 1}	1	1	0		["incorrect", "header"]
127 69776 gz_open __int64 __fastcall(char *file, int fd) __int64 __fastcall gz_open(char *file, int fd) 21 4 [18446744073709027328, 524481, "%s", 524353] 1 1 [[78, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77]]] 6 2 {".open": 1, ".lseek64": 2, ".malloc": 2, ".snprintf": 1, ".strlen": 1, ".free": 3} 1 0 0

源文件信息

不知道为啥是空的

源码二进制比对#

打开编译好并stripped掉的libz.so.1.2.11,运行pigiaos脚本sourceimp_ids.py

image-20200501142404896

可能因为IDA版本原因,需要在Indent command中去掉-kr选项,匹配结果如下

Diff pseudo-code

是将二进制函数反编译并和源码比较,由于颜色原因看不清,可能是因为我的IDA用了黑色主题。

左侧为二进制函数F5结果,右侧为源码函数。

image-20200501142837530

show match reason

显示匹配原因

image-20200501142941360

import all functions

不是很好用,有错误导入不进来,应该是IDA版本原因

image-20200501143157961

注意#

值得一提的是,从效果来看,strip对匹配结果影响很大,当未stripped时,可以获得31条匹配结果

image-20200501143719326

而当stripped时,只有18个匹配结果

image-20200501142607993

但前面也提到,Pigaios的主要目的是低误报率,避免误导分析人员,所以也是一个不错的工具

参考#

评论