欢迎大家访问我的原站!(乙太小屋)[https://www.oisec.cn/index.php/archives/520/]
title: 程序脱壳篇
date: {{ date }}
tags: [‘程序壳’,‘程序脱壳篇’]
categories: [‘程序壳’,‘程序脱壳篇’]
excerpt: 壳是最早出现的专用加密软件技术!程序的壳可以是开发者未来保护自己的代码不被借鉴、破解、逆向;也可用来病毒、木马、蠕虫隐藏恶意代码,不被杀毒如软件查杀!所以我们每次逆向破解的时候遇到带壳程序会非常头疼,这篇文章主要是有关程序脱壳的技术的讲解,内容详细,深入学习者一定不能错过,带你深入研究程序脱壳技术,一定有所收获!
声明
首先要感谢的是我在2022年为了进阶逆向工程学习这门技术的老师,是鱼C工作室的老师小甲鱼,先学会感恩,是为人处世的基本准则。这是我看了它的脱壳篇后的课后自己实践过一遍的笔记总结。
其次这篇文章我最早发布于我临时搭建的博客网站,随后发布现在才搭建的正式网站
最后,抄一百篇不如自己实践操作写一篇,尊重知识产权,写笔记辛苦,禁止转载!!!引用注明出处!!!写了几个月,因为都是实践过的,如今分享给大家学习,但是尊重劳动成果。
我是同一作者!
一· 什么是壳?
1 . 初步认识壳
历史:壳是最早出现的专用加密软件技术!
作用:可以是开发者未来保护自己的代码不被借鉴、破解、逆向;也可用来病毒、木马、蠕虫隐藏恶意代码,不被杀毒如软件查杀!
预习:vmp纯虚拟机壳,目前公认认为公认最强。
温馨提示:脱壳上瘾,可不要随意传播哦!
2 . 壳的执行过程
加壳压缩,壳先执行,还原源程序,执行源程序
3 . OEP(Original Entry Point)
1·EP(Entry Point)
EP(Entry Point),意即程序的入口点。而OEP是程序的原始入口点,一个正常的程序只有EP,
例如:C语言main函数、winAPI函数、VC编译的入口地址0x40000000
只有入口点被修改的程序(加壳等),才会拥有OEP。
2·OEP
OEP:(Original Entry Point),程序的原始入口点,软件加壳就是隐藏了EP, 只要我们找到程序的OEP,就可以立刻脱壳。
PUSHAD (压栈) 代表程序的入口点,POPAD (出栈) 代表程序的出口点,与PUSHAD相对应,一般找到这个OEP就在附近啦。
4 . 壳的装载过程
模拟windows加载器:
壳获取自己所需要使用的API地址、解压或解密源程序的各个区块、进行必要的重定位、跳转到程序原入口点(OPE)
5 . 压缩引擎
1·aPLib
- aPLib
低于64kb的文件压缩速度较快
2·JCALG1
- JCALG1
对大文件效果较好
3·LZMA
- LZMA
使用目的:开源,稳定
特点:保证压缩比的情况下解压速度一定要快
6 . 壳的分类
1·压缩壳
- ASPack
- UPX
- PECompact
2·加密壳
- ASProtect
- Armadillo
- EXECryptor
- Themida
- VMProtect 缺点慢
二· 实践是检验真理的唯一标准
接下来我们尝试破解这样一个老程序壳:
1 . 查看壳
1·PEiD查看
2·Exeinfo PE查看
咳咳咳,咋就是说,这找不到这个程序壳的特征码,咋们看不出什么来,试试别的,是个老古董壳!
3·脱壳后的程序查看
可见它是由VC5.0编译的程序!
可见无壳!
2 . OD脱壳
用od把程序打开,刚进来情况是这样的:
刚进来的时候是要提示是壳的EP,如果不是,要在选项/调制选项/事件中设置第一次暂停于主模块入口:
点击OD里面的M查看进程内存:
试着分析对比,可见代码的入口地址是0x00401000,按C回到查看汇编代码,我们现在的入口地址是0x46B6DE,可见它在包含SFX的地址0x46B00000中。这壳有这三个区块(资源、SFX、重定向)。.text段就是程序的代码段块!
接下来单步执行F8,到jmp的时候跳转到地址0x004271B0:
来到这大概这个样子,右键分析/分分析代码:
再看进程内存,可知0x004271B0是在包含代码地址0x00401000中,所以它开头是55/8BEC,我们就找到了OEP
在OD的plugin文件夹中加入插件重新打开:
右键选择插件dump debugged process把程序dump出来
如下设置:
确保勾选Fix Raw Size是修复壳进行的数据偏移,不勾选Rebuil lmport输入表不进行重定位!
点击dump,保存文件命名dumo1,回到上面脱壳后的查看!
3 . 删除壳,减小程序大小
目前查看dump1.exe文件属性/大小:
打开软件lordPE,然后点击PE Editor按钮,选择dump1,对照弹出窗口里面显示的出数据和OD里的进程内存对比,然后点击Sections按钮,发现多余的壳区块,通通右键选择wipe section header, 如果是灰色的,那么请确保程序先不处于运行状态。后点击Save按钮保存OK。
最后点击Rebuild PE再选择dump1打开:
咋就知道,程序大小最后变成了原本91%!
此时再用OD打开就发现壳已经无了:
4 . 根据堆栈平衡原理寻找OEP
1·示例:zip压缩壳
实例:看文件名可猜测这是一个压缩壳
用OD打开,发现刚进来第一句就是一堆跳转之中的一个jmp:
这里就是壳的模块入口了,F8执行,可见这格式像是VC程序的入口点:
可是这并不是OEP,这是为什么呢?原因我们来到进程内存来看:
程序目前运行地址在0x004682BC,这大概在进程内存的0x00465000到0x0046A000之间,并不是我们所想的在0x401000里面,所以它其实是一个伪造程序区块OEP的壳!
现在我们重新打开,从最开始就要注意观察ESP寄存器的变化:
刚开始是0012FFC4,当运行到
时ESP发生第一次变化,变成0012FFC0,我们要把鼠标放在ESP显示的数据上右键选择数据窗口中跟随:
然后就可以在数窗口看到0012FFC0,这是ESP所在的内存的位置,在该位置右键/断点/硬件访问/word都可以:
此时就设置好了硬件断点,此断点不一般,只能在/调试/硬件断点/查看到哦,也可在此修改,也可以看到一般它只有四个硬件断点,使用的CPU特殊寄存器下断:
F9直接运行到断点的位置
此时ESP的值是0012FFC4,刚好是刚才断点的位置!
这里可以记录一下,一般大部分的壳出现jmp eax都是跳转到OEP,但是这会出现一个明显的分界点,所以现在一些的强壳不会这样做!
可以看到EAX的值是004271B0,我们继续看到进程内存,它在0x00401000到0x0044B000之间的代码区块!
然后F8就进到了OEP:
此时就可以删除硬件断点,并进行dump操作!
也是先不勾选重定向,然后保存dump2!
此时打开lordPE,点击PE Editor按钮,选择dump2打开:
先点击Sections按钮把壳的区块删除。然后可以看到ImageBase:00040000正确。但是代码段的偏移Base0fCode:00065000指向的还是伪装壳的代码段,所以我们要把他修改偏移成1000,Base0fData:
改成4B000。,Save,OK保存!
会发现它脱完壳之后竟然变大了原来的85%,因为这是压缩壳!
接下来就可以根据retn所指向链接的黑线找到该区块的入口:
根据这个内存地址和连接指向的红线我们可以再往上寻找,发现就是回到了我们刚进来的一堆jmp的
地方:
也就是这样通过一连串的代码块的跳转对文件不同区块的压缩和加密!
2·作业
1.eXPressor壳
程序:
查看PEiD:
可见这是一个exPressor的壳!
OD打开程序,刚进来就在这个地址:
点击M查看进程内存:
根据经验,程序本来的.text代码块被壳包含在了0x0046B000开始的区段,达到了“壳里有肉,肉里有壳”的效果!
根据堆栈平衡原理(80%有效)找到OEP,这次进行dump操作的时候,一点要勾选上重定向输入表!
这样插件就会搜索重建输入表,搜索里面的jmp跳转到一些API,然后再把这些API依次组成一个输入表!保存为dump3
再用PEiD查看dump3.exe的时候,就原型毕露了,知道它是VC++5.0写的一个程序!
再用OD打开dump3.exe点击M查看进程内存:
发现它多了一个输入表!
我们在进行LordPE的时候修改偏移Base0fcode的时候,首先要看到dump3程序入口点地址:
对比上面的进程内存,可见他在0x401000开始的区段,所以Base0fcode要修改成1000!Save
在删除多余的区块的时候,备份一份,尝试后发现,只有.ex_cod可以wipe掉!
最后Rebuild PE选择dump3!
完成!
2.MEW压缩壳
程序:
查看壳:
这题只需要注意一点,像MEW系列壳除了可以使用堆栈平衡原理寻找OEP,记得分析代码;还可以在第一个区段网下滑找到retn加断点,F9运行到这F8,就到了OEP。应为这种壳,就是有个大区段,对软件进行解压缩,没有对它进行什么防护措施!
其他的都很简单,直接ReBulid PE就行,不用删除什么区块!
5 . .text区块加断点寻找OEP
程序:
查看壳:
该程序直接点击运行是没有任何问题的,但是一用OD运行就只能运行出一个空窗口,而且这时使用堆栈平衡法寻找OEP也是不管用,所以这个程序是具有反调试系统的,所以我们之能先一步一步走,看能不能找到关于OD的API函数,直到运行到:
IsDebuggerPresent是windows提供的对调试器判断的一个API
还有一种更快捷的办法,右键/查找/所有模块中的名称/往下滑就能看到IsDebuggerPresent:
双击就进了它的动态链接库:
在这里我们加断点,运行到这,然后按Alt+F9也可以来到差不多我们刚才单步走到的位置!
注意寄存器EAX,当能探测到调试器的时候eax=1,反之不能探测到则为eax=0
因此可见,该程序跳转就不能是实现了:
所以我们可以先修改,标准寄存器ZF位的值吧,也可以把它改为jmp,让其实现跳转,此时再按F9运行可见程序就能正常执行了!
此时就是去掉他的反调试功能了,在修改jmp的位置此时右键/复制到可执行文件/选择,然后保存文件,命名2。
继续用OD打开去掉反调试后的该文件2:
查看进程内存:
但是!后面发现,用堆栈平衡方式寻找OEP并没有用,甚至停不下!所以这里我们要用另外一种方法就是在.text区块加断点!因为壳不论怎么运行终究还是要回到源程序的代码段的,所以我们就直接在原程序代码段加断点:
00401000右键/在访问上设置中断,运行:
但是这并不是我们想要的地址,那由于运行过了,断点取消了,那我们再继续断继续走,多次后发现并不可以,但是我们看到了loopd:
可以看到这里灰线的箭头其实是多次循环回去了,所以我们先直接先让它跳出循环再说,在循环外的下一个地址加断/运行/取段,就来到了这:
此时再次00401000下断点/运行的时候,又是多次来到了这里:
可见这是又有一个循环的,那总是进去很多个循环怎么办,一个一个像上面那样吗?不!我们重新开始,可以Ctrl+F8,或者长按F8,先观察循环都在哪里,再直接找到循环的末端,通过上面的方式下断:
看就是这个灰色循环,目前我们已经断在外了,取断后,断00401000执行,然后我们就来到真实的OEP啦,右键/分析/分析代码:
然后我们就把它dump5吧:
接下来备份dump5就可以开始老的方法wipe去除多余的区块.Prt啦!
成功啦!