文件执行的总过程
当文件从硬盘中读入虚拟内存(FileBuffer)中时,文件数据会被原封不动的复制一份到虚拟内存中,然后进行拉伸对齐。此时虚拟内存中文件数据叫做文件印象或者内存印象,即ImageBuffer。此时ImageBuffer中的文件的数据满足文件运行的条件,但文件仍然不能真正的运行。只有当操作系统将文件真正的装入物理内存中, CPU进行分配执行后,文件才算真正运行了
SizeOfRawData与Misc.VirtualSize
SizeOfRawData表示此节在硬盘上经过文件对齐后的大小,Misc.VirtualSize表示此节在内存中对齐前的大小,即文件数据在内存中真正分配的大小
当我们初始化一个全局变量,如数组int arr[1000] = {0}时,编译器将编译好的.exe文件存放在硬盘上,这1000个int类型的0会存放在某一个节中,并分配1000个0的空间。并且这个空间大小在硬盘中和在内存中是一致的。由于SizeOfRawData的大小加上了文件对齐时多出来的空间,所以SizeOfRawData一般大于或等于Misc.VirtualSize
但是当我们只声明不初始化该数组时,文件在硬盘上的数据对此数组有声明但是不会分配这1000的空间的。所以SizeOfRawData在计算时不会算上这未初始化的空间。但是当文件真正加载到物理内存中时,操作系统会分配这1000的空间,所以Misc.VirtualSize在计算时会加上未初始化的空间的。在这种情况下Misc.VirtualSize可能大于SizeOfRawData
手动模拟FileBuffer到ImageBuffer过程
1.获取FileBuffer:先在硬盘上找一个可执行文件,将该文件的数据复制到缓冲区中,即FileBuffer中
2.申请ImageBuffer:根据SizeOfImage即文件加载到4GB虚拟内存中的大小,使用malloc开辟另一块缓冲区ImageBufferb并初始化,用来存放文件虚拟内存中的数据
3.复制PE头字段:由于经过文件对齐后的所有头和节表的数据加载到ImageBuffer的过程中是不会改变的,所以可以通过sizeofheads获取这些数据的内存大小,直接从FileBuffer中复制到ImageBuffer中
4.复制所有的节:通过第一个节对应节表中的PointerToRawData的值确定该节在FileBuffer的起始地址,然后通过SizeOfRawData的值确定该节在FileBuffer中需要复制的数据的大小,再通过VirtualAddress再加上ImageBase得到此节在ImageBuffer中的起始地址,最后将FileBuffer对应的数据在ImageBuffer中的起始地址位置开始复制,完成第一个节的复制。剩下的节如此循环赋值即可。
选择SizeOfRawData来确定需要复制的节的大小的原因:在极端情况下,当节中存在足够大未初始化的数据时,按照Misc.VirtualSize值将FileBuffer中的数据复制到ImageBuffer中,很可能会把FileBuffer中下一个节的数据也复制过去,这样就会造成复制错误。所以直接用SizeOfRawData就可以了。在这两个选择中更好的做法是比较SizeOfRawData和VirtualSize,选择较小值。但实际上这两种选择哪一个都可以
内存偏移地址与文件偏移地址换算
在此之前我们应该知道DOS头的起始地址和imagebase没有关系,现在假设有一个文件在4GB虚拟内存中起始位置0x500000,有一个数据,其地址为0x501234。我们要求出该地址对应的硬盘内存地址:
1.先算出此数据虚拟内存地址相对于文件在虚拟内存中的起始地址的偏移量
2.通过这个偏移量和每一个节的VirtualAddress做循环比较,当此偏移量大于某一个节的VirtualAddress并且小于此VirtualAddress + Misc.VirtualSize,就说明这个内存地址就在这个节中
3.用此偏移量-此节的VirtualAddress得到这个数据的虚拟内存地址相对于所在节的偏移量
4.找该数据虚拟内存地址所在节对应节表中PointerToRawData,通过PointerToRawData + 内存地址相对于所在节的偏移量来得到此内存地址在硬盘上时相对于文件的偏移量
举例:现在我们要找0x501234对应的文件偏移是多少?
1.0x501234 - 0x500000 = 0x1234
2.因为0x1000 < 0x1234 < 0x1000 + Misc.VirtualSize,所以0x501234在可执行文件的第一个节中
3.0x1234 - 0x1000 = 0x234获取该数据相对于该节的偏移量
由于第一个节的PointerToRawData为0x400,且假设FileBuffer的起始地址为0(相对与文件在真实内存起始地址),则0x501234对应的文件偏移地址为0x400 + 0x234 = 0x634