基址重定位表
PE重定位
PE文件向进程中载入时,会被加载到PE头的ImageBase所指的地址处,如果加载的是DLL/SYS文件,该地址已经被其他DLL占用,那么就需要进行PE文件重定位.
PE文件在重定位时会使用基址重定位表
,将硬编码在PE程序中的内存地址进行重新计算,让内存地址随加载地址变化而变化.
DLL/SYS重定位
假设TEST.EXE在载入时,A.DLL被载入到TEST.EXE的10000000地址处,此时B.DLL试图加载到相同地址,PE装载器就会将其装载到另一个未被占用的地址(该地址不能够和TEST.EXE和A.DLL所占的内存重叠).
EXE重定位
创建好进程后,EXE首先被加载到内存,因此无需考虑重定位问题.
另外,Windows Vista之后引入了ASLR机制,这会让每次加载都会在不同的随机地址.
重定位操作
原理很简单:
查找硬编码时,使用到PE文件中的基址重定位表
,它记录了硬编码地址偏移(位置),在PE文件构建(编译/链接)时制定该表.
基址重定位表
该表位于PE头的DataDirectory数组的第6个元素:
跳转到该地址可以看到基址重定位表的实际内容:
IMAGE_BASE_RELOCATION结构体
重定位表中罗列了程序中所有的硬编码地址的偏移(位置).重定位表是IMAGE_BASE_RELOCATION结构体数组.
第一个成员VirtualAddress为基准地址,实际上是一个RVA;第二个SizeOfBlock为重定位块的大小;最后一项为注释,并非结构体成员,表示结构体之下会出现WORD类型的数组,其元素值为硬编码的地址偏移.
根据下表的例子,可以知道TypeOffset数组成员的基准地址(起始地址)RVA为1000,块总大小为150.块末尾以0结束:
每个元素值为2字节大小,最高4位为Type,低12位是Offset.
PE中Tpye常见为3(IMAGE_REL_BASED_HIGHLOW),PE+文件常见为A(IMAGE_REL_BASED_ABSOLUTE).
接下来就可以定位硬编码了,公式如下:
VirtualAddress(1000)+Offset(420)=1420(RVA)
跳转到该地址就可以找到硬编码地址:
然后就可以根据实际加载地址去进行重新计算了.其余的硬编码都如此依次处理.