实现一个压缩壳,需要做三步操作,三步过程涉及比较深入的PE结构理解,最终的实现目标如下图所示

其中过程分为以下三步
1、对原有数据压缩
对源程序使用算法进行压缩,并将数据放至目标程序的节表数据二的位置
2、对原有数据还原
编写壳代码,对自身节表二的数据进行压缩算法还原,并将生成的PE文件放至内存或者节表数据一的位置
3、对还原后的数据加载至内存并运行
编写LoadPE加载程序,对加载至内存的代码进行加载
需要编写一个工具程序,将源目标程序变为目标程序的样子,该程序可以为控制台,也可以为其他程序,本文为了方便,编写为MFC程序
1、创建MFC程序
此处选择基于对话框的设计,使用MFC选择在静态库中使用MFC,使用静态库可以将其他库打包进入该程序,相对于共享链接DLL更干净,迁移更方便

2、添加对应的窗体功能
Mfc Edit Browser Control组件,用于选择打开的文件路径,ID此处命名为MEDIT_FILE_PATH 
Button Control,用于加壳的入口,此处命名为BTN_PACK,并右键该按钮,添加点击事件


3、添加窗体代码
对传入的文件进行解析,并将其存入变量
使用WinAPI的Compress函数对原始数据进行压缩,得到压缩数据m_pComData
壳代码主要做两件事
完成PE的解压缩
进行PE的加载
创建的PE文件,可能有基址随机化,无重定位修正机制等问题,为了提升壳代码的健壮性,需要用地址无关代码。
我们需要做以下几步
进程在加载时,FS寄存器指向当前活动线程的TEB结构,通过TEB的[0x30]获取到对应的TEB结构,再通过TEB拿到加载的Kernel32的基址
此处为了使用方便,建了一个函数指针的结构体
Kernel32函数基址和想要的名称我们有了,剩下的就是找到对应的函数,需要用到导出表的知识
进行函数调用
首先需要获取当前程序的模块基址,和Kernel32的模块基址获取类似,通过TEB找到PEB,再通过PEB找到模块基址
获取完用户的基址后,再对数据节的代码进行解压缩
LoadPE是模仿操作系统将PE文件按照特定格式加载进内存的操作,并将EIP执行新PE的入口点
PE加载分为两种,一种为进程镂空操作,另一种为反射式加载,这边采用反射式加载的方式,申请一块新内存,并手动解析PE,将其运行。
LoadPE分为以下几步
解析完成后,获取到对应的ImageSize,并使用VirtualAlloc分配内存,尝试使用PE自带的ImageBase,如果能分配成功,则分配至该内存,否则随机分配
基于新的PE基址,算出偏移值和新的入口点地址
通过NTHeader和导入表地址算出导出节表头
进行导入表修复
最后跳转至入口点
写一个壳代码的PE文件分为以下几步
使用WinAPI直接创建即可
PE头主要关注PE内容和对应的写入长度,需要对原始PE文件做解析,获取原始的Header大小和原始的PE文件
同时需要对新的PE头进行修改部分数据
空节无数据,需要在PE头将其内存映射准备好即可
将0x03节的压缩数据放至该节,并使用文件对其的方式进行对齐
壳代码需要将0x04章节的数据生成干净的代码
需要做以下步骤,确保代码入口点为自己的入口代码
最终的代码节如图所示

将生成的代码拷贝为C++代码

完成后,即可完成一个简单的加壳程序
节数量

数据目录表

节表
有三个节,分别为代码节,数据节和壳节

节数据有两个节
1、数据节(原始PE的压缩数据)

2、代码节(壳代码)

内存布局
EIP指针指向.PackSec的入口


参照源码进行对照,前两个为初始化环境变量和获取用户模块基址

跟进初始换环境变量函数

获取kernel32模块基址

跟进调试,成功获取Kernel32基址


将局部变量存入栈


下边获取各个函数地址

GetProcAddress

LoadLibrary

VirtualAlloc

VirtualProtect

使用LoadLibrary加载cabinet库

获取CreateDecompressor函数

获取Decompress函数

下一步获取模块基址



内存如图所示

下一步解析PE数据,找到压缩数据入口和对应的大小,并对压缩数据进行解压缩

DosHeader + 3C的位置得到NTHeader

通过NTHeader的可选头位置+可选头大小得到节表起始位置

获取压缩数据的起始位置,大小

使用函数分配内存,并使用解压函数,将解压后的数据放至内存

成功分配内存,内存地址为0x001D0000

该内存暂时为空数据

后边分别调用创建解压器工具和进行解压操作
DeCompressor创建成功

下一步调用DeCompress函数,将数据段中的数据解压缩至0x001D0000位置处

下一步为模拟操作系统进行加载PE操作
加载PE分为一下几步
1、分配内存
首先尝试在原始PE的ImageBase上分配内存,分配失败则自动分配

调试中,发现需要分配的内存地址为0x00400000
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!