PE第四课——数据目录表

课堂笔记

在数据目录表中一般只关心导入表,导出表和资源这几个部分,但是资源实在是太复杂了,而且在一般的病毒木马中也不会存在资源,所以在这个工具中只是简单的解析了一下导出表和导出表。

在 VS 中,数据目录表的结构很简单。

1
2
3
4
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

就是一个虚拟地址和大小的字段。

它是一个数组结构,用宏定义可以看见什么表在某个下标的位置。

RVA和FRVA

RVA 转化为 FRVA 主要是通过某个数据在内存中的相对偏移地址找到其在文件中的相对偏移地址,在对某个程序进行逆向时,如果找到关键的那个变量或者那句指令,我根据变量或者代码指令在内存中的 RVA 找到它在文件中的偏移,就可以找到它的位置,修改它可能就可以破解某个程序。

所以我们写一个 RVA 转为 FRVA 的函数。

它有这么几个参数:一个是它本身的 rva 肯定要给,然后我们判断它在哪个区段,如果都不在第一个区段内肯定是说明在头这里,就直接 return 吧,这里说是 return rva,但是我感觉还是暂时 return 0 吧。

其次就是遍历区段,区段那边有 虚拟地址 + size,这两个字段足以确定虚拟地址的上下界,我们判断 rva 是否在其中就可以判断这个数据目录表是哪个区段的了。

这还是很好理解的。这节课就听了一个 RVA 转 FRVA 的一个函数写法,然后也没啥运行结果,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include<windows.h>
#include<stdio.h>
#include"main.h"
int main() {
FILE* fd = fopen("C:\\Users\\xia0ji233\\Desktop\\Home\\C++\\test.exe", "rb");
char* buffer = (char*)malloc(0x10000);
if (buffer == NULL) {
perror("malloc fail");
exit(0);
}
if (fd == NULL) {
perror("NO such File");
exit(0);
}
fread(buffer, 1, 0x10000, fd);
PIMAGE_DOS_HEADER pheader = (PIMAGE_DOS_HEADER)buffer;
printf("MS-DOS INFO:\n");
printf("MAGIC HEADER: ");
fwrite((char*)&pheader->e_magic, 2, 1, stdout);
putchar(10);
printf("PE OFFSET:%x\n", pheader->e_lfanew);
printf("PE INFO:\n");
PIMAGE_NT_HEADERS ReadNTHeaders = (PIMAGE_NT_HEADERS)(&(buffer[pheader->e_lfanew]));

printf("PE Magic Header:");
fwrite(&ReadNTHeaders->Signature, 2, 1, stdout);
putchar(10);

printf("Standard Header info:");
printf("Platform:");
switch (ReadNTHeaders->FileHeader.Machine)
{
case IMAGE_FILE_MACHINE_I386:
printf("I386");
break;
case IMAGE_FILE_MACHINE_IA64:
printf("Intel 64");
break;
case IMAGE_FILE_MACHINE_AMD64:
printf("AMD 64");
break;
default:
printf("UNKnown Platform");
break;
}
putchar(10);

printf("Optional PE Header:");
printf("ImageBase:%08x\n", ReadNTHeaders->OptionalHeader.ImageBase);

PIMAGE_SECTION_HEADER ReadSectionHeader = IMAGE_FIRST_SECTION(ReadNTHeaders);
PIMAGE_FILE_HEADER pFileHeader = &ReadNTHeaders->FileHeader;
for (int i = 0; i < pFileHeader->NumberOfSections; i++) {
printf("Name(区段名称):%s\n", ReadSectionHeader[i].Name);
printf("Voffset(起始的相对虚拟地址):%08X\n", ReadSectionHeader[i].VirtualAddress);
printf("VSize(区段大小):%08X\n", ReadSectionHeader[i].SizeOfRawData);
printf("ROffset(文件偏移):%08X\n", ReadSectionHeader[i].PointerToRawData);
printf("RSize(文件中区段大小):%08X\n", ReadSectionHeader[i].Misc.VirtualSize);
printf("标记(区段的属性):%08X\n\n", ReadSectionHeader[i].Characteristics);
}



system("pause");
}

DWORD RvaToFrva(DWORD Rva,char *buffer) {
//DOS
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
if (Rva < pSection[0].VirtualAddress) {
return 0;
}
for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) {
if (Rva >= pSection[i].VirtualAddress && Rva <= pSection[i].Misc.VirtualSize+pSection[i].VirtualAddress) {
//Rva-pSection[i].VirtualAddress相当于是数据目录表相对于区段的偏移加上该区段在文件中的位置
//就相当于数据目录表在文件中的位置了。
return Rva - pSection[i].VirtualAddress + pSection[i].PointerToRawData;
}
}
return 0;
}