程序加载的过程:
1、一般情况下,EXE都是可以按照ImageBase的地址进行加载的.因为Exe拥有自己独立的4GB 的虚拟内存空间
但DLL 不是 DLL是有EXE使用它,才加载到相关EXE的进程空间的.
2、为了提高搜索的速度,模块间地址也是要对齐的 模块地址对齐为10000H 也就是64K
打开一个程序,观察一下全局变量的反汇编
00401D58 A1 44 CA 42 00 mov eax,[x (0042ca44)] 00401D5D 50 push eax 00401D5E 68 EC 91 42 00 push offset string "%d\n" (004291ec) 00401D63 E8 28 62 00 00 call printf (00407f90)
编译时生成的地址 = ImageBase + RVA
这个地址在程序编译完成后,已经写入文件了。
那假设,程序在加载的时候,没有按照预定的400000 载入到指定的位置,但程序执行的时候,仍然会按照0042ca44 和 004291ec 的地址去使用这个值!
1、也就是说,如果程序能够按照预定的ImageBase来加载的话,那么就不需要重定位表
这也是为什么exe很少有重定位表,而DLL大多都有重定位表的原因
2、一旦某个模块没有按照ImageBase进行加载,那么所有类似上面中的地址就都需要修正,否则,引用的地址就是无效的.
3、一个EXE中,需要修正的地方会很多,那我们如何来记录都有哪些地方需要修正呢?
答案就是重定位表
重定位表的定位:
数据目录项的第6个结构,就是重定位表.
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
RVA转FOA
typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; } IMAGE_BASE_RELOCATION; typedef IMAGE_BASE_RELOCATION ,* PIMAGE_BASE_RELOCATION;
解析说明:
1、通过IMAGE_DATA_DIRECTORY结构的VirtualAddress
属性 找到第一个IMAGE_BASE_RELOCATION
2、判断一共有几块数据:
最后一个结构的VirtualAddress与SizeOfBlock都为0
3、具体项 宽度:2字节
内存中的页大小是1000H 也就是说2的12次方 就可以表示
一个页内所有的偏移地址 具体项的宽度是16bit 高四位
代表类型:值为3 代表的是需要修改的数据 值为0代表的是
用于数据对齐的数据,可以不用修改.也就是说 我们只关注
高4位的值为3的就可以了.
4、VirtualAddress 宽度:4字节
当前这一个块的数据,每一个低12位的值+VirtualAddress 才是
真正需要修复的数据的RVA
真正的RVA = VirtualAddress + 具体项的低12位
5、SizeOfBlock 宽度:4字节
当前块的总大小
具体项的数量 = (SizeOfBlock - 8)/2