exe文件解析_PE文件格式之重定位表
exe⽂件解析_PE⽂件格式之重定位表
⼀、重定向表的作⽤:
这个在⽹上有很多种解释,那我也说⼀下⾃⼰对这个表的解释,程序运⾏的时候⼀般有两种⽅式来调⽤函数就是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也是没有效⽤的。⽐如和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
上代码:
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
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, 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
def explode_crc32_c1(dst):
if dst == 0x0:
return ord('X')
for i in range(31, 127):
if(dst == 32(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)
32(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)
32(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)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。