系列汇总
- 写一个PE的壳_Part 1:加载PE文件到内存
- 写一个PE的壳_Part 2:ASLR+修复输入表(IAT)+重定位表支持(.reloc)
- 写一个PE的壳_Part 3:Section里实现PE装载器
- 写一个PE的壳_Part 4:修复对ASLR支持+lief构建新PE
- 写一个PE的壳_Part 5:PE格式修复+lief源码修改
- 写一个PE的壳_Part 6:简单的混淆
文章目录
Part 3中遗留了一个隐患,MinGW无法生成重定位表,因此无法产生可移动的二进制文件;Part 4中要处理MinGW生成的可执行文件(不能移动的,即不支持ASLR的二进制文件)的打包问题
1.解决思路
step 1:当前现状
寻找解决办法前,先回顾一个Part 3中
packed.exe
是怎么处理支持ASLR的二进制文件(用calc.exe
举例)的?
- 1.packed.exe里面新增一个名称为
.packed
的区段,区段的内容被加载为calc.exe- 2.packed.exe负责解析
.packed
,放到一块我们申请的内存中,用参数是NULL的VirtualAlloc来申请一块随机内存来达到对ASLR的支持- 3.修复完calc.exe的输入表和重定位表,找到calc.exe的入口EP,执行calc.exe
下面是Part 3中unpacker.exe(或者叫packed.exe也可以)的内存布局示意图
VA | RVA | CONTENT |
---|---|---|
0x00400000 | 0 | unpacker PE header |
0x00401000 | 0x1000 | unpacker .text section |
0x00402000 | 0x2000 | unpacker .rdata section |
0x00403000 | 0x3000 | unpacker .idata section |
0x00404000 | 0x4000 | unpacker .packed section |
下面给出了Part 3中问题的原因
-
问题:为什么Part 3中对不支持ASLR的二进制不支持呢?
-
假设:part 3中最后生成的壳(或者叫打包)文件名为:
packed.exe
;不支持ASLR的测试程序名为:test.exe
,源码如下://test.exe的源码 #include <stdio.h> #include <stdlib.h> int main() { printf("this is a test for compil with MinGW, no reloc\n"); return 0; }
-
冲突:
packed.exe
默认是加载在0x400000
的,test.exe
因为不能移动(即不支持ASLR),导致也需要加载在0x400000
上,产生冲突 -
原因:
packed.exe
先运行,把坑占用了,test.exe
没有重定位表,没法重定位,因此不能运行
step 2:解决思路
test.exe
的重定位表没有,在packed.exe
内存里拷贝一份不就解决了
问题1:那拷贝在哪里才能生效?
当然是不支持ASLR的程序(如test.exe)加载在0x00400000位置时,test.exe的重定位表应该放的位置(感觉像废话)
也从侧面看出来,测试整个进程packed.exe的加载基址应该是0x00400000,而不能是NULL的VirtualAlloc来申请一块随机内存,这部分后面后看到区别
问题2:packed.exe的重定位表已经把坑占了怎么办?
那就将packed.exe的重定位表整理后移呗,反正它也不用重定位表,下面是思路:
- 1.packed.exe的重定位表都是在section里,先算好test.exe的重定位表的大小
sizeof_alloc
- 2.在packe.exe的第一个section的位置预留出
sizeof_alloc
大小的空间,后续load_PE函数会处理这里- 3.packed.exe的所有section整体向后平移
sizeof_alloc
就可以了
问题3:上面修改就解决问题了?
当然,先将test.exe加载在packe.exe的Image Base算出(是否支持ASLR处理方式不同),然后不破坏packed.exe的PE header,解析.packed开始执行test.exe;但是已经巧妙的将test.exe的section放入packed.exe区段的起始位置,这样test.exe就不需要重定位了,巧妙的解决了没有重定位表的问题
step 3:内存布局
如果我们想打包一个二进制文件,希望放在VA 0x00400000开始的内存中,下面是unpacker.exe修改后(文件名改成了packe.exe)的内存布局示意图
VA | RVA | CONTENT |
---|---|---|
image base of the packed binary | 0 | unpacker PE header |
0x1000 | .alloc section, for the packed binary loading |
|
0x1000 + size of the packed binary in memory | sections of the unpacker | |
… | sections of the unpacker | |
0x4000 + size of the packed binary in memory | .packed section, with the packed PE file |
2.修改unpack部分
相对于Part 3中的unpack部分
,改的很少,主要是改动load_PE
函数,c文件名是unpack_part4.c
;整体修改前后对比,如果看不清楚也没有关系,后面会有详细的介绍
Part 3中支持ASLR是通过VirtualAlloc来申请一块内存来实现的,Part 4中要解决不支持ASLR的情况;几处改动说明:
Image Base
首先要检查PE文件的头中的DLLCharacteristics
,看一下是否支持ASLR?
- 不支持:也就没有重定位表什么事了,PE文件的加载位置就应该是默认的基址,使用当前模块地址作为基址
- 支持:VirtualAlloc新申请一块内存,用于放被加载的程序(如test.exe)
char* ImageBase = NULL;
if(p_NT_HDR->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) {
ImageBase =