壳的机制以及脱壳技术

news/2024/10/25 20:24:59/

0 壳的概念

  1. 壳是用来保护计算机软件不被非法修改或编译的程序;
  2. 其附加在原始程序上,通过Windows加载器载入内存后,先于原始程序执行以得到程序控制权,在执行过程中对原始程序进行解密、还原,还原后把控制权还给原始程序,执行原来的代码;
  3. 由于壳能保护自身代码,许多木马和病毒都喜欢用壳来保护及隐藏自身;
  4. 对于一些流行的壳,杀毒引擎能先对目标软件进行脱壳,再进行病毒检查。对大多数私人壳,杀毒软件不会专门开发解压引擎,而是直接把壳当成木马或病毒来处理;
  5. 处于不同的目的,壳可以分为压缩壳(减小软件的体积)和加密壳(使用各种反跟踪技术来保护程序不被调试、脱壳等)。

1 壳的加载过程

1.1 保存入口参数

加壳程序在初始化时会保存各寄存器的值,待外壳执行完毕,在恢复各寄存器的内容,最后跳到原程序执行。通常用 pushad/popad、pushfd/popfd指令来保存与恢复现场。

1.2 获取壳本身需要使用的API地址

大多情况下,外壳的导入表只有GetProcAddress、GetMoudleHandle和LoadLibrary这三个函数。如果需要使用其他API,可以通过LoadLibrary将DLL文件映像映射到调用进程的地址空间中。

1.3 解密原程序各个区块的数据

处于保护原程序代码和数据的目的,壳一般会加密源程序文件的各个区块。在程序执行时,外壳将解密这些区块数据,从而使程序能够正常运行。因为壳一般是按区块加密的,所以在解密时也按区块解密,并把解密的区块数据按照区块的定义放在内存中合适的位置。

1.4 IAT的初始化

IAT的填写本来应该由PE装载器实现,但由于在加壳时构造了一个自建导入表,并让PE文件头数据目录表中的导入表指针指向自建的导入表,PE装载器会对自建的导入表进行填写。程序的原始导入表被外壳变形后存储,IAT的填写会有外壳程序实现。

外壳要做的就是:将这个变形的导入表的结构从头至尾扫描一遍,重新获取每一个DLL引入的所有函数的地址,并将其填写在IAT中。

1.5 重定位项的处理

对于动态链接库(DLL)文件来说,由于Windows操作系统没有办法保证在DLL每次运行时都提供相同的地址,所以需要进行重定位操作。壳中也要有用于“重定位”的代码,否则原程序中的代码是无法正常运行的。

1.6 Hook API

在程序文件中,导入表的作用就是让Windows操作系用在程序运行时将API的实际地址提供给程序使用。壳大都在修改原程序文件的导入表后自己模仿Windows操作系统的执行流程:项导入表中填充相关的数据。在填充过程中,外壳可以填充Hook API代码的地址,从而间接获得程序的控制权。

1.7 跳转到程序原入口点(OEP)

在此刻壳就把控制权还给了原程序。所以找到了OEP也就意味着找到了原程序,也就是脱壳完毕。下面讨论如何寻找OEP。

2 寻找OEP

2.1 根据跨段指令寻找OEP

绝大多数PE加壳程序在被加密的程序中加上了一个或多个区块,当外壳代码处理完毕就会跳转到程序本身的代码处。所以根据跨段的转移指令就可以找到真正的程序入口点了。

同样使用书中的例子RebPE.exe,首先利用StudyPE对比程序加壳前后的区别:

整体思路:

可以看出加壳后,程序中多了一个区块.pediy。这个区块就是外壳,相当于一个文件加载器。当程序运行时,各区块被Windows操作系统映射到内存中,现在入口点地址是13000h,指向外壳。当外壳拿到控制权后,会获得自己所需要的API地址,解密原程序各区块的数据,填充IAT。做完这些工作后,就准备跳到OEP处执行。 

使用OD打开加壳后的可执行文件,加壳程序的入口点代码如下:

在0x413001处跟进call函数:

跟进0x413135处的jmp指令进入外壳的第2部分,外壳第2部分的主要工作是还原各区块数据,初始化原程序:

解压各区块: 

填充IAT:

每一次循环执行到0x2e01de处的GetProcAddress函数时,都可以看到加壳程序在获取不同API函数的地址,以此来填充IAT。

修正重定位数据:

Anti Dump:

准备返回OEP:


至此,外壳代码处理完毕,其已将目标程序初始化了。在执行完上图的ret指令后就进入到OEP,通过Crtl+A迫使OD重新分析代码: 

可以看到函数开辟栈帧的操作:push ebp + mov ebp, esp。

复盘一下整个过程:外壳两次使用了跨段转移指令:第1次从.pediy区块跳到外壳的第2部分(其中第2部分的内存空间是函数VirtualAlloc随机分配的);第2次从外壳的第2部分跳到程序本身的代码所在的区块,在本例中是.text区块,这也是判断该处是否为OEP的关键。当程序停在OEP是,在OD中通过Alt+M查看各区块情况:

2.2 利用内存访问断点寻找OEP

外壳先将压缩的代码解压并释放到对应的区块上,处理完毕再跳转到代码段执行。当对代码段设置内存访问断点时,一定会断在外壳对代码进行读取的那句指令上。

Alt+M打开内存模块,对代码段(.text区块)按F2设置内存访问断电:

F9运行程序,程序将中断如下:

上面的代码是aPLib的解压函数aP_depack_asm。 Ctrl+F9返回到上一层:

上面的代码依次将.text、.rdata、.data、.rsrc区块解压并放到正确的位置。目前程序断在代码段,在0x2F00DA处执行完毕、将代码段全部解压后,再次对代码段(.text区块)设置内存访问断点后执行程序,当外壳跳到OEP返回代码段是即可触发内存访问断点:

2.3 根据栈平衡原理寻找OEP

加壳程序在初始化时保存各寄存器的值,待外壳执行完毕恢复各寄存器的内容,最后跳转到原程序执行。在脱壳时,根据栈平衡原理对esp设断,很快就能找到OEP。

OD加载已加壳的程序:

在执行pushad后,各寄存器的值将入栈:

此时esp指向0x0018FF6C,对该地址设置硬件访问断点:

F9运行程序,外壳代码处理结束后,在调用popad指令恢复现场时访问这些栈,程序就会中断,此处离OEP也就不远了:



http://www.ppmy.cn/news/572854.html

相关文章

壳---了解和汇总

壳:专用加密软件技术,用来保护软件不被非法修改或反编译。 加了壳的软件在磁盘中是以加密的形式存在的,当执行时,壳先进入内存将软件解密还原。 压缩引擎:一些加壳软件压缩文件时,往往调用现成的压缩引擎…

说说“壳”这个字

“壳”是个会意字。甲骨文中是手持槌击“南”(一钟形陶制乐器)的形象。钟里面是空的,外层是硬的,故产生坚硬外皮的含义。壳的本义为敲击,凡可以敲击出声的东西,多内空而外有硬皮,故壳引申为坚硬…

在Vue项目中使用openlayers开发GIS项目

一、安装依赖 "ol": "^7.3.0", "proj4": "^2.8.0",二、 初始化一个简单的地图 创建地图的代码如此简单,以致于让一部分初学者误认为在此基础上的深入开发也很简单,这是一个非常错误的理解。此时,最…

哈工大计算机网络课程网络层协议详解之:DHCP协议

哈工大计算机网络课程网络层协议详解之:DHCP协议 文章目录 哈工大计算机网络课程网络层协议详解之:DHCP协议如何获得IP地址?硬编码动态主机配置协议-DHCP:(Dynamic Host Configuration Protocol) 动态主机配…

计算机基础--->数据结构(4)【优先队列】

文章目录 优先队列设置优先队列优先级例题23. 合并 K 个升序链表215. 数组中的第K个最大元素 优先队列 优先队列是一种特殊的队列,与普通队列不同的是,优先队列中的元素具有优先级,队列中高优先级的元素会被先出队。具体来说,优先…

对英雄联盟英雄属性数据的预处理及相似度矩阵计算

目录 一、引言 二、任务1 1、填充缺失值 2、用中位数填充“生命值”属性列缺失值 3、 用均值填充“生命值”属性列缺失值 三、任务2 注:英雄联盟英雄属性数据资源可在博客资源中自行获取。 一、引言 英雄联盟作为一款古早的刀塔游戏,可谓之刀塔游…

什么是CDS?

CDS是一个企业级的持续交付和DevOps自动化开源平台 弹性 CDS资源/worker是按需启动的,以确保用户的等待时间较短,并且不会过度消耗空闲资源 可扩展的 在CDS中,任何类型的操作(Kubernetes和OpenStack部署、推送到Kafka、cve的测试……)都可以…

CLKREQ#

PCIE的REFCLK一般由外部提供,Downstream/Upstream Component通过assert CLKREQ#来请求REFCLK。 在PCIE3.0,Upstream Port可以在L1/ASPM L1以及L2/3状态,de-assert CLKREQ#,但其他状态需要assert CLKREQ#。 PCIE3.1a相对于PCIE3.0…