大家好,又见面了,我是你们的朋友全栈君。
之前看到的UPX脱壳文章都只是教了方法,对UPX的原理少有提及。看了《逆核》的UPX脱壳一章后,俺尝试把UPX脱壳与PE文件结构的知识结合起来整理了一些(也可联系压缩器Paker的知识)。 分析样本来自BUUCTF:Reverse题目“新年快乐”(本文将寻找样本的OEP)
可能会用到的几个跟踪命令:
命令 | 快捷键 | 作用 |
---|---|---|
Animate Into | Ctrl+F7 | 反复执行Step Into命令(画面显示) |
Animate Over | Ctrl+F8 | 反复执行Step Over命令(画面显示) |
Trace Into | Ctrl+F11 | 反复执行Step Into命令(画面不显示) |
Trace Over | Ctrl+F12 | 反复执行Step Over命令(画面不显示) |
停止跟踪 | F7 |
Animate:执行时画面会跟着光标移动一直显示; Trace:会在事先设置好的跟踪条件处停下,并生成日志文件。 跟踪命令适合用在大型代码,一好处是容易发现短循环。 我分析时只是使用了F8/F7/F4。
一般情况下,分析压缩后的UPX壳,可以看到第一个节区的名称为UPX0,SizeOfRawData
=0,而VirtualSize
=1000h(大于0),第二个节区UPX1,它的PointerToRawData
和第一个节区的相同。也就是说,第一个节区在文件中是不占用空间的,只有运行时才分配大小。
其实,第二个节区含有解压代码和将被解压的代码(压缩前的代码),在加载过程中,第二个节区的解压代码运行,将被解压代码解压到第一个节区。最后到达EP执行代码。
4. F8一会可以在下面不远处发现一个循环
大致作用:从ESI(指向UPX1)中读取一个字节写入EDI(指向UPX0) 需要F4跳出循环的话记得先删除刚刚下的内存断点,继续F8
0040E3C2 5E pop esi ; 新年快乐.<ModuleEntryPoint>
0040E3C3 89F7 mov edi,esi //EDI=ESI ; 新年快乐.00401000
0040E3C5 B9 4A000000 mov ecx,0x4A
0040E3CA 8A07 mov al,byte ptr ds:[edi] //0101540A 取EDI的值进行对比
0040E3CC 47 inc edi ; 新年快乐.00401006
0040E3CD 2C E8 sub al,0xE8
0040E3CF 3C 01 cmp al,0x1
0040E3D1 ^ 77 F7 ja short 新年快乐.0040E3CA //判断取出来的值是否是E8/E9
0040E3D3 803F 00 cmp byte ptr ds:[edi],0x0
0040E3D6 ^ 75 F2 jnz short 新年快乐.0040E3CA
0040E3D8 8B07 mov eax,dword ptr ds:[edi] //需要修改的地址
0040E3DA 8A5F 04 mov bl,byte ptr ds:[edi+0x4]
0040E3DD 66:C1E8 08 shr ax,0x8
0040E3E1 C1C0 10 rol eax,0x10
0040E3E4 86C4 xchg ah,al
0040E3E6 29F8 sub eax,edi ; 新年快乐.00401006
0040E3E8 80EB E8 sub bl,0xE8
0040E3EB 01F0 add eax,esi ; 新年快乐.00401000
0040E3ED 8907 mov dword ptr ds:[edi],eax //处理后的新地址放入EDI
0040E3EF 83C7 05 add edi,0x5
0040E3F2 88D8 mov al,bl
0040E3F4 ^ E2 D9 loopd short 新年快乐.0040E3CF
40E3F6处执行后ESI = 401000(第一个节区),EDI = 40D000(第二个节区)
GetProcAddress可以根据函数名称获取地址:
按照书中的说法,此段程序会根据API名称,用GetProcAddress()函数获取API函数的地址,然后把地址输入到EBX指向的原IAT区域。
401280就是程序的OEP
本人是菜鸡,调试能力有限,如有错误的地方麻烦指出。
这篇文章非工具脱UPX壳,也涉及PE知识: https://my.oschina.net/u/1171187/blog/1143063 (侵权则删)
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/158192.html原文链接:https://javaforall.cn