Note
Search…
intel-pin
懒人玩reverse

intel-pin

之前玩了angr之后发现有点局限性,后来接触了 intel-pin 这个动态插桩工具,当时发现对于一些加密字节关联度不大的题目,尤其是代码混淆比较严重的题目,可以编写 pintool 统计指令数等信息,多快好省的通过侧信道的方法逐位爆破出 flag。

安装

官网下载即可,解压后根目录下pin程序直接可使用。

使用1

在 source/tools/ManualExamples 中有一些现成的 pintool 可以使用,基本涵盖了各个模块的用法。inscount0.cpp是指令计数插桩的功能,inscount1.cpp是基本块计数(条件分支)的插桩功能。
下面就说说指令计数的功能。
make obj-intel64/inscount0.so TARGET=intel64 编译生成64位的pintool make obj-ia32/inscount0.so TARGET=ia32 编译生成32位的pintool
pin -t your_pintool -- your_binary <arg> 使用基本命令 或者喂给输入在后面加 <<< "flag{dsada}"
通常在make前,我们可以改一改fini函数
1
VOID Fini(INT32 code, VOID *v)
2
{
3
// Write to a file since cout and cerr maybe closed by the application
4
// OutFile.setf(ios::showbase);
5
// OutFile << "Count " << icount << endl;
6
// OutFile.close();
7
std::cout<< "Count " << icount << std::endl;
8
9
}
Copied!
注释掉OutFile部分,不让输出文件,直接cout输出结果到命令行。

使用2

对于基本块这种情况,有些时候在程序运行过程中,我们只关心程序本身执行的基本块的个数,并不关心在外部动态链接库中的执行,因此需要使用IMG_AddInstrumentFunction记录程序镜像开始和结束地址
1
//主函数,在调用TRACE_AddInstrumentFunction之前插入IMG_AddInstrumentFunction调用
2
........
3
IMG_AddInstrumentFunction(imageLoad, 0);
4
...........
5
6
7
8
//全局变量
9
ADDRINT imageBase;
10
ADDRINT imageEnd;
11
12
13
14
//imageLoad函数
15
void imageLoad(IMG img, void *v)
16
{
17
if (IMG_IsMainExecutable(img))
18
{
19
imageBase = IMG_LowAddress(img);
20
imageEnd = IMG_HighAddress(img);
21
}
22
23
}
24
25
26
27
//在Trace函数开始处添加
28
ADDRINT addr = TRACE_Address(trace);
29
if (addr < imageBase || addr > imageEnd)
30
{
31
return;
32
}
Copied!
inscount1.cpp默认的docount函数是这样的,貌似是值记录基本块的指令数.....
1
VOID docount(UINT32 c) { icount += c; }
Copied!
我们可以定义个UINT64 bblCount = 0;全局变量,在docount函数里面添加基本块的计数:
1
VOID docount(UINT32 c) {bblCount++; icount += c; }
Copied!

使用3

对于一些相同的输入但是指令数不固定的程序,我们可以找到关键校验地址,对 inscount0 的 docount 函数做如下更改
1
更改前:
2
VOID docount() { icount++; }
3
4
更改后:
5
VOID docount(void *ip)
6
{
7
// .text:000000000047B96E cmp al, cl;
8
if ((long long int)ip == 0x000000000047B96E)
9
icount++;
10
}
Copied!
需要注意的是 INS_InsertCall 是一个变参函数,前三个参数分别为指令(ins),插入的实际(IPOINT_BEFORE,表示在指令运行之前插入 docount 函数),函数指针(docount,转化为了 AFUNPTR 类型),之后的参数为传递给 docount 函数的参数,以 IARG_END 结尾,所以这里还要继续修改INS_InsertCall函数的参数。
1
更改前:
2
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
3
4
更改后:
5
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END);
Copied!
这样再运行,只有运行到 0x47B96E 一句才会计数,这样我们就可以根据 pintool 的结果来逐位爆破 flag 了

一些脚本

猜长度
1
from subprocess import Popen, PIPE
2
from sys import argv
3
import string
4
import time
5
6
def Read():
7
f = open('./inscount.out','r')
8
file = f.read().strip('\n')
9
f.close()
10
return file
11
12
13
pinPath = "/home/b0ldfrev/pin/pin"
14
pinInit = lambda tool, elf: Popen([pinPath, '-t', tool, '--', elf],stdin = PIPE, stdout = PIPE)
15
pinWrite = lambda cont: pin.stdin.write(cont)
16
pinRead = lambda : pin.communicate()[0]
17
18
if __name__ == "__main__":
19
last = 0
20
for i in range(1, 33):
21
pin = pinInit("./inscount0.so", "./test")
22
pinWrite("a" * i + '\n')
23
# time.sleep(0.5)
24
v=pinRead().split(" ")[1]
25
26
now = int(v,10)
27
28
print ( "inputLen({:2d}) -> ins({}) -> delta({})".format(i, now, now - last) )
29
last = now
Copied!
更多脚本见百度云intel-pin.