exe文件解析_PE文件格式之重定位表

exe文件解析_PE文件格式之重定位表_第1张图片

一、重定向表的作用:

这个在网上有很多种解释,那我也说一下自己对这个表的解释,程序运行的时候一般有两种方式来调用函数就是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那么就代表这个数据是用来填充保证对齐的数据无意义。

三、解析:


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

但是这个地方查资料,在吾爱上有个大牛说重定向表是多少个就是多少个并不存在结束的标记位 直至某个块首结构的VirtualAddress为 0,表明重定位表结束。 这个地方没明白有点懵懂……(链接:https://www.52pojie.cn/thread-562035-1-1.html)

上代码:

 PIMAGE_DOS_HEADER  pDosHeader = NULL;
 PIMAGE_NT_HEADERS  pNTHeader = NULL;
 PIMAGE_FILE_HEADER pFileHeader = NULL;
 PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
 PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
 PIMAGE_BASE_RELOCATION     pRelocation = NULL;
 PIMAGE_SECTION_HEADER   pSectionHeader = NULL;
 
 DWORD RelocationFOA = 0;
 
 LPVOID pFileBuffer  = NULL;
 
 //打开文件
 ReadPEFile(INDLLPATH,&pFileBuffer);
  
 //判断文件是否打开成功
 if (!pFileBuffer){
 printf("文件打开失败/n");
 free(pFileBuffer);
 }
 
 
 //给各个头赋值
 
 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
 pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)(((DWORD)pFileHeader) + IMAGE_SIZEOF_FILE_HEADER);
 pDataDirectory = (PIMAGE_DATA_DIRECTORY)((DWORD)pOptionalHeader + 96);
 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
 
 for (size_t i = 0; i < 5; i++, pDataDirectory++);
  
  
 RelocationFOA = RVATOFOA(pDataDirectory->VirtualAddress, RelocationFOA, pFileBuffer);
  
  
 //定位到重定位表
 pRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer+RelocationFOA);
  
 BYTE secName[9] = { 0 };
 for (int i = 1; pRelocation->VirtualAddress && pRelocation->SizeOfBlock; i++)
 {
 DWORD size = (pRelocation->SizeOfBlock - 8) / 2;
 //先判断这个页属于哪个节里边 打印信息
 DWORD upper = 0;
 DWORD lower = 0;
 DWORD FOA = 0;
 
 FOA = RVATOFOA(pRelocation->VirtualAddress, FOA, pFileBuffer);
 for (size_t j = 0; j < pFileHeader->NumberOfSections; j++,pSectionHeader++)
 {
 lower = RVATOFOA(pSectionHeader->VirtualAddress, lower,pFileBuffer);
 upper = RVATOFOA(pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize, upper, pFileBuffer);
 if (FOA >= lower&&FOA <= upper){
 memcpy(secName, pSectionHeader->Name, 9);   //将节的名字复制到名字空间
 break;
 }
 }
 
 //打印出所有的重定向表的所在节的名称
 printf("t第%d个重定向表tt所在的节的名称%sttn",(i+1),secName);
 //定位到第一个块的首字节
 WORD* RelocaAddr = (WORD*)((BYTE*)pRelocation + 8);
 printf("=================第%d个页中所有的重定向数据============================n",i);
 for (DWORD t = 0; t < size ; t++)
 {
 //先判断是真正的重定向函数的基址,还是用来填充做为对齐的数据
 //确定重定向表的函数物理偏移
 //低12位是函数的偏移   物理地址=基址+偏移
 DWORD RelocaAdd = (RelocaAddr[t] & 0X0FFF) + FOA;
 WORD test = RelocaAddr[t] >> 12;
 if (test == 0){
 printf("第%d位tt类型[%d]n", t + 1, test);
 }
 else
 {
 printf("tt第%d位tt类型[%d]tt地址[%x]n",t+1,test,RelocaAdd);
 }
 }
 memset(secName, 0, 9);
 //进行下一页的判断
 pRelocation = (PIMAGE_BASE_RELOCATION)((BYTE*)pRelocation + pRelocation->SizeOfBlock);
  
 }
  
 
}

运行结果:
大概就是这个思路,如果有什么不对的地方希望前辈可以指正,毕竟不断改错才能进步嘛。我接着去沉浸在失恋的阴影中了……

from struct import *
import binascii
import base64
import string
 
Offst_xor_func = 0x00
Offst_func_size = 0x08
Offst_s_start_addr = 0x0c
Offst_s_len = 0x10
Offst_dst_pos = 0x14
Offst_xor_data_pos = 0x18
Offst_x_data = 0x20
 
fib_list = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738, 1293530146158671551, 13493690561280548289, 14787220707439219840, 9834167195010216513, 6174643828739884737, 16008811023750101250, 3736710778780434371, 1298777728820984005, 5035488507601418376, 6334266236422402381, 11369754744023820757, 17704020980446223138, 10627031650760492279, 9884308557497163801, 2064596134548104464, 11948904692045268265, 14013500826593372729, 7515661444929089378, 3082418197812910491, 10598079642741999869, 13680497840554910360, 5831833409587358613, 1065587176432717357, 6897420586020075970, 7963007762452793327, 14860428348472869297, 4376692037216111008, 790376311979428689, 5167068349195539697, 5957444661174968386, 11124513010370508083, 17081957671545476469, 9759726608206432936, 8394940206042357789, 18154666814248790725, 8102862946581596898, 7810785687120836007]
orig_bs_table = [0x2A, 0x39, 0x5F, 0x64, 0xC2, 0xA7, 0x46, 0x23,
0x53, 0x6B, 0x74, 0x47, 0x28, 0x4D, 0x70, 0x42,
0x49, 0x25, 0x52, 0x6A, 0x62, 0x38, 0x40, 0x4A,
0x69, 0x45, 0x44, 0x59, 0x2D, 0x31, 0x24, 0x50,
0x67, 0x79, 0x54, 0x21, 0x4C, 0x76, 0x71, 0x66,
0x2B, 0x63, 0x68, 0x6D, 0x51, 0x57, 0x4F, 0x30,
0x65, 0x4E, 0x5A, 0x34, 0x75, 0x6E, 0x33, 0x6C,
0x37, 0x48, 0x26, 0x32, 0x77, 0x61, 0x7A, 0x4B]
for i in range(len(orig_bs_table)):
 orig_bs_table[i] = chr(orig_bs_table[i])
 
def dump_data_core(filename, data_addr, base_addr, size):
 fp= open(filename, "rb")
 offset = data_addr - base_addr
 fp.seek(offset,0)
 f = fp.read()
 type_format = '<' #default
 if size == 1:
 type_format += 'B'
 elif size == 2:
 type_format += 'H'
 elif size == 4:
 type_format += 'I'
 elif size == 8:
 type_format += 'Q'
 else:
 print "[warn]"
 type_format += 'x'
 target_data, = unpack(type_format, f[:size])
 return target_data
 
def dump_data_specific(filename, start_addr, base_addr, order, species, size, count):
 arr_data = []
 for i in range(count):
 data_addr = start_addr + 0x120*order + species + i*size
 arr_data.append(hex(dump_data_core(filename, data_addr, base_addr, size)))
 return arr_data
 
def explode_fib_num(dst):
 for i in range(len(fib_list)):
 if(dst == fib_list[i]):
 return i-1
 return "Error"
 
def spec_fib(key_1, key_pos, filename, start_addr, base_addr, order, species, size, count):
 res_arr = dump_data_specific(filename, start_addr, base_addr, order, species, size, count)
 print 'order:'+str(order)+' '+str(res_arr)
 for i in range(len(res_arr)):
 if(res_arr[i][-1] == 'L'):
 c = chr(explode_fib_num(int(res_arr[i][:-1] ,16)))
 key_1[key_pos+i] = c
 else:
 c = chr(explode_fib_num(int(res_arr[i],16)))
 key_1[key_pos+i] = c
 return key_1
 
def explode_crc32_c1(dst):
 if dst == 0x0:
 return ord('X')
 for i in range(31, 127):
 if(dst == binascii.crc32(chr(i)) &0xffffffff):
 return chr(i)
 return 'E1ROR'
 
def explode_crc32_c2(dst):
 if dst == 0x0:
 return ord('X')
 for x0 in range(31, 127):
 for x1 in range(31, 127):
 s = chr(x0) + chr(x1)
 if(binascii.crc32(s) &0xffffffff) == dst:
 return s
 return 'E2ROR'
 
def explode_crc32_c3(dst):
 if dst == 0x0:
 return ord('X')
 for x0 in range(31, 127):
 for x1 in range(31, 127):
 for x2 in range(31, 127):
 s = chr(x0) + chr(x1) + chr(x2)
 if(binascii.crc32(s) &0xffffffff) == dst:
 return s
 return 'E3ROR'
 
def spec_crc32(key_1, key_pos, filename, start_addr, base_addr, order, species, size, count):
 res_arr = dump_data_specific(filename, start_addr, base_addr, order, species, size, 1) #dst_value must is 1*dword
 print 'order:'+str(order)+' '+str(res_arr)
 if count == 1:
 p = res_arr[0]
 if res_arr[0][-1] == 'L':
 p = res_arr[0][:-1]
 dec = explode_crc32_c1(int(p,16) 

你可能感兴趣的:(exe文件解析)