使用x64dbg脱壳之开源壳upx
引言
大家好我是蓝铁,你们的老铁,^_^
我们知道在分析病毒的时候,最常见的一种壳就是upx,使用upx的好处就是压缩率还不错,可以让原程序缩小一倍,便于在网络中传输。本节就是以开源壳upx为例讲解x64dbg中的脱壳方法。
脱壳中的一些常见概念
首先我向大家介绍一下脱壳相关的概念。
- 壳
外壳,英文为shell,在黑客技术中,一种比较高超的加密技术,对可执行文件(例如windows下的exe文件、Linux下的elf文件)进行加密压缩处理的技术。
一般对可执行文件加壳的目的有三:
① 软件加壳,保护数据、防止破解
② 外挂加壳,保护数据、防止破解
③ 病毒加壳,防止被查杀。
常见的壳分为压缩壳和加密壳两种,upx属于压缩壳。 - 脱壳
对加密处理的文件进行反分析,将已有的保护外壳去掉的过程。 - OEP
程序入口点,程序最开始执行的地方。 - 原始OEP
当程序加壳之后,壳会修改程序入口点,会先执行壳代码,会将原程序的入口点隐藏,这里我们把原程序的入口点称为原始OEP。 - dump内存
将内存中的数据或代码转储(dump)到本地 - IAT
导入地址表,windows下可执行文件中文件格式中的一个字段,描述的是导入信息函数地址,在文件中是一个RVA数组,在内存中是一个函数地址数组。(关于PE文件相关的知识可以通过搜索引擎查阅一下) - 修复IAT
脱壳中比较重要的一步,不论是压缩壳还是加密壳,在脱壳过程中都需要修复IAT,因为脱壳时会将内存中的数据转储(dump)到本地,保存成文件,而IAT在文件中是一个RVA数组,在内存中是一个函数地址数组。我们需要将转储出来的文件中的IAT修复成RVA数组的形式,这样程序才算是恢复。 - 脱壳的环境
这个单独出来说,主要原因就是不同的系统脱壳时遇到的问题可能是不一样的,因为脱壳时要修改IAT,而不同系统中同一个模块的API导出的顺序是不一样的,所以修复时一般都会出现点问题。因此,我建议脱壳的环境应该是在32位系统的虚拟机中,以下的所有操作应该在32位系统的虚拟机中操作,64位系统下可能会出现意想不到的问题。
脱壳中的一些方法
脱壳算是软件逆向技术中一种比较难的技术,方法也有很多,我在这里先介绍有代表性的方法:
单步跟踪(踏踏实实法)
一步一步分析每一条汇编指令,吃透每一行汇编背后所代表的意思,将壳代码读懂,从而找到原始OEP然后脱壳。这种方法是最锻炼人的,也是最难的。即使一个有经验的逆向分析者在对一个陌生的加壳程序分析时也需要花费很多时间,但这就是逆向工程的魅力,挑战自我,挑战才能突破。一般这种方法是在我们学习逆向工程时或是不能使用技巧时才会用的方法,耐心是这个方法的关键。
平衡堆栈(又称ESP定律,技巧法)
一般加壳程序在运行时,会先执行壳代码,然后在内存中恢复还原原程序,再跳转回原始OEP,执行原程序的代码,我们可以把壳代码理解为一个大的函数,既然是函数,那么进入函数和退出函数时,堆栈应该就是平衡的,基于这样的一种特性,我们可以在壳代码操作了堆栈之后,对堆栈设置访问断点,然后让程序跑起来,当程序暂停的时候,就是壳代码即将执行完的时候,然后在其附近单步跟踪,就可以找到原始OEP了。这种方法比较适用于upx这种只对代码和数据压缩了的壳,如果还对代码加密了,那么就不是太好找了。加密的话就需要结合单步跟踪法。
脱壳三步法
不管是哪种脱壳方法,都需要遵循脱壳三步法,脱壳三步法分为以下三步:
① 寻找原始OEP
这一步骤的主要作用就是要确定原始程序代码到底在哪里,能找到原始程序的代码,说明壳代码执行完了,我们只有找到原始OEP才能进行下一步的动作。
② dump内存到文件
当我们找到原始OEP,调试运行到原始OEP时,只要代码被还原,我们就可以在这个地方进行dump内存,将内存中被还原的代码和数据抓取下来,重新保存成一个文件,这样脱完壳时,我们就可以用静态分析工具分析程序了。
③ 修复文件
这一步主要就是修复IAT,对从内存中转储到本地的文件进行修复。
下载upx与使用upx对文件加壳
下载upx
upx壳的官网是:https://upx.github.io/
下载最新版的upx:https://github.com/upx/upx/releases/tag/v3.94
本次实验使用的是windows版的upx,所以下载的版本是:
使用upx对文件加壳
下载完之后,解压开可以找到upx加壳的主程序upx.exe
,我们可以对任意程序进行加壳,可以使用命令行进行加壳。
以VC6.0编写的一个程序为例,对其进行加壳,加壳之前我们先使用x64dbg查看一下其OEP:
可以观察一下OEP有什么特点?
然后我们使用命令行对vc6.0的demo程序加壳
可以看到,加壳之后,文件大小从200多K变成了90多K。
再使用x64dbg观察加壳后程序的OEP:
可以发现,加壳之后的程序和加壳前真的很不一样,所以我们才要进行脱壳。
使用x64dbg脱壳之寻找OEP
寻址OEP的方法有多种,这里我们使用前面介绍的平衡堆栈法,我们使用x64dbg调试加壳后的程序,观察oep处的指令,可以发现OEP的第一条指令是pushad
,其作用一般是保存寄存器环境,可以将8个通用寄存器都压入堆栈,那么我们可以单步程序(F8),执行pushad
,这样堆栈就会发生变化,而后我们可以在堆栈栈顶处,即ESP指向的内存处,设置硬件访问断点,
我们可以在x64dbg中的寄存器窗口处,选中esp,然后右键选中在内存窗口中转到
然后在内存窗口处,右键设置硬件访问断点
之后,可以使用快捷键F9运行程序,程序会再次暂停下来,我们观察附近指令
可以发现我们暂停的指令上方就是popad
指令,一般遇到popad
指令,就离原始OEP不远了,因为执行完popad
指令意味着壳代码告一段落了。
继续单步,可以发现一个比较大的jmp跳转。这个jmp其实就会跳转到原始OEP。
实际上在分析时,我们是先单步到jmp跳转到的代码进行观察之后得出是否是原始OEP的结论的,这个部分需要我们对未加壳程序的OEP要有所了解,比如VC6.0的程序一般OEP最开始的一个API调用是GetVersion
,看OEP见到GetVersion
就如见到了vc6.0程序。
我们单步到跳转之后的代码处,409376
,这个地方就是原始OEP,而后我们要做的就是在这个地方进行dump。
使用x64dbg脱壳之dump内存
当我们找到原始OEP时,我们运行到此处,然后对当前程序的内存进行dump,需要使用x64dbg中的一个插件:Scylla。
打开插件Scylla
使用快捷键Ctrl+I
可以直接打开插件,进行dump。
Scylla的使用
注意,dump时需要填写正确的原始OEP地址,然后点击Dump按钮保存文件。
保存完毕之后,最后就是修复文件了。
使用x64dbg脱壳之修复文件
修复文件,本质上就是修复IAT,所以还是使用插件Scylla,先对当前程序的IAT进行扫描,如果能找到就可以使用工具修复,不能就需要手动修复。
为了能更好的获取IAT,我们需要对插件Scylla进行设置。
- 打开设置
- 设置高级扫描
- 查找IAT
- 获取导入表
- 修复上一节的dump文件
- 最终生成文件
最终会生成的文件是:vc6.0_demo_dump_SCY.exe
当运行时发生了一个问题,出现了访问异常
使用x64dbg单步跟踪,发现出现异常的地方
根据这个地址以及经验可以猜出,可能是这个地址所在的区段的属性不可写。使用LordPE,可以查看区段属性,如下图,果然是没有可写的属性。
将其可写属性打钩,保存文件即可添加属性,这样就完成了脱壳。
测试结果
最后我们可以运行已经脱壳修复完成的程序,先使用PEID查看一下区段,然后运行。
总结
脱壳这门技术在任何一个平台下都是比较难的技术,想要练成这项技术,除了对汇编语言要非常熟悉之外,还需要对可执行文件的格式很熟悉,并且对可执行文件的加载流程还有一定认识,比如在这一节中,我们讲到了一个名词,修复IAT,只有对可执行文件加载流程理解了,修复IAT才会真正理解。综上,脱壳技术的练习是综合练习,我们从Upx开始,一步一步去分析现在主流的病毒混淆壳吧!希望大家可以跟着我尝试练习^_^。