ida 脚本

IDAPython

IDA对于逆向的人而言已经非常熟悉了,并且IDA也提供非常强大的功能,但是在某些特殊情况,我们需要提取一些IDA分析的结果,这个时候我们可以使用IDAPython编写一些基本的脚本完成目的。

最近在做一些代码相似度的分析,需要查看给出的源代码和二进制之间是否有关联,如果使用IDA一行一行分析,确实可以判断是否一致,但是太过于浪费时间,能否写一个脚本,辅助我们完成一部分工作。

整体思路是:

  • 源代码会有一些特定的指令序列
  • 二进制会有一些特定的指令序列
  • 指令序列如果相似,那么可能匹配成功

Source Code Feature

首先需要在源代码上提取一些特征。选取一些简单特征,比如:函数调用,比较,字符串常量等,这些特征大概率不会被编译器优化掉。

Binary Feature

而这些特征对应的汇编代码是:call,cmp,test,string。

实现

在源代码上选择一条路径,提取特征,例如[‘call’, ‘call’, ‘cmp’, ‘call’, ‘cmp’, ‘call’]
在二进制的CFG上搜索该序列是否出现,广度优先搜索,按照序列依次匹配你,如果匹配成功则返回。
需要使用到的IDAPython的命令。
当前指令地址:ea = here()
当前函数:func = idaapi.get_func(ea)
前一条指令:idc.PrevHead(now_addr)
后一条指令:idc.NextHead(now_addr)
当前指令的汇编:idc.GetDisasm(now_addr)
当前指令的助记符:idc.GetMnem(now_addr)
操作数类型:idc.GetOpnd(now_addr, 0)
basicblock操作:bb.preds 和 bb.succs
basicblock地址:bb.startEA 和 bb.endEA

因为使用了其他一些图算法,所以使用了sark的库,非常方便的将ida内置的图转化为networkx图。

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
seq = ['call', 'call', 'cmp', 'call', 'cmp', 'call']

def checkInst(insts, seq, depth):
count = 0
for inst in insts:
# print depth, count
if not check(inst.ea):
continue
# print inst
if depth + count == len(seq):
return count
if inst.insn.mnem in ivar[seq[depth + count]]:
# print inst
count += 1
else:
return 0

return count

def BFS(bb, seq, depth, path):
# dumpBB(bb)
num = checkInst(bb.lines, seq, depth)
succs = list(bb.next)
result = False
if (len(seq) - 1) <= (depth + num):
return True
if len(succs) == 0:
return False
for succ in succs:
result = result or BFS(succ, seq, depth + num, path)
if result:
path.append(succ)
# dumpBB(succ)
# print result
return result

Result


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
// seq = ['call', 'call', 'cmp', 'call', 'cmp', 'call']
BB Start
[000F1DFB] lea rax, fromcode; "UTF-8"
[000F1E02] lea r12, [rsp+0F8h+ctxt]
[000F1E07] test encoding, encoding
[000F1E0A] mov ecx, 14h
[000F1E0F] mov r8d, [rsp+0F8h+format]
[000F1E14] cmovz encoding, rax
[000F1E18] mov rdi, r12
[000F1E1B] xor eax, eax
[000F1E1D] rep stosq
[000F1E20] xor eax, eax
[000F1E22] test r8d, r8d
[000F1E25] mov rdi, r12; ctxt
[000F1E28] setnz al
[000F1E2B] mov [rsp+0F8h+ctxt.doc], doc
[000F1E30] mov [rsp+0F8h+ctxt.buf], buf
[000F1E35] mov [rsp+0F8h+ctxt.format], eax
[000F1E39] mov [rsp+0F8h+ctxt.level], r15d
[000F1E3E] mov [rsp+0F8h+ctxt.encoding], rbx
[000F1E43] call xmlSaveCtxtInit
[000F1E48] mov rdi, doc; doc
[000F1E4B] or [rsp+0F8h+ctxt.options], 20h
[000F1E50] call xmlGetIntSubset
[000F1E55] test dtd, dtd
[000F1E58] jz short loc_F1E6B
BB End
BB Start
[000F1E5A] mov rsi, [dtd+68h]; publicID
[000F1E5E] mov rdi, [dtd+70h]; systemID
[000F1E62] call xmlIsXHTML
[000F1E67] test eax, eax
[000F1E69] jg short loc_F1EA0
BB End
BB Start
[000F1E6B] mov rsi, cur; cur
[000F1E6E] mov rdi, r12; ctxt
[000F1E71] call xmlNodeDumpOutputInternal
BB End
Path Matched
[000F1DFB] lea rax, fromcode; "UTF-8"
[000F1E07] test encoding, encoding
[000F1E22] test r8d, r8d
[000F1E43] call xmlSaveCtxtInit
[000F1E50] call xmlGetIntSubset
[000F1E55] test dtd, dtd
[000F1E62] call xmlIsXHTML
[000F1E67] test eax, eax
[000F1E71] call xmlNodeDumpOutputInternal

code