stardragon
2007-12-03, 19:18
在学习修改PE文件的源代码中,遇到这么一条语句,看了半天不懂
还请高手指教:
// 得到代码偏移,用代码段起始RVA减去此段的物理偏移
// 应为程序的入口计算公式是一个相对的偏移地址,计算公式为:
// 代码的写入地址+dwCodeOffset.
dwCodeOffset=header-> opt_head.BaseOfCode-header-> section_header[0].PointerToRawData;
// 保存旧的程序入口地址.
dwOldEntryAddress=header-> opt_head.AddressOfEntryPoint;
// 计算新的程序入口地址.
dwNewEntryAddress=dwEntryWrite+dwCodeOffset;
按照个人的理解这是计算PE新的入口地址的代码,但是有点不是太懂!PointerToRawData应该是指向硬盘中文件的指针,跟BaseOfCode(文件映射的执行代码RVA)好像不能发生什么关系吧。
请大侠不吝赐教,感激!
combojiang
2007-12-03, 19:57
这里要理解好PE文件存在的两种形态,一种是以硬盘文件存储的形式,一种是内存加载的形式。
对于code代码段,在内存中存在的形式和硬盘中存储的形式是一样的,都是以二进制方式存在。
BaseOfCode是code代码段在内存中加载的相对起始地址,PointerToRawData是code代码段在硬盘文件中的相对起始地址。PointerToRawData是以文件起头为基准。BaseOfCode是以ImageBase为基准。
因此,可以透过内存中的某个值的偏移计算出其在文件中的位置,也可以根据文件中的某个值的偏移,计算出其在内存中的位置。
stardragon
2007-12-03, 21:54
这里要理解好PE文件存在的两种形态,一种是以硬盘文件存储的形式,一种是内存加载的形式。
对于code代码段,在内存中存在的形式和硬盘中存储的形式是一样的,都是以二进制方式存在。
BaseOfCode是code代码段在内存中加载的相对起始地址,PointerToRawData是code代码段在硬盘文件...
感谢您的回复!但还是有点不懂,在PE Header中好像没有关于文件起始段的描述吧,那样的话PointerToRawData怎么寻找自己的基准呢?另ImageBase是从哪里起始的(应该是从PE Header的某个地方开始吧),能否给出一个从文件偏移到内存位置的位置计算公式或者从内存偏移到文件位置的计算公式:): :):
combojiang
2007-12-04, 00:56
呵呵,看得出,你对pe文件格式还是不太了解。
ImageBase对于EXE文件来说,由于每个文件总是使用独立的虚拟地址空间,EXE总是能够按照这个地址装入。通常是0x00400000,imagebase指向的内容是DOS文件头。对于内存中pe文件代码段的起始地址是ImageBase + BaseOfCode;对于磁盘文件中代码段的起始地址是相对于文件头偏移PointerToRawData的位置。
假设要在磁盘文件中的代码段缝隙中添加代码,则首先需要查看磁盘文件代码段缝隙大小,是否容的下插入的代码,如果能容下,则根据文件对齐的原则,首先将原来的磁盘文件中代码段按照文件对齐,找出起始插入代码的位置。例如:
// 此段的真实长度.
dwVirtSize=header->section_header[0].Misc.VirtualSize;
// 此段的物理偏移.
dwPhysAddress=header->section_header[0].PointerToRawData;
// 此段的物理长度.
dwPhysSize=header->section_header[0].SizeOfRawData;
// 得到代码段的可用空间,用以判断可不可以写入我们的代码
// 用此段的物理长度减去此段的真实长度就可以得到.
dwSpace=dwPhysSize-dwVirtSize;
if(dwSpace < viruslength) return;
// 代码写入的物理偏移.
dwEntryWrite=header->section_header[0].PointerToRawData+header->
section_header[0].Misc.VirtualSize;
//对齐边界.
mods=dwEntryWrite%16;
if(mods!=0)
{
dwEntryWrite+=(16-mods);
}
//计算加载到内存中的代码段与文件中的代码段的差。
dwCodeOffset=header->opt_head.BaseOfCode-dwPhysAddress;
这里的dwEntryWrite就是对齐后要插入代码的位置。现在有了物理文件中的位置,需要找出加载到内存中的位置。根据我在上面贴子中讲的“对于code代码段,在内存中存在的形式和硬盘中存储的形式是一样的,都是以二进制方式存在”,可以有公式:
内存中位置 - (ImageBase + BaseOfCode) = dwEntryWrite - dwPhysAddress;
由于pe中使用的都是PVA,因此假设内存中对应位置的RVA是dwNewEntryAddress,
则上面公式中: 内存中位置 = ImageBase + dwNewEntryAddress;
把这个代入上面公式就是:
ImageBase + dwNewEntryAddress - (ImageBase + BaseOfCode) = dwEntryWrite - dwPhysAddress
于是:dwNewEntryAddress = dwEntryWrite - dwPhysAddress + BaseOfCode;
dwNewEntryAddress = dwEntryWrite + (BaseOfCode - dwPhysAddress );
而前面dwCodeOffset=header->opt_head.BaseOfCode-dwPhysAddress;
所以:
dwNewEntryAddress = dwEntryWrite + dwCodeOffset;
到此为止,就推断出了你开头 计算新的程序入口地址的公式了。