Fork me on GitHub

IDA Pro使用方法


知识点

IDA Pro(交互式反汇编器专业版)是一款极其强大的反汇编器,是许多恶意代码分析师、逆向工程师和漏洞分析师的首选。

IDA Pro接口:

图形接口:

  • 文本模式:文本模式左侧部分被称为箭头窗口,显示了程序的非线性流程,实线标记的是无条件跳转,虚线标记了条件跳转,向上的箭头表示一个循环。
  • 图形模式:图形模式中,箭头的颜色和方向显示程序的流程,红色表示一个条件跳转没有被采用,绿色表示这个条件跳转被采用,蓝色表示一个无条件跳转被采用,向上的箭头同样表示一个循环条件。

IDA窗口分布:函数窗口:列举可执行文件中的所有函数,并显示每个函数的长度。你可以根据函数长度进行排序来筛选出一些规模庞大而有趣的函数,这个窗口中每个函数关联了一些标志,如L代表此函数是库函数。

  • 名称窗口:列举每个地址的名字,包括函数、命名代码、命名数据、字符串。
  • 字符串窗口:显示所有字符串,默认显示长度超过5个字符的ASCII字符串,可以通过右键字符串窗口并选择Setup来修改此属性。
  • 导入表窗口:列举一个文件的所有导入函数。
  • 导出表窗口:列举一个文件的所有导出函数,一般多用于分析DLL文件。
  • 结构窗口:列举所有的活跃数据的结构布局。

使用链接和交叉引用:IDA中有常见的几个链接类型:

  • 子链接:一根函数开始的链接,如printf
  • 本地链接:跳转指令目的地址的链接,如loc_40107E
  • 偏移链接:内存偏移的链接

导航栏:导航栏包括一个以颜色伪代号的被加载的二进制地址空间的线性视图,其对应关系可在下方图例中找到。

跳转到指定位置:在反汇编窗口中点击G键并在弹出的对话框中输入要跳转的虚拟内存地址或命名的位置即可跳转到目标地址。

使用交叉引用:交叉引用在IDA Pro中称为xref,可以告诉你一个函数在何处被调用,或者一个字符串在何处被使用,如下:

1
2
3
4
5
6
7
.text:1000D02E ; BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
.text:1000D02E _DllMain@12 proc near ; CODE XREF: DllEntryPoint+4B↓p
.text:1000D02E ; DATA XREF: sub_100110FF+2D↓o
.text:1000D02E
.text:1000D02E hinstDLL = dword ptr 4
.text:1000D02E fdwReason = dword ptr 8
.text:1000D02E lpvReserved = dword ptr 0Ch

在默认情况下,IDA Pro只显示给定函数的少数交叉引用。要查看一个函数的多有交叉引用,单击函数名并按X键,弹出的窗口会列举这个函数所有被调用的位置。

函数重命名:IDA在自动命名虚拟地址和栈变量方面做的很好,但是用户也可以手动修改这些自动生成的名字来使得分析过程更加便捷。

注释:将光标放在反汇编的某行上,并按冒号键:便会弹出一个注释窗口。

重新定义代码和数据:有时候IDA可能会错误的识别字节,比如把数据识别为代码,这时你可以按C键定义原始字节为代码,并通过D键和A键将这些字节定义为数据或者ASCII字符串。

IDA插件:IDA支持IDC脚本和Python脚本:

  • IDC脚本是IDA内置的脚本语言,其中所有的函数都被声明为静态函数,参数不需要指定类型,auto被用来定义全局变量。
  • IDAPython提供三个模块来访问IDA API(idaapi)、IDC接口(idc)、以及IDAPython工具函数(idautils)。

课后练习

Lab5-1

只用IDA Pro分析在文件Lab05-01.dll中发现的恶意代码。这个实验的目标是给你一个用IDA Pro动手的经验。如果你已经用IDA Pro工作过,你可以选择忽略这些问题,而将精力集中在恶意代码上。

问题

1.DllMain的地址是什么

双击DllMain即可看到:

1
2
3
4
5
6
7
.text:1000D02E ; BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
.text:1000D02E _DllMain@12 proc near ; CODE XREF: DllEntryPoint+4B↓p
.text:1000D02E ; DATA XREF: sub_100110FF+2D↓o
.text:1000D02E
.text:1000D02E hinstDLL = dword ptr 4
.text:1000D02E fdwReason = dword ptr 8
.text:1000D02E lpvReserved = dword ptr 0Ch

0x1000D02E

2.使用Imports窗口并浏览到gethostbyname,导入函数定位到什么地址?

在Imports窗口双击该函数即可

1
2
3
4
.idata:100163CC ; struct hostent *__stdcall gethostbyname(const char *name)
.idata:100163CC extrn gethostbyname:dword
.idata:100163CC ; CODE XREF: sub_10001074:loc_100011AF↑p
.idata:100163CC ; sub_10001074+1D3↑p ...

0x100163CC

3.有多少函数调用了gethostbyname?

通过查看调用关系能查出有5个函数调用了此函数

4.将精力集中在位于0x10001757处的对gethostbyname的调用,你能找出哪个DNS请求将被触发吗?

在call指令之前找到参数入栈,双击off_10019040

1
2
3
4
.text:1000174E                 mov     eax, off_10019040
.text:10001753 add eax, 0Dh
.text:10001756 push eax ; name
.text:10001757 call ds:gethostbyname

即可找到DNS请求的域名:

1
2
3
4
.data:10019040 off_10019040    dd offset aThisIsRdoPicsP
.data:10019040 ; DATA XREF: sub_10001656:loc_10001722↑r
.data:10019040 ; sub_10001656+F8↑r ...
.data:10019040 ; "[This is RDO]pics.praticalmalwareanalys"...

pics.praticalmalwareanalys.com

5.IDA Pro识别了在0x10001656处的子过程的多少个局部变量?

F5大法好,看到如下结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.text:10001656 var_675         = byte ptr -675h
.text:10001656 var_674 = dword ptr -674h
.text:10001656 hModule = dword ptr -670h
.text:10001656 timeout = timeval ptr -66Ch
.text:10001656 name = sockaddr ptr -664h
.text:10001656 var_654 = word ptr -654h
.text:10001656 Dst = dword ptr -650h
.text:10001656 Str1 = byte ptr -644h
.text:10001656 var_640 = byte ptr -640h
.text:10001656 CommandLine = byte ptr -63Fh
.text:10001656 Str = byte ptr -63Dh
.text:10001656 var_638 = byte ptr -638h
.text:10001656 var_637 = byte ptr -637h
.text:10001656 var_544 = byte ptr -544h
.text:10001656 var_50C = dword ptr -50Ch
.text:10001656 var_500 = byte ptr -500h
.text:10001656 Buf2 = byte ptr -4FCh
.text:10001656 readfds = fd_set ptr -4BCh
.text:10001656 buf = byte ptr -3B8h
.text:10001656 var_3B0 = dword ptr -3B0h
.text:10001656 var_1A4 = dword ptr -1A4h
.text:10001656 var_194 = dword ptr -194h
.text:10001656 WSAData = WSAData ptr -190h
.text:10001656 lpThreadParameter= dword ptr 4

所以因该是23个局部变量。

6.IDA Pro识别了在0x10001656处的子过程的多少个参数?

1
.text:10001656 lpThreadParameter= dword ptr  4

1个参数

7.使用string窗口,来在反汇编中定位字符串\cmd.exe /c。它位于哪?

1
xdoors_d:10095B34 aCmdExeC        db '\cmd.exe /c ',0     ; DATA XREF: sub_1000FF58+278↑o

0x10095B34

8.在引用\cmd.exe /c的代码区域发生了什么?

先通过双击XREF找到引用位置可发现如下代码:

1
2
3
4
5
6
.text:100101C8                 cmp     dword_1008E5C4, ebx
.text:100101CE jz short loc_100101D7
.text:100101D0 push offset aCmdExeC ; "\\cmd.exe /c "
.text:100101D5 jmp short loc_100101DC
.text:100101D7 loc_100101D7: ; CODE XREF: sub_1000FF58+276↑j
.text:100101D7 push offset aCommandExeC ; "\\command.exe /c "

cmd.exe /c命令可以在执行完命令之后关闭CMD窗口,因此这应该是个命令执行,且父函数的参数是SOCKET s,且函数后半部分有大量的cmp指令来跟一系列的指令比较,因此猜测这个函数是一个实现远程命令执行的函数。

9.在同样的区域,在0x100101C8处,看起来好像dword_1008E5C4是一个全局变量,他帮助你决定走哪条路径。那恶意代码是如何设置dword_1008E5C4的呢?(提示:使用dword_1008E5C4的交叉引用)

通过查看dword_1008E5C4的交叉引用,发现如下信息

第一条数据显示了一个赋值操作,因此双击第一条,可以发现dword_1008E5C4的值为函数sub_10003695()的返回值:

1
2
3
4
5
6
7
8
if ( !sub_10001000() )
{
v39 = 0;
hModule = 0;
dword_1008E5C4 = sub_10003695();
dword_1008E5C8 = sub_100036C3();
Sleep(0x3A98u);
sub_100110FF();

因此查看一下函数sub_10003695()的内容:

1
2
3
4
5
6
7
8
BOOL sub_10003695()
{
struct _OSVERSIONINFOA VersionInformation; // [esp+0h] [ebp-94h]

VersionInformation.dwOSVersionInfoSize = 148;
GetVersionExA(&VersionInformation);
return VersionInformation.dwPlatformId == 2;
}

因此dword_1008E5C4VersionInformation.dwPlatformId == 2的值,1或0。

10.在位于0x1000FF58处的子过程的几百行指令中,一系列使用memcmp来比较字符串的比较。如果对robotwork的字符串比较是成功的(当memcmp返回0),会发生什么?

找到指定位置:

1
2
3
4
5
6
7
8
9
10
11
12
.text:10010444 loc_10010444:                           ; CODE XREF: sub_1000FF58+4E0↑j
.text:10010444 push 9 ; Size
.text:10010446 lea eax, [ebp+Dst]
.text:1001044C push offset aRobotwork ; "robotwork"
.text:10010451 push eax ; Buf1
.text:10010452 call memcmp
.text:10010457 add esp, 0Ch
.text:1001045A test eax, eax
.text:1001045C jnz short loc_10010468
.text:1001045E push [ebp+s] ; s
.text:10010461 call sub_100052A2
.text:10010466 jmp short loc_100103F6

如果memecmp()函数返回0,则jnz不执行,程序将执行函数sub_100052A2,函数sub_100052A2是一个数据库查询函数,查询了HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WorktimeHKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Worktimes的值。然后再通过sub_100038EE函数将查询的结果发送出去。

11.PSLIST导出函数做了什么?

函数伪C代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __userpurge PSLIST@<eax>(char a1@<sil>, int a2, int a3, char *Str, int a5)
{
int result; // eax

dword_1008E5BC = 1;
result = sub_100036C3();
if ( result )
{
if ( strlen(Str) )
result = sub_1000664C(a1, 0, Str);
else
result = sub_10006518();
}
dword_1008E5BC = 0;
return result;
}

其中,函数sub_100036C3()用于判断操作系统和操作系统版本,当操作系统版本符合要求时,程序通过CreatToolhelp32Snapshot函数来获取当前正在运行的进程的列表,结合导出函数的名字我们可以得知这是一个获取计算机运行进程列表的函数。

12.使用图模式来绘制出对sub_10004E79的交叉引用图。当进入这个函数时,哪个API函数可能被调用?仅仅基于这些API函数,你会如何重命名这个函数?

API函数如图粉色方块内容。

getSysLang

13.DllMain直接调用了多少个Windows API?多少个在深度为2时被调用?

本题。。。卒

14.在0x10001358处,有一个对Sleep(一个使用一个包含要睡眠的毫秒数的参数的API函数)的调用。顺着代码向后看,如果这段代码执行,这个程序会睡眠多久?

代码如下:

1
2
3
4
5
6
7
8
9
10
11
.text:10001341                 mov     eax, off_10019020
.text:10001346 add eax, 0Dh
.text:10001349 push eax ; Str
.text:1000134A call ds:atoi
.text:10001350 imul eax, 3E8h
.text:10001356 pop ecx
.text:10001357 push eax ; dwMilliseconds
.text:10001358 call ds:Sleep
.text:1000135E xor ebp, ebp
.text:10001360 jmp loc_100010B4
.text:10001360 sub_10001074 endp

因此指令add eax, 0Dh执行之后EAX的值为30,因此Sleep函数为Sleep(30*1000),因此程序会睡眠30秒。

15.在0x10001701处是一个随socket的调用。它的三个参数是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.text:100016FB loc_100016FB:                           ; CODE XREF: sub_10001656+374↓j
.text:100016FB ; sub_10001656+A09↓j
.text:100016FB push 6 ; protocol
.text:100016FD push 1 ; type
.text:100016FF push 2 ; af
.text:10001701 call ds:socket
.text:10001707 mov edi, eax
.text:10001709 cmp edi, 0FFFFFFFFh
.text:1000170C jnz short loc_10001722
.text:1000170E call ds:WSAGetLastError
.text:10001714 push eax
.text:10001715 push offset aSocketGetlaste ; "socket() GetLastError reports %d\n"
.text:1000171A call ds:__imp_printf
.text:10001720 pop ecx
.text:10001721 pop ecx

IDA给出的参数名为aftypeprotocol,值分别为216

16.使用MSDN页面的socket和IDA Pro中的命名符号常量,你能使参数更加有意义吗?在你应用了修改以后,参数是什么?

AF_INETSOCK_STREAMIPPROTO_TCP

17.搜索in指令(opcode 0xED)的使用。这个指令和一个魔术字符串VMXh用来进行VMware检测。在这个恶意代码中被使用了吗?使用对执行in指令函数的交叉引用,能发现进一步检测VMware的证据吗?

再IDA中选择Search->sequence of bytes,输入ED然后选择搜索所有结果,再搜索结果中能找到一个in指令:

右键选择564D5868h,可以看到此数据的ASCII码是VMXh

查看此函数的交叉引用可以看到如下几个相同的调用;

查看后发现如下字符串:

18.将你的光标跳转到0x1001D988处,你发现了什么?

发现了一些ASCII码

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
78
79
80
.data:1001D988                 db  2Dh ; -
.data:1001D989 db 31h ; 1
.data:1001D98A db 3Ah ; :
.data:1001D98B db 3Ah ; :
.data:1001D98C db 27h ; '
.data:1001D98D db 75h ; u
.data:1001D98E db 3Ch ; <
.data:1001D98F db 26h ; &
.data:1001D990 db 75h ; u
.data:1001D991 db 21h ; !
.data:1001D992 db 3Dh ; =
.data:1001D993 db 3Ch ; <
.data:1001D994 db 26h ; &
.data:1001D995 db 75h ; u
.data:1001D996 db 37h ; 7
.data:1001D997 db 34h ; 4
.data:1001D998 db 36h ; 6
.data:1001D999 db 3Eh ; >
.data:1001D99A db 31h ; 1
.data:1001D99B db 3Ah ; :
.data:1001D99C db 3Ah ; :
.data:1001D99D db 27h ; '
.data:1001D99E db 79h ; y
.data:1001D99F db 75h ; u
.data:1001D9A0 db 26h ; &
.data:1001D9A1 db 21h ; !
.data:1001D9A2 db 27h ; '
.data:1001D9A3 db 3Ch ; <
.data:1001D9A4 db 3Bh ; ;
.data:1001D9A5 db 32h ; 2
.data:1001D9A6 db 75h ; u
.data:1001D9A7 db 31h ; 1
.data:1001D9A8 db 30h ; 0
.data:1001D9A9 db 36h ; 6
.data:1001D9AA db 3Ah ; :
.data:1001D9AB db 31h ; 1
.data:1001D9AC db 30h ; 0
.data:1001D9AD db 31h ; 1
.data:1001D9AE db 75h ; u
.data:1001D9AF db 33h ; 3
.data:1001D9B0 db 3Ah ; :
.data:1001D9B1 db 27h ; '
.data:1001D9B2 db 75h ; u
.data:1001D9B3 db 5
.data:1001D9B4 db 27h ; '
.data:1001D9B5 db 34h ; 4
.data:1001D9B6 db 36h ; 6
.data:1001D9B7 db 21h ; !
.data:1001D9B8 db 3Ch ; <
.data:1001D9B9 db 36h ; 6
.data:1001D9BA db 34h ; 4
.data:1001D9BB db 39h ; 9
.data:1001D9BC db 75h ; u
.data:1001D9BD db 18h
.data:1001D9BE db 34h ; 4
.data:1001D9BF db 39h ; 9
.data:1001D9C0 db 22h ; "
.data:1001D9C1 db 34h ; 4
.data:1001D9C2 db 27h ; '
.data:1001D9C3 db 30h ; 0
.data:1001D9C4 db 75h ; u
.data:1001D9C5 db 14h
.data:1001D9C6 db 3Bh ; ;
.data:1001D9C7 db 34h ; 4
.data:1001D9C8 db 39h ; 9
.data:1001D9C9 db 2Ch ; ,
.data:1001D9CA db 26h ; &
.data:1001D9CB db 3Ch ; <
.data:1001D9CC db 26h ; &
.data:1001D9CD db 75h ; u
.data:1001D9CE db 19h
.data:1001D9CF db 34h ; 4
.data:1001D9D0 db 37h ; 7
.data:1001D9D1 db 75h ; u
.data:1001D9D2 db 6Fh ; o
.data:1001D9D3 db 7Ch ; |
.data:1001D9D4 db 64h ; d
.data:1001D9D5 db 67h ; g
.data:1001D9D6 db 66h ; f
.data:1001D9D7 db 61h ; a

19,如果你安装了IDA Python插件(包括IDA Pro的商业版本插件),运行Lab05-01.py,一个本书中随恶意代码提供的IDA Pro Python脚本,(确定光标是在0x1001D988处。)你运行这个脚本后发生了什么?

运行此脚本后发现同一位置的数据发生了变化:

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
78
79
80
.data:1001D988                 db  78h ; x
.data:1001D989 db 64h ; d
.data:1001D98A db 6Fh ; o
.data:1001D98B db 6Fh ; o
.data:1001D98C db 72h ; r
.data:1001D98D db 20h
.data:1001D98E db 69h ; i
.data:1001D98F db 73h ; s
.data:1001D990 db 20h
.data:1001D991 db 74h ; t
.data:1001D992 db 68h ; h
.data:1001D993 db 69h ; i
.data:1001D994 db 73h ; s
.data:1001D995 db 20h
.data:1001D996 db 62h ; b
.data:1001D997 db 61h ; a
.data:1001D998 db 63h ; c
.data:1001D999 db 6Bh ; k
.data:1001D99A db 64h ; d
.data:1001D99B db 6Fh ; o
.data:1001D99C db 6Fh ; o
.data:1001D99D db 72h ; r
.data:1001D99E db 2Ch ; ,
.data:1001D99F db 20h
.data:1001D9A0 db 73h ; s
.data:1001D9A1 db 74h ; t
.data:1001D9A2 db 72h ; r
.data:1001D9A3 db 69h ; i
.data:1001D9A4 db 6Eh ; n
.data:1001D9A5 db 67h ; g
.data:1001D9A6 db 20h
.data:1001D9A7 db 64h ; d
.data:1001D9A8 db 65h ; e
.data:1001D9A9 db 63h ; c
.data:1001D9AA db 6Fh ; o
.data:1001D9AB db 64h ; d
.data:1001D9AC db 65h ; e
.data:1001D9AD db 64h ; d
.data:1001D9AE db 20h
.data:1001D9AF db 66h ; f
.data:1001D9B0 db 6Fh ; o
.data:1001D9B1 db 72h ; r
.data:1001D9B2 db 20h
.data:1001D9B3 db 50h ; P
.data:1001D9B4 db 72h ; r
.data:1001D9B5 db 61h ; a
.data:1001D9B6 db 63h ; c
.data:1001D9B7 db 74h ; t
.data:1001D9B8 db 69h ; i
.data:1001D9B9 db 63h ; c
.data:1001D9BA db 61h ; a
.data:1001D9BB db 6Ch ; l
.data:1001D9BC db 20h
.data:1001D9BD db 4Dh ; M
.data:1001D9BE db 61h ; a
.data:1001D9BF db 6Ch ; l
.data:1001D9C0 db 77h ; w
.data:1001D9C1 db 61h ; a
.data:1001D9C2 db 72h ; r
.data:1001D9C3 db 65h ; e
.data:1001D9C4 db 20h
.data:1001D9C5 db 41h ; A
.data:1001D9C6 db 6Eh ; n
.data:1001D9C7 db 61h ; a
.data:1001D9C8 db 6Ch ; l
.data:1001D9C9 db 79h ; y
.data:1001D9CA db 73h ; s
.data:1001D9CB db 69h ; i
.data:1001D9CC db 73h ; s
.data:1001D9CD db 20h
.data:1001D9CE db 4Ch ; L
.data:1001D9CF db 61h ; a
.data:1001D9D0 db 62h ; b
.data:1001D9D1 db 20h
.data:1001D9D2 db 3Ah ; :
.data:1001D9D3 db 29h ; )
.data:1001D9D4 db 31h ; 1
.data:1001D9D5 db 32h ; 2
.data:1001D9D6 db 33h ; 3
.data:1001D9D7 db 34h ; 4

以上字符串如下:

1
xdoor is this backdoor string decoded for Practical Malware Analysis Lab :)1234

20.将光标放在同一位置,你如何将这个数据转成一个单一的ASCII字符串?

在同一位置点击A键可以自动将这串字符串提取出来:

21.使用一个文本编辑器打开这个脚本,它是如何工作的?

Lab05-01.py内容如下:

1
2
3
4
5
6
sea = ScreenEA()

for i in range(0x00,0x50):
b = Byte(sea+i)
decoded_byte = b ^ 0x55
PatchByte(sea+i,decoded_byte)

这个脚本先获取光标所在位置的第一个数据,将第一个数据后的50h的数据与0x55作与运算,再将运算结果替换掉原来的数据。


本章结束🎊

您的支持是我最大的动力🍉