首页
社区
课程
招聘
[原创]PE文件格式之重定位表
发表于: 2018-9-10 19:19 11733

[原创]PE文件格式之重定位表

2018-9-10 19:19
11733
这几天跟女朋友分手了,心里不爽,丧了几天……
不说废话开始:

       一、重定向表的作用:

    这个在网上有很多种解释,那我也说一下自己对这个表的解释,程序运行的时候一般有两种方式来调用函数就是OD的那个CALL,一个是基址+偏移,另一种就是写死的函数地址比如 CALL 0x78441354这样的,对于这样一个exe程序,他有两个dll:a.dll b.dll 如果两个的dll的基址(ImageBase)都是10000000h但是a.dll先加载了占用了这个地址,那么当b.dll加载的时候就会加载到其他的位置,比如12000000H的位置。
     但是在b.dll中有些地址是根据ImageBase固定的,被写死了的,而且是绝对地址不是相对偏移地址。比如b.dll中存在一个call 0X01034560,这是一个绝对地址,其相   对于ImageBase的地址为δ = 0X01034560 - 0X01000000 = 0X34560H;而此时的内存中b.dll存在的地址是1200000H开始的内存,加载器分配的ImageBase和b.dll中原来默认的ImageBase(1000000H)相差了200000H,因此该call的值也应该加上这个差值,被修正为0X01234560H,那么
δ = 0X01234560H - 0X01200000H = 0X34560H则相对不变。否则call的地址不修正会导致call指令跳转的地址不是实际要跳转的地址,获取不到正确的函数指令,程序则不能正常运行。
      由于一个dll中的需要修正的地址不止一两个,可能有很多,所以用一张表记录那些“写死”的地址,将来加载进内存时,可能需要一一修正,这张表称作为重定位表,一般每一个PE文件都有一个重定位表。当加载器加载程序时,如果加载器为某PE(.exe、.dll)分配的基址与其自身默认记录的ImageBase不相同,那么该程序文件加载完毕后就需要修正重定位表中的所有需要修正的地址。如果加载器分配的基址和该程序文件中记录默认的ImageBase相同,则不需要修正,重定位表对于该dll也是没有效用的。比如test.exe和a.dll的重定位表都是不起作用的(由于一般情况.exe运行时被第一个加载,所以exe文件一般没有重定位表,但是不代表所有exe都没有重定位表)。同理如果先加载b.dll后加载a.dll,那么b.dll的重定位表就不起作用了。 


二、重定位表的结构解析:

重定向表位于数据目录项的第6位:
typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;//RVA
    DWORD   SizeOfBlock;
} IMAGE_BASE_RELOCATION,* PIMAGE_BASE_RELOCATION;
#define IMAGE_SIZEOF_BASE_RELOCATION         8
该结构体有两个成员:一个是地址,一个是大小。如下图所示:一个重定位表由多个大小SizeOfBlock的Block组成,(不同块的SizeOfBlock大小不一)。每一个块记录了1000H即4KB大小的内存中需要重定位信息的地址(一页大小),这些地址以VirtualAdress为该页的基址,偏移地址占两个字节(1000H最多需要12bit即可:0~FFFH)。所以两个字节的低12位为偏移地址,而高4位就是一个标记,当此标记为0011(3)时低12为才有效,否则该2个字节可能是为了对齐而产生的,并且为对齐而产生的字节其值全为0。

不会插入表格,贴个图,大家凑活着看,这个重定位表是通过页表来存储的信息的,而且有16个二进制位来表示,高四位是用来存储标记是否需要重定向的标记,VirtualAddress就相当于基址低12位就相当于偏移,这样就能表示出一块需要重定位的函数地址数据,如果标记是0那么就代表这个数据是用来填充保证对齐的数据无意义。
重定向表位于数据目录项的第6位:
typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;//RVA
    DWORD   SizeOfBlock;
} IMAGE_BASE_RELOCATION,* PIMAGE_BASE_RELOCATION;
#define IMAGE_SIZEOF_BASE_RELOCATION         8
该结构体有两个成员:一个是地址,一个是大小。如下图所示:一个重定位表由多个大小SizeOfBlock的Block组成,(不同块的SizeOfBlock大小不一)。每一个块记录了1000H即4KB大小的内存中需要重定位信息的地址(一页大小),这些地址以VirtualAdress为该页的基址,偏移地址占两个字节(1000H最多需要12bit即可:0~FFFH)。所以两个字节的低12位为偏移地址,而高4位就是一个标记,当此标记为0011(3)时低12为才有效,否则该2个字节可能是为了对齐而产生的,并且为对齐而产生的字节其值全为0。

不会插入表格,贴个图,大家凑活着看,这个重定位表是通过页表来存储的信息的,而且有16个二进制位来表示,高四位是用来存储标记是否需要重定向的标记,VirtualAddress就相当于基址低12位就相当于偏移,这样就能表示出一块需要重定位的函数地址数据,如果标记是0那么就代表这个数据是用来填充保证对齐的数据无意义。

三、解析:

 1、通过IMAGE_DATA_DIRECTORY结构的VirtualAddress 属性 找到第一个IMAGE_BASE_RELOCATION
2、判断一共有几块数据: 最后一个结构的VirtualAddress与SizeOfBlock都为0
3、具体项 宽度:2字节  也就是这个数据  内存中的页大小是1000H 也就是说2的12次方 就可以表示  一个页内所有的偏移地址 具体项的宽度是16字节 高四位  代表类型:值为3 代表的是需要修改的数据 值为0代表的是  用于数据对齐的数据,可以不用修改.也就是说 我们只关注  高4位的值为3的就可以了.
4、VirtualAddress 宽度:4字节 当前这一个块的数据,每一个低12位的值+VirtualAddress 才是 真正需要修复的数据的RVA 真正的RVA = VirtualAddress + 具体项的低12位
5、SizeOfBlock 宽度:4字节 当前块的总大小 具体项的数量 = (SizeOfBlock - 8)/2 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2018-9-21 13:15 被kanxue编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (17)
雪    币: 871
活跃值: (9841)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
2
就佩服老哥.女朋友有么用.代码才是真爱.
2018-9-10 22:52
1
雪    币: 36
活跃值: (102)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
mark
2018-9-12 17:29
0
雪    币: 5734
活跃值: (1737)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
IMAGE_BASE_RELOCATION 结构后面是隐藏数组
Count = (SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
2018-9-12 19:22
0
雪    币: 2046
活跃值: (265)
能力值: ( LV7,RANK:104 )
在线值:
发帖
回帖
粉丝
5
小艾 IMAGE_BASE_RELOCATION 结构后面是隐藏数组 Count = (SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHO ...
隐藏数组?怎么讲?
2018-9-12 19:28
0
雪    币: 2046
活跃值: (265)
能力值: ( LV7,RANK:104 )
在线值:
发帖
回帖
粉丝
6
张新琪 就佩服老哥.女朋友有么用.代码才是真爱.[em_13]
还是琪哥看得开啊
2018-9-12 19:28
0
雪    币: 205
活跃值: (98)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
mark
2018-9-13 15:36
0
雪    币: 293
活跃值: (287)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
女朋友的作用:第二杯半价 
最后于 2018-9-13 16:27 被瀚海云烟编辑 ,原因:
2018-9-13 16:27
0
雪    币: 1535
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
瀚海云烟 女朋友的作用:第二杯半价 
不要说得这么直白...
2018-9-18 09:06
0
雪    币: 9
活跃值: (180)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
10
Mark
2018-10-1 14:09
0
雪    币: 175
活跃值: (2531)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
写的真好,学习了。
2018-10-1 17:23
0
雪    币: 291
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
请问如何打印物理页编号?
2019-6-30 05:02
0
雪    币: 174
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
看看!!!!!!
2019-6-30 09:24
0
雪    币: 83
活跃值: (1087)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
14
666啊 
2019-7-1 14:17
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
 pDataDirectory = (PIMAGE_DATA_DIRECTORY)((DWORD)pOptionalHeader + 96);
RelocationFOA = RVATOFOA(pDataDirectory->VirtualAddress, RelocationFOA, pFileBuffer);
你这里转的是DataDirectory[16]中的第一个数据项,搞笑呢?
2020-3-10 15:39
0
雪    币: 7377
活跃值: (4081)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
mb_lowsphez pDataDirectory = (PIMAGE_DATA_DIRECTORY)((DWORD)pOptionalHeader + 96); RelocationFOA = RVATOFOA(pD ...
是不是漏看了这一行 for (size_t i = 0; i < 5; i++, pDataDirectory++);
2020-3-10 16:51
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
htpidk 是不是漏看了这一行 for (size_t i = 0; i < 5; i++, pDataDirectory++);
     我是这么写的:rva转foa
       DWORD nBaseRLC_Addr_rva = pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
       DWORD nBaseRLC_Addr_foa = RVATOFOA(nBaseRLC_Addr_rva, pFileBuffer);
另外判断特征是不是【3】我是这么写的:

                       PWORD recaddr = (PWORD)((DWORD)nBaseRLC + sizeof(IMAGE_BASE_RELOCATION) +        i * 2);
                       DWORD testcharac = (*recaddr>> 12);
                       
                       if (testcharac == 0x03) {
                               DWORD relcoAddr = (*recaddr & 0x0FFF) + nBaseRLC->VirtualAddress;//把特征值过滤掉
              
                               printf("第%d个                类型[%d]                地址%#x                地址%#x\n", i + 1, testcharac, relcoAddr, recaddr);
                       }
                       else
                               printf("第%d个                类型[%d]                地址%#x\n", i + 1, testcharac, recaddr);
最后于 2020-3-13 14:45 被mb_lowsphez编辑 ,原因:
2020-3-13 14:44
0
雪    币: 0
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18

第二段“原来默认的ImageBase(1000000H)相差了200000H”  这里的1000000H少了个0吧 XD


好像是前面的“比如12000000H的位置” 多打了个0?我看书里提到“默认情况下,EXE文件在内存中的基地址是0x00400000,DLL文件是0x10000000”

最后于 2020-10-13 20:22 被y4ung编辑 ,原因: 好像回复错了
2020-10-13 19:51
0
游客
登录 | 注册 方可回帖
返回
//