---------------------------------------------------------------
1、重叠NT文件头到MZ-DOS头。
如图,把原MZ-DOS头的最后一个数据offset to new exe header设为04,就把NT文件头移到了第四个字节,NT头的数据把MZ DOS头完全覆盖。
此时MZ-DOS的offset to new header同时是NT头的可选头中,section alignment,节对齐,好在4是2的幂次,是可以使用的数值。
这样就省下了不少字节。
---------------------------------------------------------------
2、重叠引入表和可选头。
这里先回顾一下引入表。由于EXE中需要引入别的DLL中的函数,所以在PE中就要有所说明,即引入表,其中要说明包含引入的DLL信息和引入的函数的信息。
具体说来,可选头尾部的数据目录项的第二项(data directory)-->IMAGE_IMPORT_DESCRIPTOR(引入的DLL的信息)-->IAT(引入函数的信息,这里仅讨论IAT,关于INT和IAT不作解释)
由于引入的DLL可以有很多个,引入的函数也可以有很多个,所以上面所说的IMAGE_IMPORT_DESCRIPTOR其实会有许多个,是一串数组。IAT也是个数组。
OK,预热完毕,还不懂的自行百度有许多资料。
附上
数据目录项结构:
IMAGE_DATA_DIRECTORY STRUCT
VirtualAddress dd ?
isize dd ?
IMAGE_DATA_DIRECTORY ENDS
DLL描述符结构:
IMAGE_IMPORT_DESCRIPTOR STRUCT
union
Characteristics dd ?
OriginalFirstThunk dd ?
ends
TimeDateStamp dd ?
ForwarderChain dd ?
Name1 dd ?
FirstThunk dd ?
IMAGE_IMPORT_DESCRIPTOR ENDS
可选头末尾指出有两个数据目录项,第一个全为0,第二个即是框住的,引入表。引入表指向的DLL描述符,“30”处指向的即是“user32”的ASCII码。
紧随其后的“9D”即指向IAT表,看到图中9D的偏移,“DD 01”即1DD,即是MessageBoxA在user32.dll导出表中的序号。
附IAT项的结构
IMAGE_IMPORT_BY_NAME STRUCT
Hint dw ?
Name1 db ?
IMAGE_IMPORT_BY_NAME ENDS
---------------------------------------------------------------
3、重叠节表到可选头
这里需要指出的是NT头中的SizeOfOptionalHeader项中的值,并不是指可选头的大小,更准确来说,应该是,节表开始的位置,相对可选头开始的位置的偏移。
简单地说,其实这个东西标识的是节表的开始,在这个例子里,作者给它写上了0x28,也就是说节表在0x44处。
第二个框是可选头开始的幻数“0B01”,下面那一大串框起来的就是节表中的第一个节的信息。
附上节表结构
struct IMAGE_SECTION_HEADER
{
BYTE Name[8];
union
{
DWORD PhysicalAddress;//物理地址
DWORD VirtualSize;//真实长度,这两个值是一个联合结构,可以使用其中的任何一个,
//一般是节的数据大小
} Misc;
DWORD VirtualAddress;//RVA
DWORD SizeOfRawData;//物理长度
DWORD PointerToRawData;//节基于文件的偏移量
DWORD PointerToRelocations;//重定位的偏移
DWORD PointerToLinenumbers;//行号表的偏移
WORD NumberOfRelocations;//重定位项数目
WORD NumberOfLinenumbers;//行号表的数目
DWORD Characteristics;//节属性 如可读,可写,可执行等
};
可以看出,真正有用的信息从0x4c开始,意思就是说这个节实际只有4个字节大小,文件中的地址是0xC,对齐后真正需要装到内存的有0x34个字节,装入后的RVA还是0xC。
这里插个问题,为什么对齐后真正需要装到内存的是0x34个字节?(其实0x2d个字节就可以)
这个我也不是特别肯定,我觉得应该是与引入表需要的字符串资源有关,如果小于0x2d,按照对齐只能是0x29,则没有完全包括“user32”这个字串。会导致windows想要加载相应模块时出错。
---------------------------------------------------------------
4、重叠汇编代码和data directory。
如你们所见,在引入表处,作者就已经近不及待地在不影响程序正常运行的引入表的大小处直接插入了代码,随后汇编代码覆盖的是其余的data directory项,并不会造成什么别的影响。
---------------------------------------------------------------
最后说一下程序运行起来的过程。
首先windows就像我们之前分析的那样,把所有东西准备好,把IAT表指向的位置换成MessageBoxA的真实地址(0x9d)。
随后windows到可选头中查找程序入口地址,0xC,然后跳到0xC处开始执行。0xC处是一个跳转语句直接跳到下面的正常代码。
这个插个问题,为什么要这么拐弯抹角跳一次,直接指向功能代码不行吗?
因为之前节表限制了,这个节大小只能有4个字节,这个“4”,即是节表的大小,还是MajorSubsystemVersion,这个值必需得是4。4个字节能做什么?能放下一个跳转,跳到任何地方,做任何事情(长跳也可以,分两次跳),于是就有了一个跳转。