libdft64 简介

libdft64 一款基于 Intel Pin 开发的动态污点分析工具。

1. 污点分析

污点分析(Taint Analysis)是一种跟踪并分析污点信息(通常指程序接受的外部输入)在程序中流动的技术。污点分析过程通常包括三个步骤:

  1. 识别并标记污点源(Source)
  2. 跟踪污点在程序中的传播(propagation)
  3. 在关键的程序点(Sink)检测是否受到污点的影响

动态污点分析(Dynamic Taint Analysis, DTA)是一种用于确定在程序运行时,运行环境中哪些寄存器和内存区域,可以由外部输入来进行控制的技术。

动态污点分析与静态污点分析的主要区别在于,静态污点分析是模拟程序运行,而动态污点分析需要实际运行程序。动态污点分析的优缺点为:

  • 优点:误报率较低,检测结果的可信度较高。
  • 缺点:
    • 漏报率较高:由程序动态运行时的代码覆盖率决定
    • 平台相关性较高:特定的动态污点分析工具只能够解决在特定平台上运行的程序
    • 资源消耗大:包括空间复杂度和时间复杂度

2. Intel Pin

Intel Pin 是一个动态二进制插桩(Dynamic Binary Instrumentation, DBI)框架,由英特尔公司开发。它允许用户在程序运行时(即不需要源代码)对程序的二进制代码进行插桩和分析。

Intel Pin 提供了丰富的 API 库,供开发者编写和构建自己的 Pintool,用来完成特定的分析任务。

libdft64 安装和使用

1. 安装和构建

libdft64 是一款开源工具,它依赖于 Intel Pin。使用时需要下载源码,安装依赖,执行构建:

1
2
3
4
5
git clone https://github.com/AngoraFuzzer/libdft64.git
cd libdft64
PREFIX=~/pin ./install_pin.sh
export PIN_ROOT=~/pin/pin-3.20-98437-gf02b61307-gcc-linux
make

2. 示例解析

(1) 使用演示

libdft64 给出的示例用法如下:

1
2
cd tools
make test_mini

执行结果为:

(2) 源码解读

libdft64 测试对象的源码为mini_test.cpp。在main 函数中,调用一些自定义的函数(__libdft_set_taint, __libdft_get_taint, __libdft_getval_taint),来进行污点数据的设置和获取操作。

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
int main(int argc, char **argv) {
if (argc < 2)
return 0;

FILE *fp;
char buf[255];
size_t ret;

fp = fopen(argv[1], "rb");

if (!fp) {
printf("st err\n");
return 0;
}
int len = 20;
// dfsan_read_label(&(len), sizeof *buf);
ret = fread(buf, sizeof *buf, len, fp);

fclose(fp);
// printf("len is :%d\n", len);
if (ret < len) {
// printf("input fail \n");
return 0;
}

uint64_t m = 0;
__libdft_set_taint(&m, 8);
__libdft_get_taint(&m);
__libdft_getval_taint(m);

uint16_t x = 0;
__libdft_get_taint(&x);
memcpy(&x, buf + 5, 2); // x 1 - 2
__libdft_get_taint(&x);
__libdft_getval_taint(x);

uint64_t y = x + 2;
__libdft_getval_taint(y);

return 0;
}

使用 libdft64 进行污点追踪的代码为 track.cpp。通过 Intel Pin 的 API 函数RTN_FindByName 来定位 mini_test.cpp 中的函数 (__libdft_set_taint, __libdft_get_taint, __libdft_getval_taint),并通过 Intel Pin 的 API 函数RTN_InsertCall 来对这些定位到的函数进行插桩。当运行到目标函数时,会调用相应的 handler 函数 (TestSetHandler, TestGetHandler, TestGetValHandler) 执行额外操作。

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
VOID TestGetHandler(void *p) {
uint64_t v = *((uint64_t *)p);
tag_t t = tagmap_getn((ADDRINT)p, 8);
printf("[PIN][GET] addr: %p, v: %lu, lb: %d, taint: %s\n", p, v, t,
tag_sprint(t).c_str());
}

VOID TestGetValHandler(THREADID tid, uint64_t v) {
tag_t t = tagmap_getn_reg(tid, X64_ARG0_REG, 8);
printf("[PIN][GETVAL] v: %lu, lb: %d, taint: %s\n", v, t,
tag_sprint(t).c_str());
}

VOID TestSetHandler(void *p, unsigned int v) {
tag_t t = tag_alloc<tag_t>(v);
tagmap_setb((ADDRINT)p, t);
printf("[PIN][SET] addr: %p, lb: %d, taint: %d\n", p, t, v);
}

VOID EntryPoint(VOID *v) {

for (IMG img = APP_ImgHead(); IMG_Valid(img); img = IMG_Next(img)) {
RTN test_get_rtn = RTN_FindByName(img, "__libdft_get_taint");
if (RTN_Valid(test_get_rtn)) {
RTN_Open(test_get_rtn);
RTN_InsertCall(test_get_rtn, IPOINT_BEFORE, (AFUNPTR)TestGetHandler,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END);
RTN_Close(test_get_rtn);
}

RTN test_set_rtn = RTN_FindByName(img, "__libdft_set_taint");
if (RTN_Valid(test_set_rtn)) {
RTN_Open(test_set_rtn);
RTN_InsertCall(test_set_rtn, IPOINT_BEFORE, (AFUNPTR)TestSetHandler,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_FUNCARG_ENTRYPOINT_VALUE, 1, IARG_END);
RTN_Close(test_set_rtn);
}

RTN test_getval_rtn = RTN_FindByName(img, "__libdft_getval_taint");
if (RTN_Valid(test_getval_rtn)) {
RTN_Open(test_getval_rtn);

RTN_InsertCall(test_getval_rtn, IPOINT_BEFORE, (AFUNPTR)TestGetValHandler,
IARG_THREAD_ID, IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
RTN_Close(test_getval_rtn);
}
}
}

int main(int argc, char *argv[]) {

PIN_InitSymbols();

if (unlikely(PIN_Init(argc, argv))) {
std::cerr
<< "Sth error in PIN_Init. Plz use the right command line options."
<< std::endl;
return -1;
}

if (unlikely(libdft_init() != 0)) {
std::cerr << "Sth error libdft_init." << std::endl;
return -1;
}

PIN_AddApplicationStartFunction(EntryPoint, 0);

hook_file_syscall();

PIN_StartProgram();

return 0;
}

根据 Intel Pin 的 API 手册RTN_InsertCall 原型为:

1
2
3
4
5
6
7
8
9
10
11
12
VOID RTN_InsertCall (
RTN rtn,
IPOINT action,
AFUNPTR funptr,
...
)

Parameters
rtn: 需要插桩的目标函数
action: IPOINT_BEFORE 表示在目前函数执行之前插入, IPOINT_AFTER 表示在目标函数执行之后返回之前插入
funptr: 需要调用的 handler 函数
...: 传递给 funptr 的可变参数

根据官方示例,不难发现,使用 libdft64 的主要步骤为:定位目标函数 -> 对目标函数进行插桩 -> 在插桩的 handler 函数中实现分析需求。