各处转载 ,只是学习,没有其他意思~~~~~~
PE文件内容装在进内存的方式有两种:
1)直接将整个文件读入内存/
2)根据 Windows PE 装载器载入PE文件的方式将PE文件载入内存
这里采用 2 方法。
创建一个CPack类,
1)封装 LoadPE 函数:1 判断文件是否存在 2 判断文件是否为PE文件 3)文件内容读入内存 4 保存附加数据
2)判断文件是否为 DLL文件(只对exe进行加壳 压缩)
3)清除重定位 信息 (ClearReloc) 1 重定位表数据 2数据目录表中 重定位项数据
(对于EXE文件来说,实际载入基址 与优先载入基址通常是相同的,所有重定位信息是没有用的,为了达到更大的
压缩效果可以将其去除,通常编译器将重定位信息单独设为一个节,所有清除这样的重定位信息,我们只要把这个
节整体删除即可,但对于DLL文件,实际载入基址和优先载入基址很可能是不同的,这一久需要对全局数据或者函
数等信息进行重定位,所以不可以清除重定位信息。然而重定位信息已经被压缩了,所以原来应该由PE加载器完成
的重定位工作,现在应该由外壳代码在完成解压后完成。而避免PE加载器对加壳文件进行重定位工作而引发错误,
我们需要把重定位目录删除,因此把重定位目录中保存的重定位表信息保存起来,以便外壳代码进行重定位。)
4)压缩各个节数据
压缩引擎:
JCALG1 适合大文件压缩 2 LZMA 是 7-Zip 程序默认算法,很高的压缩比 3选择aPlib,使用时包含h文件,导入lib
1)压缩各个节时需要同时保存被压缩节解压后的内存RVA 2)压缩节的内存RVA 3 )压缩数据指针+大小 4)注意是否压缩资源节
封装 void CompressSections()
压缩功能函数:
PVOID CompressData(PVOID pSource, long lInLength, OUT long &lOutLenght);
pSource -> 待压缩数据所在内存地址 lInLength压缩后大小 lOutLenght
参考: http://bbs.pediy.com/showthread.php?t=131361
如果有资源节,不压缩资源节的话,被压缩节的数量减一
5) 打造导入表
我们已经将导入表给压缩了,所以要给加壳后的文件重新创建新的导入表,该导入表包含外壳代码锁使用的API函数信息。
void BuildImportTable(DWORD awRva, pImportInfo pImport PBYTE pMem);
6) 封装 ERRORIN CPack::GetFile(TCHAR *pPathName)
参数 pPathName 表示要生成的文件名的字符串指针,返回值为枚举类型
1)判断是否可以生成加壳文件 1 被加壳文件是否成功加载 2判断目标文件是否已经加壳
2)处理重定位信息
3)对目标程序的各个节进行压缩
4)创建加壳文件并写入临时PE头 (不是最终,最后还要修改,写入的是加壳前的PE头,主要是占位,从而方便后面写)
5)修正刚写入的 PE头中的节表头
根据是否压缩资源节加壳后的文件的节表层次结构分为两种情况:
* 不压缩资源节的情况:第一个是占位节,第二个是资源节,其后市各个压缩节,最后是配置信息节
* 压缩资源节的情况: 第一个是占位节,其后是各个压缩节,之后是配置信息节,最后是仅包含图标+版本信息的资源节
6)写入配置信息节的节头
配置节的长度:
1.解压参数 PressSecNum * 8 + 4 字节 *当外壳代码进行解压时要知道 源+目的 两个DWORD参数
加壳时压缩了几个节这里就有几组参数,最后一个DWORD零值结尾
2.基址 4 字节 *保存的是PE头中SizeOfImage 域所指定的优先加载基址。判断PE实际
加载基址与这个基址是否一致,因为只有两个值不一样的时候才需要对
重定位数据进行修正。是一个 DWORD 4字节
3.导入表RVA 4 字节 *加壳前文件的导入表 RVA,因为当外壳代码完成解压后需要代替PE加载
器完成IAT的填充工作,这时需要根据导入表信息区完成IAT的填充,DWORD
4.解压代码 0x149 字节 DecompressCodeSize *加压各个压缩节所使用的解压代码,定义一个全局数组变量来保存解压代码
BYTE decode[] = "解压代码的16进制数据",反汇编aplib.dll文件定位解压函
数,然后拷贝二进制数据。大小固定的是 0x149字节
5.导入表 0x** 字节 ImportTableSize *加壳后程序需要的导入表,这个导入表需要自己打造,可以使用前面封装的
BuildImportTable,然后ImportTableSize获得打造的导入表大小。加壳文件主
(kernel32.dll)要使用三个API: LoadLibrary, GetProcAddress,VirtualProtect(资源目录默认只读)
6.相对长度 4 字节 RelativeNum *前5部分总大小,该值用于外壳代码的定位,DWORD 4字节
7.资源信息 8 字节 ResourceInfoSize *保存加壳前的资源目录信息,当完成解压后需要恢复原始的资源目录,从而保
证原始程序在提取资源时不出错,保存资源的 RVA 和 大小 8字节
8.DLL信息 4字节 *DWORD 标示加壳文件是否为DLL,如果是写入1,占4字节
9.重定位表RVA 4字节 *加壳前文件的重定位表 RVA,目标是DLL那么外壳代码完成解压+IAT的填充工作后,还要
根据实际加载基址于优先加载基址的对比结果进行重定位数据的修正,这时就用到重定位表,4字节
10.oep(RVA) 4 字节 OepSize *保存原始程序的入口地址RVA
11.外壳代码 0x** 字节 *外壳代码, BYTE code[] = "外壳代码的16进制数据"
7) 写入各个节数据
如果目标文件存在资源节,并且不要求压缩的话,那么应该将资源节数据写到压缩节数据之前,PE头之后。虽然第一个节是占位节,但是它的文件大小为0,
所以PE头之后的节数据是第二个节的数据。
8) 完成配置节
压缩节解压参数 封装成一个结构体保存。 也就是 6中的各种数据
typedef struct Data
{
PBYTE pOut;
PBYTE pIn;
}
9)修正PE头。 需要修正的数据如下:
1) 程序入口地址,指向外壳代码
2)修改导入表目录使其指向新导入表
3)修改节个数
4)修正镜像
10)为加壳文件添加图标+版本资源 (压缩了资源就要运行该函数)
用到 CopyResource.lib 提供了 bool UpdateIconAndVersion (TCHAR *pSrcFile,tchar *pDstFile)函数
将pSrcFile参数 锁指向的程序图标+版本资源信息添加给pDstFile参数所指向的文件,内部是使用增加
节的方法进行添加的。成功 返回真, 需要
include "include/CopyResource.h" #pragma comment(lib,"lib/CopyResource.lib")
11) 添加附加数据
如果加壳前的文件包含附加数据,加壳后还要讲附加数据添加到加壳后生成的文件尾
最后注意 释放掉 前面申请的内存
外壳代码的实现: C++ 和 汇编代码配合完成
1)解压
2)填充IAT
3)恢复资源目录
4)修正重定位数据
5)跳回OEP
CPack pack; if (pack.LoadPE("C:\1\2.exe") == SUCCESS) { if (pack.GetFile("C:\1\2.exe") == SUCCESS) { MessageBoxA(NULL, "加壳成功", "恭喜", MB_OK); } }