一、
加载器读取一个PE文件的过程如下:
1.
2.
3.
4.
5.
6.
7.
8.
这里要提的是加载PE文件所需DLL的过程是建立在六个底层的API上。
LdrpCheckForLoadedDll:检查要加载的模块是否已经存在。
LdrpMapDll:映射模块和所需信息到内存。
LdrpWalkImportDescriptor
LdrpUpdateLoadCount:计数模块的使用次数。
LdrpRunInitializeRoutine
LdrpClearLoadInProgress:清楚某些标志,表明加载已经完成。
二、
有三种方式可以插入代码到PE文件:
1.
2.
3.
方法一、增加代码到一个存在的Section。
首先我们需要找到一个被映射到一个块有执行权限的Section。最简单的方式就是直接利用CODE Section。
然后我们需要查找这块Section内的多余空间(也就是填满了00h)。我们知道一个Section有两个数据来表示其大小。VirtualSize和SizeOfRawData。这个VirtualSize代表Section里代码实际所占用的磁盘空间。SizeOfRawData代表根据磁盘对齐后所占的空间。通常SizeofRawData都会比VirtualSize要大。如下图。
图中的SizeOfRawData是0002A000,而VirtualSize是00029E88。当PE文件被加载到内存的时候,他们之间的多余空间的数据是不会被加载到内存去。那么如果要把加入到这个间隙中间的代码也被加载到内存去,就需要修改VirtualSize的值,这里把VirtualSize的值可以改为00029FFF。这样,我们就有了一小段空间加入自己的代码。下面需要做的就是先找到PE文件的入口点OriginalEntryPoint,比如这个OriginalEntryPoint是0002ADB4,ImageBase是400000,那么入口点的实际虚拟地址是0042ADB4。然后计算出自己代码的起始RVA,更换掉PE头内的OriginalEntryPoint,在自己的代码最后加上:
MOV EAX,00042ADB4
JMP EAX
这样就可以在PE文件被加载的时候,先运行自己的代码,然后再运行PE文件本身的代码。成功的把代码加入到了PE文件内。
方法二、扩大一个存在的Section来加入代码。
如果在一个Section末尾没有足够的空间存放自己的代码,那么另外一种方法就是扩大一个存在的Section。一般我们只扩大PE文件最尾部的Section,因为这样可以避免很多问题,比如对其他Section的影响。
首先我们的找到最后一个Section使之可读可执行。这可以通过修改其对应Section头部的Characteristics来获得。然后根据PE头内文件对齐的大小,修改其SizeOfRawData。比如文件对齐的大小是200h,原先SizeOfRawData=00008000h, 那么我们增加的空间大小应该是200h的整数倍,修改完的SizeOfRawData至少是00008200h。增加完空间后,需要修改PE头内的两个字段的数值,SizeOfCode和SizeOfInitialishedData。分别为它们增加200h的大小。这样我们就成功的扩大了一个Section,然后根据方法一内的方式把代码加入到增加的空间。
方法三、新增一个Section来加入代码。
如果要加入的代码很多,那么就需要新增一个Section来存放自己的代码。
l
l
l
1.
2.
3.
4.
而文件对齐和Section对齐的数据分别是:
5.
6.
l
1.
2.
3.
4.
5.
l
l
三、
在一些特殊用途上,我们需要为执行文件或DLL增加其不包含的API。那么可以通过增加这些API在输入表中的注册来达到。
1.
2.
3.
4.
增加一个新的IID到输入表的末尾,就是把输入表末尾的全是0的IID修改成增加的新的IID,然后在增加一个全0的IID作为输入表新的末尾。但是如果在输入表末尾没有空间的话,那就需要拷贝整个输入表到一个新的足够的空间,同时修改数据目录数组对应输入表的数据结构IMAGE_DATA_DIRECTORY的RVA和iSize。
步骤一、增加一个新的IID。
步骤二、增加一个新的DLL及其需要的函数。
|
|
来自: firefox_zyw > 《待分类1》