天天看点

脱壳简单总结

title: 脱壳

date: 2021-07-05 14:37:06

tags: RE

脱壳

1.概述:

1.壳:

一:加壳的目的:为了隐藏程序真正的OEP(入口点),防止被破解。

二:加壳软件是一种在编译好可执行文件之后,为了一些特定的需求,而做的一些事情,常见需求有:

​ 有一些版权信息需要保护起来,不想让别人随便改动

​ 为了让程序小一点,从而方便使用(把可执行文件进行压缩使用)

​ 给木马等软件加壳以避免杀毒软件

三:壳的加载过程:

​ 一般壳的装载过程:

​ (1)获取壳所需要使用的API地址

加壳后的文件,比未加壳的文件的输入表所引入的API少(甚至只有Kernel32.dll以及GetProcAddress这个API函数);壳实际上还需要其他API函数来完成一些事情,它为了隐藏这些API,一般只在壳的代码中用显式链接的方式来动态加载这些API

​ (2)解密原程序的各个区块(Section)的数据

壳一般是按区块加密的,那么解密的时候也是按区块解密,并且把解密的区块数据按照区块的定义放在合适的内存位置;如果加壳时用到了压缩技术,那么在解密之前,需要用对应的解压缩技术。

​ (3)重定位

​ (4)HOOK-API

程序文件中的输入表的作用:让Windows系统在程序运行时提供API的实际地址给程序使用,这个是在程序的第一行代码执行之前,由Windows系统完成的操作。

而壳一般是修改了原程序文件的输入表,然后仿照Windows系统的工作来填充输入表中的相关数据,在填充过程中,外壳就可填充HOOK-API的代码地址,从而可以间接获得程序的控制权。

​ (5)跳转到程序原入口点

这个时候,壳把控制权交还给原程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LrESqGdT-1625549563239)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210204180643199.png)]

2.什么是脱壳

找到程序真正的入口点(OEP)

3.分类:

简单分为两类 压缩壳 加密壳

压缩壳只是减少程序体积对资源进行压缩,加密壳是程序输入表等等进行加密保护

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fjfs4YOc-1625549563243)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210204172527304.png)]

4.脱壳工具

OD 找OEP 利用插件dump也可脱壳

PEID/Exeinfope… 查壳

脱壳简单总结

ImportREC 修复的

LordPE 脱壳的 win10 用不了

无法找到进程

脱壳简单总结

方法:右击相应的进程, 修复镜像大小, 完整转存.

脱壳简单总结

脱壳机也行

5.预备知识:

1.PUSHAD (压栈) 代表程序的入口点,

●把从EAX到EDI寄存器压入堆栈,保存现场(原来的值)

2.POPAD (出栈) 代表程序的出口点,与PUSHAD想对应,一般找到这个OEP就在附近

3.OEP:程序的入口点,软件加壳就是隐藏了OEP(或者用了假的OEP/FOEP),只要我们找到程序真正的OEP,就可以立刻脱壳。

6.步骤

1.识别壳

1.查壳工具

脱壳简单总结
脱壳简单总结

目的:知道壳的版本和编译程序的语言和编译器

2.通过入口特征和区段特征来识别

连接器版本

区段

壳的版本

结合OD看入口点特征 ,需要积累 多看

常见入口点特征:

https://blog.csdn.net/w_g3366/article/details/94595881

脱壳简单总结

判断是什么语言写的 可以查看区段和入口特征:

(1)VC6特点:入口点代码是固定的代码,入口调用的API也是相同的,其中有的push地址不同程序可能不同;区段有四个也是固定的.text、.rdata、.data和.rsrc。

(2)VS特点:入口点只有两行代码,一个CALL后直接JMP,第一个CALL进去后调用的API也是相同的;区段相对于VC6多了一个.reloc。

(3)易语言独立编译是调用VC的链接程序编译的,所以从区段和入口代码特征和VC相同

(4)非独立编译比较容易识别,入口特征和模块特征都有krnln.fnr

入口特征可以通过OllyDBG载入获得,载入后可以按一下“Ctrl+A”,让OD分析一下代码,就可以把入口点一些特征字符串分析出来,对于Shielden、Safengine、VProtect这类加壳程序都可以使用这种方法判断出来。

2.寻找oep
3.dump
4.修复文件
5.其他

importREC自动修复失败 手动修复IAT

7.方法相关知识:

(1)ESP定律法:

堆栈平衡

壳代码就像一个函数,进入时会开辟堆栈,保存寄存器环境,退出时会恢复堆栈,恢复寄存器。所以是堆栈平衡

所以 在壳代码操作了堆栈后对堆栈设置硬件访问断点,然后运行程序,当断点命中的时候,就是退出壳代码的时候

在其附近单步几次。就可以到达程序原始的OEP

❤ 第一次回到初始空栈状态就可以找到我们的OEP

●断点:运行到这个地方就zanting

●访问断点再次访问这个地方 就停

!!注意!!

普通软件断点用于代码段,硬件断点可以用于内存,堆栈上

(2)内存镜像法:

内存镜像法下的内存访问断点

内存断点一般分为 内存访问断点 内存写入断点

内存访问断点:当代码读取内存的数据的时候程序暂停

内存写入断点:当代码向内存中写入数据的时候程序暂停

❤总结:

第一次内存访问断点:为了让前面的部分先解码

第二次内存访问断点:为了让后面部分也解码完成,同时在访问代码段的时候,很可能会直接断在OEP处

8.脱壳方法:

方法一:单步跟踪法

1.用OD载入,点“不分析代码!”

2.单步向下跟踪F8,实现向下的跳。也就是说向上的跳不让其实现!(通过F4)

3.遇到程序往回跳的(包括循环),我们在下一句代码处按F4(或者右健单击代码,选择断点——>运行到所选)

4.绿色线条表示跳转没实现,不用理会,红色线条表示跳转已经实现!

●如果让程序向上跳转,则可能程序往反方向跳转,无休无止,也就找不到OEP)

5.如果刚载入程序,在附近就有一个CALL的,我们就F7跟进去,不然程序很容易跑飞,这样很快就能到程序的OEP

6.在跟踪的时候,如果运行到某个CALL程序就运行的,就在这个CALL中F7进入

7.一般有很大的跳转(大跨段),比如 jmp XXXXXX 或者 JE XXXXXX 或者有RETN的一般很快就会到程序的OEP。

 PS:在有些壳无法向下跟踪的时候,我们可以在附近找到没有实现的大跳转,右键–>“跟随”,然后F2下断,Shift+F9运行停在“跟随”的位置,再取消断点,继续F8单步跟踪。一般情况下可以轻松到达OEP!

❤ OD 不能在nop处下断点

方法二:ESP定律法

ESP定理脱壳(ESP在OD的寄存器中,我们只要在命令行下ESP的硬件访问断点,就会一下来到程序的OEP了!)

1.开始就点F8,注意观察OD右上角的寄存器中ESP有没突现(变成红色)。

(这只是一般情况下,更确切的说我们选择的ESP值是关键句之后的第一个ESP值)

2.在命令行下:dd XXXXXXXX(指在当前代码中的ESP地址,或者是hr XXXXXXXX),按回车!

3.选中下断的地址,断点—>硬件访问断点>WORD断点。

4.按一下F9运行程序,直接来到了跳转处,按下F8,到达程序OEP。

方法三:内存镜像法

1:用OD打开软件!

2:点击选项——调试选项——异常,把里面的忽略全部√上!CTRL+F2重载下程序!

3:按ALT+M,打开内存镜象,找到程序的第一个.rsrc.按F2下断点,然后按SHIFT+F9运行到断点,接着再按ALT+M,打开内存镜象,找到程序的第一个.rsrc.上面的.CODE(也就是00401000处),按F2下断点!然后按SHIFT+F9(或者是在没异常情况下按F9),直接到达程序OEP!

方法四:一步到达OEP

1.开始按Ctrl+F,输入:popad(只适合少数壳,包括UPX,ASPACK壳),然后按下F2,F9运行到此处

2.来到大跳转处,点下F8,到达OEP!

方法五:最后一次异常法

1:用OD打开软件

2:点击选项——调试选项——异常,把里面的√全部去掉!CTRL+F2重载下程序

3:一开始程序就是一个跳转,在这里我们按SHIFT+F9,直到程序运行,记下从开始按SHIFT+F9到程序运行的次数m!

4:CTRL+F2重载程序,按SHIFT+F9(这次按的次数为程序运行的次数m-1次)

5:在OD的右下角我们看见有一个”SE 句柄”,这时我们按CTRL+G,输入SE 句柄前的地址!

6:按F2下断点!然后按SHIFT+F9来到断点处!

7:去掉断点,按F8慢慢向下走!

8:到达程序的OEP!

❤ 这是总结出来的一种捷径.

原理就是. 最后一次异常之后就正常运行了. 这个正常运行必须是真正的OEP. 所以最后一次异常是最靠近的

方法六:模拟跟踪法

1:先试运行,跟踪一下程序,看有没有SEH暗桩之类

2:ALT+M打开内存镜像,找到(包含=SFX,imports,relocations)

内存镜像,项目 30

地址=0054B000

大小=00002000 (8192.)

Owner=check 00400000

区段=.aspack

包含=SFX,imports,relocations

类型=Imag 01001002

访问=R

初始访问=RWE

3:地址为0054B000,如是我们在命令行输入tc eip<0054B000,回车,正在跟踪ing。。

PS:大家在使用这个方法的时候,要理解他是要在怎么样的情况下才可以使用

方法七:“SFX”法

1:设置OD,忽略所有异常,也就是说异常选项卡里面都打上勾

2:切换到SFX选项卡,选择“字节模式跟踪实际入口(速度非常慢)”,确定。

3:重载程序(如果跳出是否“压缩代码?”选择“否”,OD直接到达OEP)

ps:这种方法不要滥用得好,锻炼能力为妙。

这里最常用的ESP定律法(堆栈平衡原理),为什么说是ESP呢,当所有的寄存器都压入栈时(pushad),esp地址的是指向栈顶的地址的,当壳将执行权交给源程序时,esp地址要回到pushad之前的状态,这时就可以看到esp变化了,这就是源程序的入口点。(汇编,汇编,汇编!)

SFX:od程序自带的调试功能,用来寻找相应的OEP

使用注意:SFX使用的范围是当程序载入OD之后,入口点必须是代码段之外才可以使用

总结:

9.例子

upx壳

upx的工作原理其实是这样的:首先将程序压缩。

所谓的压缩包括两方面:

一方面在程序的开头或者其他合适的地方 插入一段代码。

另一方面是将程序的其他地方做压缩(也就是上面讲到的压缩)。压缩也可以叫做加密,因为压缩后的程序比较难看懂,原来的代码有很大的不同。

当程序执行时:实时的对程序解压缩。解压缩功能是在第一步时插入的代码完成的功能。

联起来就是:upx可以完成代码的压缩和实时解压执行。且不会影响程序的执行效率。

用upx压缩之后形式为:1–>2–>3–>4–>5–>6

最初代码的形式就应该是:7–>8–>9–>5–>6

执行时的形式变为:1–>7–>8–>9–>5–>6

● 1是upx插入的代码

●内存镜像法:

○需要读取2 3 4内存空间的代码,这个就会存在内存访问,然后把2 3 4 解压出来,就会有内存写入

○把2 看作代码段 3 看做数据段 4 看做资源段

○假设解码的顺序是按照2 3 4进行

,第一个内存访问断点,下在4 资源段处

当程序断下来的时候,说明代码段2和数据段3都解码完成了

1 7 8 4 5 6

○第二个内存访问断点 ,下在解压完成的代码段

为啥:第二次断下来的时候,4肯定解码完成了,这时候就完成了所有解码,

1 7 8 9 5 6

接下来就是找OEP了,OEP在代码段7中的代码中,所以我们下的是代码段的访问断点,达到OEP就会访问代码段,所以就可以找到OEP进而完成脱壳

手脱upx方法:

1.单步跟踪

2.一步到达(利用popad)

3.esp定律

4.内存镜像法

10.dump

利用od自带的插件ollydump完成脱壳

11.修复文件

用importREC修复导入表

前提(已经找到OEP)

1、脱壳在当前进程,如脱壳出来的程序不能运行,提示无法定位程序输入点,需要使用

importREC修复导入表。

2、打开importREC,选正ollydbg正在调试的进程,填入正确的OEP,点ITA AutoSearch,

如oep正确将得到正确提示,点GetImport,得到正确的导入表。点Fix Dump,选择脱壳

出来的保存文件,Done!(修正后的程序为原程序名后加一下划线,原程序不会被修改。)

12.emmm

如果直接Dump文件,修复导入表会发现程序仍然不能正常运行,那么他就不是一个单纯的压缩壳,壳中加密技术一般有IAT加密,混淆花指令,偷取OEP,反调试,代码虚拟化

importREC自动修复失败 手动修复IAT:

https://www.cnblogs.com/5315hejialei/p/7060315.html

原理:

程序的IAT是连续的排列的,所以我们只需要找到IAT的起始位置和末位置,就可以确定IAT的地址和大小。在压缩壳中,我们只要找一个调用系统的API的Call的地址,然后在数据窗口中查找,确定IAT起始和结束地址。然后在OD中手动修复

总结:

脱壳简单总结

手脱各种壳:

手脱 UPX 壳的捷径

1、直接ESP定律

2、用我们已开始提到的”关键提示“。

具体操作:OD载入程序后,直接Ctrl+F,输入 POPAD ;

点确定后 来到这个命令所在的位置。按F2,在这个地方下断;

再按F9(运行);

停止后,按F2取消刚才下的断点。再F8单步!

手脱 ASPCK 的壳

脱这个壳用ESP定律,还是相对快捷的。

可以用载入程序后,第二行(是一个CALL)那里面的ESP。 //多数程序这个壳的第二行都是一个CALL 在左OD左下角的命令行中,输入命令:hr ESP地址(如 hr 0012FFA4);

F9 运行。

然后从OD”调试菜单“中的”硬件断点“这一项将刚才下的断点删除,这点很重要!最后F8单步!

用内存镜像法手脱FSG 1.33 和 PCshrink 的壳

1、忽略所有异常

2、Alt+M 打开内存镜像,找到第一个 ”.rsrc“

3、F2(下断),F9(运行)

4、Alt+M 打开内存镜像,找到”Code“段;

5、F2(下断),Shift+F9【这点一定要记住,切记是 Shift+F9】运行;

6、先按F8,再按下F4,直接到达OEP

PEpack 1.0 和 WinUpack 0.37-0.39 和 RLPack v.1.14-16

用esp定律

手脱 JDpack 壳

脱这个壳推荐使用内存镜像法 。

手脱 PEDiminisher ;Dxpack 0.86 ;

32lite 0.03a ;PEtite 2.2 这几种壳的简单方法

脱PEDiminisher ;Dxpack 0.86 ;

这两种壳的时候,直接用之前讲到的ESP定律,即可完美脱壳。命令:【hr ESP地址】

**用ESP脱 32lite 0.03a 后 要注意的是,需要用 ImportREC 这个工具进行修复。**如:00410D50 在输入框中输入 10D50 就可以了【004舍去】

在用ESP定律脱 PEtite 2.2 的时候,推荐选择 Pushad 下面那行地址中的 ESP

手脱 Exestealth 2.72 的壳

看到这或许会沉迷与ESP定律当中,在这里提醒大家:Exestealth 2.72 的壳 用我们一开始提到的”懒方法脱壳“是最简单的;

手脱nspack(北斗)1.3 的壳

1、ESP定律,命令:hr ESP地址 【脱壳后程序不能正常运行】

2、用 ImportREC 这个工具进行修复,修复后程序正常运行。

另类方法脱 ASPack 2.12R 壳的技巧

Ctrl+S 搜索:retn 0C【retn和零C 中间有个空格】 找到后向下看,如下:

retn 0C push 0 //在 retn 0C 的下面 retn //在这个地方按 F2(下断) ;

F9(运行)

停止后按 一下 F8(单步);

再按一下 F7(跟进) 观看这看不懂?没关系,要是我,我也看不懂,所以我早有准备;

详细步骤,如下(这是某程序的一部分):

程序中断后来到这里: 0046B3B8 C2 oc00 retn 0C //开始F8(单步)

0046B3B9 68 64584500 push registra.00455864 //这里调用来自 00455864 (OEP) 0046B3C0 C3 retn //F7(跟进) 步入到OEP

注意:这种壳ESP不能直接脱。

脱FSG v2.0技巧

用od打开之后找到一个三跳转的地方,就是一个判断跳转语句跳过了一个jmp,用F4走到jmp的位置,单步就可以找到oep的位置,用lordpe转存之后在修复的时候还有个坑,fsg会在输入表的地方加一些无用的干扰,所以要手工定位,找到一个API,用d 地址的命令找到输入表开始的地方减去基址为rva,在判断一下大小,最后把无效函数去掉,在修复转存文件就可以了。

手脱PECompact 2.x

这个壳一般先给eax赋值然后压栈,那么一定会访问eax,所以我们运行到push eax下面,然后给eax里的地址下一个访问断点,接着不断地f9,会有一个jmp大跳转,单步跟过去就是oep

手脱KBys Packer

od载入,先走几步F8,发现了PUSHAD,这就可以用ESP了,再ESP下断点,F9一步,走到oep附近,F8,发现一个jmp大跳转,跟过去就是熟悉的入口指令,这就是oep了

re

继续阅读