PEid 0.95 Cave 查找功能逆向
在学习CCDebuger前辈的OllyDBG入门系列七之汇编功能的时候,其中有一个部分用到了PEid的Cave查找功能,一开始不知道这个Cave查找是什么意思,CCDebuger前辈的截图使用的中文翻译版PEiD,里面显示的是‘搜索全0处’这个功能,如图所示
但是根据查找结果以及与文件对比,在文件该偏移处的size块内容并不是全0,在WinHex下查看如图所示
在我使用的从52pojie论坛下载的v0.95版本,这里显示的则是Cave 查找功能,如下图所示
不太理解这里是为什么,正好又一直在学习使用ollydbg,于是将PEid拖进了调试器,进行了简单的逆向以尝试解决疑问。
打开OD,载入PEid,执行后将任意文件拖进PEid中,这里我使用的是CCDebuger前辈用来做实例的myuninst程序,PEid先对该PE文件做了简单的查找分析,结果如下图所示
由于不知道怎么通过Ollydbg给右键菜单下断点,于是一开始我想在上图所示的按钮下Button的202 WM_LBUTTONUP断点,但是实际上程序断下来的地方是显示区段消息的一个界面,这个界面再通过右键菜单才能到Cave 查找功能,距离想要查找的地方太远,于是放弃。后来偶然想到,程序在显示Cave 查找结果的时候貌似是使用了MessageBox函数进行展示的,于是尝试着对MessageBoxA这个函数下断点,最终断在了0x44BDC6处,该函数部分如下
0044BC50 /$ 55 push ebp
0044BC51 |. 8BEC mov ebp, esp
0044BC53 |. 83E4 F8 and esp, FFFFFFF8
0044BC56 |. 6A FF push -1
0044BC58 |. 68 7BCB4600 push 0046CB7B ; SE handler installation
0044BC5D |. 64:A1 0000000>mov eax, dword ptr fs:[0]
0044BC63 |. 50 push eax
0044BC64 |. 64:8925 00000>mov dword ptr fs:[0], esp
0044BC6B |. 81EC 18050000 sub esp, 518
0044BC71 |. 53 push ebx
0044BC72 |. 56 push esi
0044BC73 |. 57 push edi
0044BC74 |. B9 0A000000 mov ecx, 0A
0044BC79 |. BE 186C4000 mov esi, 00406C18 ; 区段\t\t rva\t\t 偏移 MessageBoxA的各个项目的项目名称
0044BC7E |. 8DBC24 200100>lea edi, dword ptr [esp+120]
…………
0044BCB3 |. 6A 01 push 1
0044BCB5 |. 50 push eax ; 文件路径指针
0044BCB6 |. B9 001E4000 mov ecx, 00401E00 ; this 指针
0044BCBB |. E8 E04BFFFF call 004408A0 ; 打开文件,并且将文件map映射到堆中
0044BCC0 |. 84C0 test al, al
0044BCC2 |. 0F84 0E010000 je 0044BDD6
0044BCC8 |. 8B0D 101E4000 mov ecx, dword ptr [401E10] ; [401E10] 文件大小
0044BCCE |. 8B15 0C1E4000 mov edx, dword ptr [401E0C] ; [401E0C] 映射的基地址
0044BCD4 |. 51 push ecx
0044BCD5 |. 52 push edx
0044BCD6 |. B9 280D4700 mov ecx, 00470D28
0044BCDB |. E8 E0760000 call 004533C0 ; 检查文件正确性,并且构造一个文件结构,该结构存储在0x470d28处,保存文件一些属性
…………
0044BCE8 |. 6A 20 push 20
0044BCEA |. 8D4424 14 lea eax, dword ptr [esp+14]
0044BCEE |. 68 280D4700 push 00470D28 ; 上面建立的文件结构指针
0044BCF3 |. 50 push eax ; 栈缓冲区变量,函数返回结果保存在这个变量中
0044BCF4 |. E8 B7B2FEFF call 00436FB0 ; 关键!需要步进
…………
下面是一个循环,对每个section调用了wsprintf构造格式化字符串,生成结果字符串,最后使用MessageBoxA打印输出结果。wsprintf使用的参数部分是通过上面的sub_00436FB0函数计算得到的,后面我们只需要着重分析该函数即可知道我们想要的结果。
0044BD38 |. 8B1D 4C124000 mov ebx, dword ptr [<&USER32.wsprint>; USER32.wsprintfA
0044BD3E |. 8BFF mov edi, edi
0044BD40 |> /8378 28 10 /cmp dword ptr [eax+28], 10
0044BD44 |. |72 05 |jb short 0044BD4B
0044BD46 |. |8B48 14 |mov ecx, dword ptr [eax+14]
0044BD49 |. |EB 03 |jmp short 0044BD4E
0044BD4B |> |8D48 14 |lea ecx, dword ptr [eax+14]
0044BD4E |> |8B50 08 |mov edx, dword ptr [eax+8]
0044BD51 |. |52 |push edx ; size
0044BD52 |. |8B50 04 |mov edx, dword ptr [eax+4]
0044BD55 |. |8B00 |mov eax, dword ptr [eax]
0044BD57 |. |52 |push edx ; offset
0044BD58 |. |50 |push eax ; RVA
0044BD59 |. |51 |push ecx ; section name
0044BD5A |. |8D4C24 30 |lea ecx, dword ptr [esp+30] ; output buffer
0044BD5E |. |68 006C4000 |push 00406C00 ; %8s\t\t%08x\t%08x\t%08x\n\n
0044BD63 |. |51 |push ecx
0044BD64 |. |FFD3 |call ebx ; 调用wsprintf格式化构造MessageBoxA函数的输出字符串
0044BD66 |. |8D4424 38 |lea eax, dword ptr [esp+38]
0044BD6A |. |83C4 18 |add esp, 18
0044BD6D |. |8BC8 |mov ecx, eax
0044BD6F |. |90 |nop
0044BD70 |> |8A10 |/mov dl, byte ptr [eax]
0044BD72 |. |40 ||inc eax
0044BD73 |. |84D2 ||test dl, dl
0044BD75 |.^|75 F9 |\jnz short 0044BD70
0044BD77 |. |8DBC24 200100>|lea edi, dword ptr [esp+120]
0044BD7E |. |2BC1 |sub eax, ecx
0044BD80 |. |8BF1 |mov esi, ecx
0044BD82 |. |4F |dec edi
0044BD83 |> |8A4F 01 |/mov cl, byte ptr [edi+1]
0044BD86 |. |47 ||inc edi
0044BD87 |. |84C9 ||test cl, cl
0044BD89 |.^|75 F8 |\jnz short 0044BD83
0044BD8B |. |8BC8 |mov ecx, eax
0044BD8D |. |C1E9 02 |shr ecx, 2
0044BD90 |. |F3:A5 |rep movs dword ptr es:[edi], dword >
0044BD92 |. |8BC8 |mov ecx, eax
0044BD94 |. |A1 4C104700 |mov eax, dword ptr [47104C]
0044BD99 |. |83E1 03 |and ecx, 3
0044BD9C |. |F3:A4 |rep movs byte ptr es:[edi], byte pt>
0044BD9E |. |8B0D 58104700 |mov ecx, dword ptr [471058]
0044BDA4 |. |83C0 2C |add eax, 2C
0044BDA7 |. |3BC1 |cmp eax, ecx
0044BDA9 |. |A3 4C104700 |mov dword ptr [47104C], eax
0044BDAE |.^\75 90 \jnz short 0044BD40
0044BDB0 |> 8B45 0C mov eax, dword ptr [ebp+C]
0044BDB3 |. 68 00000400 push 40000 ; /Style = MB_OK|MB_APPLMODAL|40000
0044BDB8 |. 68 F46B4000 push 00406BF4 ; |cave 信息 从这个title我们也可以猜到,这个MessageBoxA函数的调用就是我们输出我们想要的结果的
0044BDBD |. 8D9424 280100>lea edx, dword ptr [esp+128] ; |
0044BDC4 |. 52 push edx ; |Text
0044BDC5 |. 50 push eax ; |hOwner
0044BDC6 |. FF15 70134000 call dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
sub_436FB0比较长,而且对PE文件做了很多安全检查,包括各种size大小是否正确,文件类型是否正确,target machine字段是否正确等,这里只对部分关键代码进行分析,其他部分则是通过查找然后cmp目标值进行判断的。
00437016 |> /8B8C24 A40000>/mov ecx, dword ptr [esp+A4]
0043701D |. |8D49 00 |lea ecx, dword ptr [ecx]
00437020 |> |8B01 mov eax, dword ptr [ecx] ; eax == 文件映射基地址
00437022 |. |8B7D 00 |mov edi, dword ptr [ebp] ; RawOffset / PointToRawData
00437025 |. |03F8 |add edi, eax ; 获取文件映射内存处该section的起始地址
00437027 |. |8B4424 14 |mov eax, dword ptr [esp+14]
0043702B |. |50 |push eax
0043702C |. |E8 4FC30100 |call 00453380 ; 在VirtualSize、RawDataSize、FileSize-PointToRawData三个量之间计算得到最小值
00437031 |. |8BF0 |mov esi, eax
00437033 |. |3BF3 |cmp esi, ebx
00437035 |. |0F84 C9010000 |je 00437204
0043703B |. |8B8C24 A40000>|mov ecx, dword ptr [esp+A4]
00437042 |. |8B41 04 |mov eax, dword ptr [ecx+4]
00437045 |. |3945 00 |cmp dword ptr [ebp], eax ; 判断SizeOfRawData与FileSize的大小,如果FileSize 小,则跳过该section
00437048 |. |0F83 B6010000 |jnb 00437204
0043704E |. |8B49 14 |mov ecx, dword ptr [ecx+14]
00437051 |. |8B41 24 |mov eax, dword ptr [ecx+24] ; FileAlignment的值
00437054 |. |8D4C30 FF |lea ecx, dword ptr [eax+esi-1]
00437058 |. |48 |dec eax
00437059 |. |F7D0 |not eax
0043705B |. |23C8 |and ecx, eax ; SizeOfRawData 以 FileAlignment值 向上对齐,比如 .text 的大小为9411, 处理之后为9600
0043705D |. |8D49 00 |lea ecx, dword ptr [ecx]
00437060 |> |385C37 FF |/cmp byte ptr [edi+esi-1], bl ; 从section的尾部(section_start + section_size - 1)开始寻找,直到找到第一个0值,或者找到section头部位置
00437064 |. |75 03 ||jnz short 00437069
00437066 |. |4E ||dec esi
00437067 |.^|75 F7 |\jnz short 00437060
00437069 |> |8B9424 A80000>|mov edx, dword ptr [esp+A8] ; [esp+A8] is a constant, pushed in 44BCE8 to call sub_436FB0
00437070 |. |8BF9 |mov edi, ecx
00437072 |. |2BFE |sub edi, esi ; 这里就是我们的关键点了,将对齐后的SizeOfRawData 与上面寻找到的最尾部的0做差,就是cave的大小了,结果放在edi寄存器中作为直到后面处理,以及输出
00437074 |. |83C2 08 |add edx, 8
00437077 |. |3BFA |cmp edi, edx
00437079 |. |0F86 85010000 |jbe 00437204
上面通过计算得到的值,在后面又做了一个-8操作,才是最终真正输出的cave size,至此也弄清楚了整个cave 查找的方法了,CCDebuger前辈的PEid中汉化的注释并不正确,不是所谓的搜索全0处,而是搜索section的0结束处,一直到section最后文件块的结束处的大小。
附件是分析的样本PEid。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)