静态分析工具之ida

啦啦啦啦~

ida

ida使用的是数据库文件,在第一次分析binary时会生成它,并且之后的几乎所有操作都是对数据库的操作上,并且它的操作是不可撤回的。

复合结构

数组

不算吧,反正在首地址Edit->Array即可定义,对于伪代码可以Y来定义

结构体

直接看就懂了,主要问题是笔记本的del和ins键在一起,可以使用fn+del新建。

另外也可以使用local types来新建结构体,包含头文件也可以,只是需要自己手动将其添加进数据窗口。

错误

Decompilation failure: XXXXXX: call analysis failed Please refer to the man

类似的错误是因为不能正确识别函数原型,手动设置下函数类型即可,对于间接调用设置调用地址。

positive sp value

IDA会自动分析SP,寄存器的变化量,由于缺少调用约定、参数个数等信息,导致分析出错,Option→Generala中设置显示Stack pointer,然后去检查对应地址附近调用的函数的调用约定以及栈指针变化

cannot convert to microcode

部分指令无法被反编译,最常见起因是函数中间有未设置成指令的数据字节,按c将其设置为指令即可,其次常见的是x86中的rep前缀,比如repXX jmp等,可以将该指令的第一个字节(repXX前缀的对应位置)patch为0x90(NOP)

stack frame is too big

在分析栈帧时,IDA出现异常,导致分析出错。找到明显不合常理的stackvariable offset,双击进入栈帧界面,按u键删除对应stack variable;如果是壳导致的原因,先用OD等软件脱壳;可能由花指令导致,请手动或自动检查并去除花指令

local variable allocation failed

分析函数时,有部分变量对应的区域发生重叠,多见于ARM平台出现Point、Rect等8字节、16字节、32字节结构时尤其多见;修改对应参数为多个int;修改ida安装目录下的hexrays.cfg中的HO_IGNORE_OVERLAPS

F5分析结果不正确

F5会自动删除其认为不可能到达的死代码,常见起因是一个函数错误的被标注成了noreturn函数,进到目前反编译结果,找到最后被调用的函数(被错误分析的函数),双击进入,再返回(迫使HexRays重新分析相应函数);如果上述方案不成功,那么进到被错误分析的函数,按Tab切换到反汇编界面,按Alt-P进入界面取消函数的Does not return属性

调试

直接把idadbg文件夹下对应调试器放在目标环境并运行,然后选择调试器,在debugger->process options下设置调试选项:

idc

ida提供的类C脚本语言,下面介绍也和C对照着来:

变量

变量区分大小写,需要先声明后使用,可在局部声明:

1
2
auto addr,reg,valu=0;     //局部变量
extern globalVar; //全局变量,不能在声明时赋值

对于整数全部作为有符号数处理。

表达式

支持C里面除+= /= *= -=外所有运算,支持字符串赋值复制及切片:

1
2
3
auto str1 = "what the fa?";
auto str2 = str1;
auto str3 = str2[3:];

语句

支持C里面除switch外所有语句,支持try/catch/throw

函数

在idc文件中支持定义函数(idc命令行不行),函数参数是传值还是传引用有调用者确定:

1
2
3
4
5
6
7
8
static printAB(a,b){
b = a * b;
Message(b);
}
auto a = 3;
auto b = 4;
printAB(a,b); //a=3,b=4
printAB(a,&b); //a=3,b=12

函数可以返回任意类型,若不显示指出返回0.

注释

插件

注意:安装插件时,若缺少dll最好去安装微软的运行时库而不要直接百度下载dll,不然将会被奇妙的问题带到奇妙的坑里

  • keypatch:用来修改程序,也可以用来搜索指令。
  • findcrypt:使用yara来发现加密算法中的常数
  • ida_clemency:肝clemency的
  • auto_re:增强标记
  • sk3wldbg:一个基于unicorn的调试器
  • lighthouse:神奇的工具,似乎很厉害,还不会用
  • IDASkins:皮肤
  • ida_nightfall:一种配色
  • idastealth:对抗反调试的(三年没更新了)
  • sig-database:用于识别静态编译的库函数
  • ida-signsrch:通过特征识别函数,能够识别很多压缩、多媒体、密码算法、字符串、反调试等算法
  • Ponce
  • idaemu:使用UNicorn进行模拟执行的,其实感觉和unicorn一样
  • diaphora:用于diff,可以比较F5的伪代码,现在已经支持7.0啦
  • bindiff:用于diff,目前只支持到6.8,但是很强大
  • FRIEND:指令、寄存器扩展与帮助文档
  • SimplifyGraph:一个十分好用的绘图工具,用于增强ida的流图界面
  • HexRaysPyTools:自动创建结构体,类与虚表
  • nao:简化一些没意义的汇编指令

例题

pwnhub-无题

想用这道题来练习下idc脚本编写,但是没找到原题的binary了,只能自己根据题目writeup描述自己写一个类似的:

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
#!/usr/bin/env python
# coding=utf-8
import random
with open("outfile.c","w") as of:
protype = []
cases = []
funs = []
for i in range(1,30000 + 1):
protype.append("int func_%d();\n"%i)
cases.append("case %d:func_%d();break;\n"%(i,i))
bufLen = random.randrange(15,30)
inputableLen = 0x20+0x10 if i == 2314 else random.randrange(bufLen-10,bufLen-1)
funs.append("""
int func_%d(){
char buf[%d];
fread(buf,%d,1,stdin);
return fwrite("thank you for testing!!\\n",1,0x18,stdout);
}
"""%(i,bufLen,inputableLen))
of.write("""
#include<stdlib.h>
#include<stdio.h>
""")
of.writelines(protype)
of.write("""
int main(){
setvbuf(stdout,0,_IONBF,0);
setvbuf(stdin,0,_IONBF,0);
setvbuf(stderr,0,_IONBF,0);
int fun;
puts("~~~~~~~~~~fxxk ada~~~~~~~~~");
puts("fun No.?(1~3000):");
scanf("%d",&fun);
switch(fun){
""")
of.writelines(cases)
of.write("""
default:puts("err No.");
}
return 0;
}
""")
of.writelines(funs)

编译:

1
gcc -fno-stack-protector -no-pie outfile.c -o test

假装不知道漏洞在哪个函数里面,就写了下面的脚本来分析咯:

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
for addr in XrefsTo(0x00400550, flags=0):
func=addr.frm;
writesize=0;
buffersize=0;
while(1):
if(Byte(func)==0xBE and Byte(func+2)==0x00 and Byte(func+3)==0x00 and Byte(func+4)==0x00):
writesize=Byte(func+1)
elif(Byte(func)==0xBE and Byte(func+3)==0x00 and Byte(func+4)==0x00 and Byte(func+5)==0x00):
writesize=Word(func+1)
if(Byte(func)==0x48 and Byte(func+1)==0x83 and Byte(func+2)==0xEC):
buffersize=Byte(func+3)
break;
elif(Byte(func)==0x48 and Byte(func+1)==0x81 and Byte(func+2)==0xEC):
buffersize=Byte
break;
elif(Byte(func)==0x48 and Byte(func+1)==0x83 and Byte(func+2)==0xC4):
buffersize=Byte
break;
func-=1;
if(addr.frm-func > 0x40):
break;
if writesize==0 or buffersize==0:
print "not found!",hex(addr.frm)
elif buffersize<writesize:
print "find overflow",hex(addr.frm),"buffersize="+str(hex(buffersize)),"writesize="+str(hex(writesize))
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
.text:0000000000435E7B                 public func_2314
.text:0000000000435E7B func_2314 proc near ; CODE XREF: .text:00000000004099B0↑p
.text:0000000000435E7B
.text:0000000000435E7B ptr = byte ptr -20h
.text:0000000000435E7B
.text:0000000000435E7B ; __unwind {
.text:0000000000435E7B push rbp
.text:0000000000435E7C mov rbp, rsp
.text:0000000000435E7F sub rsp, 20h ;;buf 0x20
.text:0000000000435E83 mov rdx, cs:stdin@@GLIBC_2_2_5
.text:0000000000435E8A lea rax, [rbp+ptr]
.text:0000000000435E8E mov rcx, rdx ; stream
.text:0000000000435E91 mov edx, 1 ; n
.text:0000000000435E96 mov esi, 2Fh ; size ;;可写0x2f
.text:0000000000435E9B mov rdi, rax ; ptr
.text:0000000000435E9E call _fread
.text:0000000000435EA3 mov rax, cs:stdout@@GLIBC_2_2_5
.text:0000000000435EAA mov rcx, rax ; s
.text:0000000000435EAD mov edx, 18h ; n
.text:0000000000435EB2 mov esi, 1 ; size
.text:0000000000435EB7 lea rdi, aThankYouForTes ; "thank you for testing!!\n"
.text:0000000000435EBE call _fwrite
.text:0000000000435EC3 leave
.text:0000000000435EC4 retn
.text:0000000000435EC4 ; } // starts at 435E7B
.text:0000000000435EC4 func_2314 endp

可以看到溢出了0xf字节,比原题要多5字节