周笔记整理——脱壳
什么是壳?
壳是一种软件加密保护软件,它能够保护软件不被非法修改或编译的程序。
壳载入的过程
1、保存入口参数
保证外壳程序执行完后,能够跳转到正确执行的入口。
2、获取壳本身需要使用的API地址
3、解密源程序各个区块的数据
壳一般按区块加密,只有在执行时才能解密,解密完后,把区块数据按照区块的定义放在内存中合适的位置。
4、IAT的初始化
IAT(Import Address Table,导入地址表),用于存放调用的DLL或外部函数的实际地址
外壳程序有一个自建的输入表,PE装载器实现对自建输入表的填写。
5、重定位项的处理
为保证DLL每次运行都提供相同的基址,需要重定位的代码,这些代码存在于重定位表中。
6、Hook API
模仿Windows的工作流程,向输入表中填充相关的数据。
填充Hook API代码的地址,间接获取程序的控制权。
7、跳转到程序原入口点
原入口点(OEP,original entry point):这是程序真正的入口点,程序加壳就是隐藏OEP。
脱壳的步骤
两个关键点:
1、找到源入口点(OEP),以此分离壳和源程序
2、从入口点进入能够让程序正常的运行
具体步骤:
-
寻找OEP
-
抓取内存映像
什么叫抓取内存映像?
把内存指定地址的映像文件读出,用文件等形式等将其保存下来
为什么要抓取内存映像?
因为加壳的程序区块只有在内存中才会被解密,因此需在程序在内存执行时把必要的初始化数据保存下来。
何时抓取内存映像?
脱壳时需要在源程序没有运行起来之前,内存中“只有”外壳的初始化数据,这时抓取内存映像中的数据,有助于脱壳分析。
如何抓取内存映像?
工具:LordPE、PETools
- 重建输入表
什么是输入表? 前面已回答:外部调用的函数、DLL
为啥要重建输入表?
前面已分析(壳的加载过程):外壳程序加载时,需要初始化输入表,将外壳程序的输入表添加到源程序的输入表中,脱壳时,要将输入表恢复到为加壳的状态。
怎么重建输入表?
重建输入表的前提是获取输入表,需要了解输入表的结构,根据当前内存中的输入表,能够还原源程序的输入表。
一些加密软件为了防止输入表被还原:填充虚拟地址(HOOK API的外壳代码的地址)到输入表。因此,重建输入表的关键是如何获取输入表。
Windows加载器将PE文件映像到虚拟地址空间,建立输入表的过程如下,根据这个加载原理建立输入表(这只是个人的目前的观点,待后续补充)
- DLL文件脱壳
脱壳套件
0、识壳:PEID、Exeinfo PE
1、寻找OEP ——Ollydbg、dbg
2、转存映像文件——LordPE、ProcDump
3、重建DLL的输入表——ImportIAT
4、构造重定位表——ImportIAT修复即可
常用的手工寻找OEP的方法:
法1:手动分析法
1.用OD载入
载入的时候不分析代码,有壳就会出现这个,没壳的就不会。
2.单步向下跟踪F8
-
遇到程序往回跳的(包括循环),在下一句代码处按F4,或右健单击代码,选择断点——运行到所选位置。
-
绿色线条表示跳转没实现,不用理会,红色线条表示跳转已经实现。
3.如果刚载入程序,在附近就有一个CALL,F7跟进去,这样很快就能到程序的OEP 。
4.在跟踪过程中,如果运行到某个CALL程序就运行的,就在这个CALL中F7进入 。
5.通常在大跳转的附近就会出现OEP。
jmp XXXX
je XXXXXX
retn
法2:ESP定律
原理:ESP定律就是堆栈平衡原理。根据堆栈平衡原理,便可以通过内存断点,在入口点处,断在与入口点相对应的出口点处,而程序的出口点处一般就有个跳转指向OEP。
为什么ESP定律能够找到OEP呢?
在命令行下ESP的硬件访问断点,当ESP的数据发生变化时,就会引发中断,而ESP指向的是当前运行函数的栈顶,当进入OEP时,ESP的值必然会变化。因此,通过ESP的变化来定位OEP。
具体操作如下:
1.OD载入程序,F8步过,ESP变红。
2.选中ESP红色的数据,右键-》数据窗口跟随-》选中16进制数据,下硬件断点。
或者在命令行下断 dd [当前代码中的ESP地址]。
3.F9运行程序,来到跳转处,按下F8,到达程序OEP,脱壳 。
3.14内存溢出的原理](C:UsersmoheDesktop3.14内存溢出的原理.png)
覆盖的是ESP、EIP指针,具体的是如何实现跳转的呢?
法三: 内存跟踪
1:用OD载入
2:点击选项——》调试选项——》异常,把里面的忽略全部√上,CTRL+F2 重载程序。
3:按ALT+M,打开内存镜象,找到第一个 .rsrc, 按F2下断点, 然后SHIFT+F9运行到断点,接着ALT+M, 打开内存镜象,找到RSRC上面的CODE,按 F2下断点。然后按SHIFT+F9,直接到达程序OEP,脱壳!
why?
ps:为什么是第一个.rsrc,资源段包含了模块的资源信息,一个程序存在多个 rsrc.? why? 不同区段的rsrc.有什么不同?
//WINNT.H
typedef struct _IMAGE_RESOURCE_DIRECTORY {
ULONG Characteristics;
ULONG TimeDateStamp;
USHORT MajorVersion;
USHORT MinorVersion;
USHORT NumberOfNamedEntries;
USHORT NumberOfIdEntries;
} IMAGE_RESO
NumberOfNamedEntries和NumberOfIdEntries代替了指针,它们被用来表示这个目录附有多少入口,目录入口就在段数据之中的目录后边。
法4: 一步到达OEP
这个方法建立在很熟悉各种壳的入口特点的基础上,通过Ctrl+F查找关键词,然后按下F2,F9运行到此处
2.来到大跳转处,F8,脱壳。
附上常见的壳的入口特点
此段整理笔记来自http://blog.sina.com.cn/s/blog_414cc36d0100lde4.html
6种常见压缩壳:
UPX 0.89.6 - 1.02 / 1.05 - 1.24 (Delphi) stub -> Markus & Laszlo
ASPack 2.12 -> Alexey Solodovnikov
堀北压缩(KByS)0.28主程序脱壳
PECompact 2.x -> Jeremy Collake
PEncrypt 3.1 Final -> junkcodeeuk
北斗壳 Nspack3.7
1、UPX 0.89.6 - 1.02 / 1.05 - 1.24 (Delphi) stub -> Markus & Laszlo
006055B0 > 60 pushad
006055B1 BE 00605400 mov esi,STV.00546000 ; 这里使用ESP定律(具体操作见法2)
006055B6 8DBE 00B0EBFF lea edi,dword ptr ds:[esi+FFEBB000>
006055BC C787 C4201600 135F>mov dword ptr ds:[edi+1620C4],705D>
006055C6 57 push edi
006055C7 83CD FF or ebp,FFFFFFFF
006055CA EB 0E jmp short STV.006055DA
...... .............. ................
00605727 ^E9 28CFF5FF jmp STV.00562654 ;F8一次 直接到达OEP
0060572C 44 inc esp
0060572D 57 push edi
2、ASPack 2.12 -> Alexey Solodovnikov
0044B001 > 60 PUSHAD ; OD载入停在这里
0044B002 E8 03000000 CALL acerip.0044B00A ; ESP定律
0044B3B0 /75 08 JNZ SHORT acerip.0044B3BA ;ESP后 OD停到这里, F8一次
0044B3B2 |B8 01000000 MOV EAX,1
0044B3B7 |C2 0C00 RETN 0C
0044B3BA 68 BC1F4000 PUSH acerip.00401FBC
0044B3BF C3 RETN ; RETN之后 就跳到OEP
3、堀北压缩(KByS)0.28主程序脱壳
00401000 >/$ 68 71EA4A00 PUSH 堀北压缩.004AEA71 ; OD载入停到这里~
00401005 |. E8 01000000 CALL 堀北压缩.0040100B ; 这里ESP定律
F9 OD停到这里
004AEA71 B8 83EA0A00 MOV EAX,0AEA83 ; ESP停到这里
004AEA76 BA 00004000 MOV EDX,堀北压缩.00400000
004AEA7B 03C2 ADD EAX,EDX
004AEA7D FFE0 JMP EAX
004AEA7F B1 15 MOV CL,15
004AEA81 0000 ADD BYTE PTR DS:[EAX],AL
004AEA83 60 PUSHAD
004AEA84 E8 00000000 CALL 堀北压缩.004AEA89 ; F9一次到这里
F9后 循环返回到这里
00401005 |. E8 01000000 CALL 堀北压缩.0040100B
下面继续F9运行 (大约)15次时 OD有一次明显的时间间隔 然后停到这里
00432A71 B8 E5A70000 MOV EAX,0A7E5 ;停到这里
00432A76 BA 00004000 MOV EDX,堀北压缩.00400000
00432A7B 03C2 ADD EAX,EDX
00432A7D FFE0 JMP EAX ;到这里F7进
00432A7F B1 15 MOV CL,15
00432A81 0000 ADD BYTE PTR DS:[EAX],AL
00432A83 60 PUSHAD
00432A84 E8 00000000 CALL 堀北压缩.00432A89
进入后 (Ctrl+A 我这的OD需要分析下代码 )这里就是OEP
0040A7E5 /. 55 PUSH EBP ;OEP
0040A7E6 |. 8BEC MOV EBP,ESP
0040A7E8 |. 6A FF PUSH -1
0040A7EA |. 68 C0F54000 PUSH 堀北压缩.0040F5C0
0040A7EF |. 68 00BC4000 PUSH 堀北压缩.0040BC00 ; SE 处理程序安装
0040A7F4 |. 64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
4、PECompact 2.x -> Jeremy Collake
005B8B60 > B8 94426000 MOV EAX,MeteorNe.00604294 ; OD载入后停到这里
005B8B65 50 PUSH EAX
005B8B66 64:FF35 0000000>PUSH DWORD PTR FS:[0]
005B8B6D 64:8925 0000000>MOV DWORD PTR FS:[0],ESP ; 这里使用ESP定律
006042C3 83C4 04 ADD ESP,4
006042C6 55 PUSH EBP
006042C7 53 PUSH EBX
006042C8 51 PUSH ECX
006042C9 57 PUSH EDI
006042CA 56 PUSH ESI
006042CB 52 PUSH EDX
…………中间代码省略
00604350 5A POP EDX
00604351 5E POP ESI
00604352 5F POP EDI
00604353 59 POP ECX
00604354 5B POP EBX
00604355 5D POP EBP
00604356 FFE0 JMP EAX //跳向OEP
5、PEncrypt 3.1 Final -> junkcodeeuk
004E3600 > $ B8 00E05500 MOV EAX,MediaFix.0055E000 ;OD停到这里 F8单步一次
004E3605 . FFD0 CALL EAX ;F7跟进此Call
004E3607 2C DB 2C ;CHAR ‘,’
……
F7跟进CALL:
0055E000 /E9 25010000 JMP MediaFix.0055E12A //这里的跳转是实现的 F8一次即可
0055E005 |57 PUSH EDI
0055E006 |65:6E OUTS DX,BYTE PTR ES:[EDI] ; I/O 命令
……
F8一次后停到这里:
0055E12A 58 POP EAX ; 在该代码下方找到第一个retn语句 并下断
0055E12B E8 16030000 CALL MediaFix.0055E446
0055E130 48 DEC EAX
……中间无用代码省去
0055E2A7 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
0055E2A9 59 POP ECX
0055E2AA 5F POP EDI
0055E2AB 5E POP ESI
0055E2AC C3 RETN //这就是第一个retn语句,下断,运行停在这里。F8一次 即可通往OEP
004E3600 > $ 55 PUSH EBP ; OEP
004E3601 . 8BEC MOV EBP,ESP
004E3603 . 83C4 F0 ADD ESP,-10
004E3606 . 53 PUSH EBX
004E3607 . B8 F0324E00 MOV EAX,MediaFix.004E32F0
004E360C . E8 8737F2FF CALL MediaFix.00406D98
6、北斗壳 Nspack3.7
0057A867 > 9C PUSHFD ; OD载入后停到这里
0057A868 60 PUSHAD ; 我们在这里ESP定律即可
0057A869 E8 00000000 CALL 流星Nisy.0057A86E
0057AAD9 - E9 5AF5F8FF JMP 流星Nisy.0050A038 ; ESP定律后到这里 F8一次到OEP
0057AADE 8BB5 A1F9FFFF MOV ESI,DWORD PTR SS:[EBP-65F]
0057AAE4 0BF6 OR ESI,ESI
0057AAE6 0F84 97000000 JE 流星Nisy.0057AB83
0050A038 55 DB 55 ; CHAR ‘U’//这里Ctrl+A分析一下就可以把入口点一些特征字符串分析出来
0050A039 8B DB 8B
0050A03A EC DB EC
0050A03B 83 DB 83
0050A03C C4 DB C4
0050A03D EC DB EC
0050A03E 53 DB 53 ; CHAR ‘S’
Ctrl+A分析后的代码:
0050A038 . 55 PUSH EBP
0050A039 . 8BEC MOV EBP,ESP
0050A03B . 83C4 EC ADD ESP,-14
0050A03E . 53 PUSH EBX
0050A03F . 56 PUSH ESI