- 壳的作用:
- OEP:Original Entry Point
- 壳的装载过程
- 压缩引擎
- 简单脱壳1
- 简单脱壳2:根据堆栈平衡原理寻找OEP(tuts4you-lena15 UnPackMe_EZIP1.0.exe)
- 简单脱壳3:根据堆栈平衡原理寻找OEP(UnPackMe_eXPressor1.3.0.1Pk.exe)
- 简单脱壳4:根据堆栈平衡原理寻找OEP(UnPackMe_eXPressor1.3.0.1Pk.exe)
- 脱壳5:代码段下断点寻找OEP(UnPackMe_NoNamePacker.d.out.exe)
- 重建输入表
- HOOK API
- FSG压缩壳和ImportREC工具(修复IAT)的使用
- UPX和WinUpack压缩壳的使用和脱法
- 常见的恶意代码加壳特征
- 常见壳
- 内存dump
- 穿山甲双进程壳
- 预备知识
- 查找OEP的方法总结
- 手脱UPX
- 手脱ASPack和变形(检查是未知壳)
- 手脱PECompact
- 手脱nspack(北斗)和PEncrypt1
- 手脱Yoda’s Crypter(加密壳,重点是修复)
- 手脱tElock(加密壳,重点是修复)
- 手脱PETIE
- 手脱FSG2.0
- ESP定律(堆栈平衡原理)
- 内存断点法
- 附加数据的处理
- 程序自校验的解除
- 手脱EncryptPE+附加数据处理
- 手脱穿山甲(Armadillo)1.xx~2.xx 单进程
- 手脱穿山甲 4.xx(Armadillo)双进程(太复杂没看完。。。)
- 破解工具介绍
- 如何区分加密壳和压缩壳
壳的作用:
- 加密:ASProtect、Armadillo(穿山甲)、EXECryptor、Themida、VMProtect
压缩:ASPack(win32)、UPX、PECompact
OEP:Original Entry Point
-
壳的装载过程
获取壳自己所需要使用的API地址
- 解压、解密原程序的各个区块
- 进行必要重定位
-
压缩引擎
aPLib
- JCALG1
-
简单脱壳1
单步走,跳到OEP时没有正确分析为代码:右键->分析->分析代码
- 接着用脱壳插件OllyDump,
- 把壳删除,减少空间。使用LoadPE,和OD的M地址对应,点击section,右键wipe,save->ok->rebuild pe
简单脱壳2:根据堆栈平衡原理寻找OEP(tuts4you-lena15 UnPackMe_EZIP1.0.exe)
- OD载入程序,发现很多跳转,如果跟随进去长得都像OEP的形式,但是要注意M窗口代码段的地址
- 发现这个跳转在壳的代码段
- 根据堆栈平衡原理寻找OEP,关注ESP的值,跳转到壳的代码后,单步运行,只要ESP的值改变(第一次变化就动手),就在它对应的内存处下硬件断点
- F9运行,断下到 jmp ax,这通常是跳转到OEP的特征,这里eax=e4271B0,在主程序的代码段,寻找正确
- 删除硬件断点(在查看的地方删除),在OEP处右键使用OllyDump,保存
- 使用LordPE(PETools)修复,把壳的section删除后体积变大了,因为原壳是压缩壳
- eXPressor 1.3.0 -> CGSoftLabs壳
- OD打开,M窗口查看section分布,发现壳和原程序代码段混在一起,当OEP在401000~46A000时说明找到了,离开了壳的区域。
- 单步执行,当esp改变时在对应内存区域下硬件断点,F9执行,断在OEP处
- ollyDump转储,脱壳成功(因为选择了重建输入表,新程序中增加了.idata段)
简单脱壳4:根据堆栈平衡原理寻找OEP(UnPackMe_eXPressor1.3.0.1Pk.exe)
- MEW 11 1.2 -> NorthFox/HCC壳(压缩壳)
- 方法一:堆栈平衡
- 方法二(MEW系列通用)
- 起始在entrypoint先jmp
- 在jmp到的地址向下寻找到retn,下断点,F9运行。断下后F7到OEP。
- ollydump重建导入表,转存
脱壳5:代码段下断点寻找OEP(UnPackMe_NoNamePacker.d.out.exe)
- PEID查壳,未找到信息
- 程序具备反调试(win API),单步跟踪找到或者右键查找所有模块名称
- 在API函数处下断点,F9运行
- 在API地址处按ALT+F9到了调用它的程序领域中的下一条指令,查看这里eax的值,这是
isDebugPresent函数的返回值,在这个je处控制跳转(1or1=1,0or0=0),改成jmp去掉调试器检测。
- 接着开始寻找OEP,在.text段下断点,运行
- 断下的位置仍不在.text段,只是访问到了代码段
- 跳过loopd后,继续在.text处下断点,断到新的位置,发现还是对代码段的访问
- 多次观察循环,找到循环末端,反复下断,找到.text内的地址。Crtl+F8(自动步过)跟踪执行,F12暂停,遇到循环手动略过。直到遇到一个持续循环,F4走到retn后,在再M窗口的.text出下断点,F9运行,断在了OEP。
- ollydump转存,.Prt节可以删掉
重建输入表
- 输入表:IAT(import address table)
- 记录应用程序需要调用的API函数在动态链接库里的具体地址,由windows加载器加载可执行程序的时候填充
- 如何填充
- IMAGE_IMPORT_DESCRIPTOR -> INT -> IMAGE_IMPORT_BY_NAME
IAT是如何被填充和工作的(举例)
调用Messagebox函数
打开M窗口,双击PE头,关注imagebase(基址:0x400000)和import address table(IAT的RVA:0x402050)
- 查看IAT地址处(crtl+g跳到0x402050),使用analysethis插件分析该地址处汇编
- 有两个输入表,一个给kernel32,一个给user32
- 0x402098为IMAGE_IMPORT_DESCRIPTOR中的第0项OriginalFirstThunk的偏移地址,再找到INT,进而找到API name
- 回到IMAGE_IMPORT_DESCRIPTOR(0x402050)处,第五项为FirstThunk,指向IAT的偏移地址。跳到0x40200c,是dll中API加载的地址。最后将上面得到的名字和该地址挂钩。
HOOK API
- 壳模拟加载器加载原程序,因此加壳后的IAT是壳用hook API技术替换后的地址(间接调用)
FSG压缩壳和ImportREC工具(修复IAT)的使用
- FSG压缩壳入口点典型形式:xchg
- 复杂:F8单步,遇到向上的跳转直接F4跳过
- 新方式(自解压): 选项->调试选项 ->SFX->字节方式跟随真正入口,重新载入程序,停在OEP。
- FSG壳会对IAT进行破坏,使用OllyDump时先不用重建导入表
- 使用importREC修复IAT(RVA中填充的一定是包含数据区所有内容,不能只有名字),size要覆盖这一块所有内容,大点没关系
- 点击获取输入表,发现很多Invalid,点击显示无效函数(FSG壳捣鬼,加载器加载时会报错,地址不合法
- )。把不合法的都删掉,点击修复转存文件。
- 成功脱壳并能正确执行,因为修复了很多空白的地方,脱壳后体积增大
UPX和WinUpack压缩壳的使用和脱法
- UPX
- 使用文件为,查壳
- 堆栈平衡脱壳,ollydump,不重建IAT
- ImportREC修复
- WinUpack
- 堆栈平衡脱壳,ollydump,不重建IAT
- ImportREC修复
常见的恶意代码加壳特征
- 程序中导入函数很少,导入函数仅有LoadLibrary和GetProcAddress时,应该引起特别注意
- 当时用OllyDbg打开程序时,会有程序可能被加壳的警告
- 程序的节名中包含某款加壳器的标识
- 程序拥有不正常的节大小,例如.text节的原始数据大小为0,但虚拟大小非零
常见壳
- 压缩壳
- UPX
- ASPack
- PECompact
- FSG
- 加密壳
- Themida
- ASProtect:压缩、加密、反跟踪代码、反-反汇编代码、CRC校验和花指令等
- VMProtect:难破解,影响速度
内存dump
- 如果不能自动脱壳,则另一种较好的策略是使用ProcDump工具从内存中转储不在进行调试的进程。ProcDump是微软提供的一个工具,用来转储一个Windows进程的内容。设计他的目的是与调试器一起工作,但是它本身不是调试器。ProcDump的最大优点是在不停止进程或者调试进程的情况下,转储进程中的内存。这对转储那些使用了反调试机制的壳来说,非常有价值。甚至当不能调试一个可执行文件时,仍可以使用ProcDump工具转储正在运行可执行文件的脱壳内容。这个过程并不能完全恢复可执行文件,但那时他能让我们在代码上使用Strings工具并做一些分析。
穿山甲双进程壳
- OD进程附加功能
预备知识
- PUSHAD:压栈,代表程序入口点
- POPAD:出栈,代表程序出口点
查找OEP的方法总结
1、单步跟踪法
- OD载入,点“不分析代码”
- 单步执行(只向下),向上的F4跳过(注意nop,有nop在下一行F4)
- 如果刚载入程序在附近就有一个CALL(近CALL),F7跟进去(否则程序会跑飞)
- 跟踪的 时候如果运行到某个CALL程序运行不停下,就在这个CALL中F7进入
- 有大跳转(大跨段):jmp、je、retn等说明很快会到OEP(附近可能有popad)
!注意!:如果无法向下跟踪时,可以在附近找没有实现的大跳转,右键->跟随,F2下断,Shitf+F9运行停止在“跟随”的位置,再取消断点,继续F8单步跟踪
2、ESP定律法
F8,观察ESP是否改变(红色)【也就是说选择的ESP是关键句之后的第一个ESP值】
- 关键句:PUSHAD、POPAD等
- 在esp对应地址的数据窗口下硬件断点
F9运行来到跳转处(-> 删除硬件断点),F8到达程序OEP
3、内存镜像法(Alt+M)
OD:选项->调试选项->异常,选择忽略全部,Ctrl+F2重载下程序
- Alt+M打开内存镜像,找到程序的第一个.rsrc,按F2下断点,按Shift+F9运行到断点
Alt+M打开内存镜像,找到程序的第一个.rsrc上面的.text(.code:delphi),按F2下断点,按Shift+F9运行到断点,直接到OEP
4、一步到达OEP(UPX,ASPACK)
Crtl+F输入:popad
- Crtl+L:查找下一个popad,找到最合适的,下断点按下F2,F9运行到此处(开头是PUSHAD的情况)
-
5、最后一次异常法(适用加密壳)
OD:选项->调试选项->异常,选择不忽略异常(勾都取消),Ctrl+F2重载下程序
- 一开始就是一个跳转,按Shit+F9,知道程序持续运行,计算这中间异常暂停的次数,记为m
- Crtl+F2重载程序,按Shift+F9(按m-1次)
- 在OD堆栈窗口看到有一个“SE句柄“,这时候按Ctrl+G,输入SE句柄前的地址
- 按F2在这个地址处下断点,Shift+F9运行到该断点
- 去掉断点,F8向下执行
-
6、模拟跟踪法(自解压?)
F9之后能直接执行,说明没有SEH暗桩,如果运行后中断或报错,说明存在SEH暗桩。该方法要在没有暗桩的情况下使用。
- Alt+M打开内存镜像,找到(包含=SFX,imports,relocations)的段
如果这个段的地址为0054B000,在命令行输入tc eip<0054B000,回车,正在跟踪ing...
7、“SFX”法
设置OD,忽略所有异常(都打上勾)
- 切换到SFX选项卡,选择“字节模式跟踪实际入口(速度非常慢)“,确定。
- 重载程序(如果弹框“是否压缩代码“,选择“否”),OD直接到达OEP
手脱UPX
- 单步
- esp定律:硬件断点(别忘了删除)
- 基本都可以
手脱ASPack和变形(检查是未知壳)
- 单步(进入有近call,F7)
- esp定律
- 变形:多次使用脱壳方法(壳外面套着壳)
手脱PECompact
PECompact 1.xx
- 单步
- !注意!:如果无法向下跟踪时(F4跑飞) ,可以在附近找没有实现的大跳转,右键->跟随,在跟随到的位置F2下断,Shitf+F9运行停止在“跟随”的位置,再取消断点,继续F8单步跟踪(大跳转可能有多个,多次重复这个操作)
- ESP定律
-
PECompact 2.xx-3.xx(跳过壳的加密)
选择忽略所有异常(勾都打上)
- 下断点:BP VirtualFree。运行,中断后取消断点,Alt+F9运行
- Ctrl+F,查找“push 8000”特征码,在这个段的retn处下断点,F9运行
- F8单步,遇到远j**m**p跳转到OEP,发现是原位置,但是解密完成!!
手脱nspack(北斗)和PEncrypt1
nspack <3.3
- ESP定律
巧方法(适用于北斗3.3以前的)
最后一次异常
手脱Yoda’s Crypter(加密壳,重点是修复)
- 方法一:内存镜像法
- 修复IAT
- shift选中无效指针,在importREC中右键跟踪等级2
- 也可以手动找(反汇编能看到)
- 方法二:
- 先忽略除了内存访问外的所有异常
- F9运行
- 找到右下角SE句柄对应的地址,下断点
- shift+F9运行中断,取消断点
- 单步执行,注意寄存器变化,会包含OEP
手脱tElock(加密壳,重点是修复)
- 最后一次异常
- 模拟跟踪法
- 先结合最后一次异常法
- 内存镜像法
- 修复
- 存在大量无效指针
- 右键跟踪等级三会卡死
- 在OD外部再打开一个加壳程序,ImportREC选中,在ImportREC中一部分一部分的修复(麻烦)
- 还无效的手动识别(反汇编能看到)
手脱PETIE
- ESP定律
- 关键句:pushfw pushad
- 对应:popfw popad
手脱FSG2.0
- 特点:xchg
- 定位后下硬件断点,防止壳检测到
- 手动查找IAT
- 找到IAT的RVA和size
- 通过call API找到IAT表,向上找到IAT开头的位置就是RVA(再往上都是0,区块分界)
- size就是找到下面全零的地址减去开始的(或者直接在importrec里size输入1000,再把垃圾指针删掉)
- 找到IAT的RVA和size
ESP定律(堆栈平衡原理)
- pushad:把所有寄存器压栈
- popad:寄存器出栈
- 下断点就是找恢复堆栈平衡的时候
- 适用范围:压缩壳更适用
内存断点法
- 因为.text段在.data段之前被壳解码
- 可以一次或多次在.data下内存断点,中断时表示.text段已经解压完毕,这时候再在.text下断点,找到OEP
附加数据的处理
- 脱壳后关键数据也被脱掉,程序无法运行
- 使用winHex打开加壳程序,拉到末尾向上找到全部为0的地方(或者通过计算出来后面区段的起始位置)
- 选择0后面的所有内容,复制选块
- 打开脱壳后修复的文件,拉到最后面,复制刚才的内容
程序自校验的解除
- 方法:
- 一个OD找到OEP,dump下来后
- 再打开一个OD,载入脱壳修复后的程序
- 两个OD都在CreateFIleA下断点,中断后都Alt+F9返回,F8通过对比跟踪找到自校验(关键条件跳转)
- 校验点
- 大小
- 类型
- 时间
- CRC
- 校验码
手脱EncryptPE+附加数据处理
- 忽略所有异常
- 手动添加两个异常(否则会提示异常无法继续调试)
- 0EEDFADE
- C0000000(IVALID HANDLE)
- esp定律+内存断点
- 添加附加数据
手脱穿山甲(Armadillo)1.xx~2.xx 单进程
- 先确定是不是单进程(进程控制器)
- 两次断点法
- bp GetModuleHandleA/hr GetModuleHandleA/bp GetModuleHandleA+5/hr GetModuleHandleA+5
- bp GetCurrentThreadId
- 下断点1,shift+f9,到一次大时间缓冲后,取消断点,Alt+F9返回,找magic_jmp(je、大跳转、LoadLibrary下面的je)
- 把je改成jmp
- 下断点2,shift+f9,到一次大时间缓冲后,取消断点,Alt+F9返回,F8单步,call edi找到OEP
手脱穿山甲 4.xx(Armadillo)双进程(太复杂没看完。。。)
- 查看有多个同名进程(LoadPE)
- 尽量下赢家断点(he)
- 注意返回的时机!!
- 返回的地址是系统领空
- Shift+F9过程中找到时间缓冲比较大的
- 下断点:bp OpenMutexA,F9提示异常,添加到异常里‘
- Shift+F9,断下
- 搜索00401000,到空数据,打补丁(好多代码哦),右键新建EIP
破解工具介绍
PEiD
如何区分加密壳和压缩壳
- 普通压缩壳OD调试时没有异常
- 加密壳全部有反跟踪代码,会有很多SEH陷阱,使OD调试时产生异常