文件解析器编写

文件解析器编写

mian函数

主要用来判断PE和ELF

判断依据是读取文件的开头, PE文件时MZ魔数, ELF则是ELF头的”ELF”

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
int main(void)
{
FILE* fp = NULL, *chrfp = NULL, *fp2 = NULL;
char file_path[MAX_LEN];//用于保存打开文件的路径
char flag_ELF[4];

printf("请输入文件路径:");
scanf("%s", file_path);

if ((fp = fopen(file_path, "rb")) == NULL) {//以二进制模式打开文件
printf("打开文件<%s>失败\n请重试", file_path);
return 0;
}
chrfp = fopen(file_path, "r");//上打开成功后直接再用文本模式打开
fp2 = fopen(file_path, "rb");//备用的文件指针
printf("打开文件<%s>成功\n", file_path);

//打开文件成功了以后读取文件头确定是PE还是ELF, 判断的依据就是ELF文件开头的第2, 3, 4字节为"ELF"
fseek(fp, 1, SEEK_SET);//移动文件指针
fread(flag_ELF, 3, 1, fp);//读取看是否是ELF
flag_ELF[3] = '\0';
fseek(fp, 0, SEEK_SET);//回到文件开头

if (!strcmp(flag_ELF, "ELF")) {//如果是ELF
printf("打开文件为ELF文件\n");
ELF_Viewer(fp);
}
else {
printf("打开文件为PE文件\n");
PE_Viewer(fp, chrfp, fp2);
}

fclose(fp);
fclose(fp2);
fclose(chrfp);

return 0;
}

RVA_to_RAW()编写

这个函数的作用就是将RVA转换成RAW

基本的公式: RAW - PointerToRawData = RVA - VirtualAddress

这个很容易理解, 就是通过相对距离来求出实际的文件偏移

重要的是我们需要知道PointerToRawData和VirtualAddress是属于哪个节区的, 而关于节区范围的信息我们又可以从文件头中得到, 所以要先从文件头中得到每个节区的范围, 然后看这个RVA是在哪个节区中, 最终确定PointerToRawData和VirtualAddress并得到了RAW

1
2
3
4
5
6
7
8
9
10
11
12
13
long RVA_to_RAW(long RVA)//将RVA转换成RAW
{
long RAW, i;
for (i = 0; i < NumberOfSections; i++)
{
if (RVA >= Start_of_section_VA[i] && RVA <= Start_of_section_VA[i] + Size_of_section[i])//遍历节区地址范围, 找到该RVA所属节区, 这些地址范围都是从文件头中得到的
{
RAW = RVA - Start_of_section_VA[i] + Start_of_section_RAW[i];
return RAW;
}
}
return 0;
}

PE_View()编写

就是分别调用解析不同位置的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void PE_Viewer(FILE* fp, FILE* chrfp, FILE* fp2) {
//char option;

DOS_head(fp);//解析DOS头的信息, 并打印出来, 注意文件指针是否要复原到开头位置

fseek(fp, NTheader_Offset, SEEK_SET);//用DOS头中记录的NT头文件偏移找到NT头的起始文件偏移
NT_head(fp);

fseek(fp, SectionHeader_Offset, SEEK_SET);
Section_head(fp);

fseek(fp, RVA_to_RAW(Import_RVA), SEEK_SET);//使用通过RVA_to_RAW的得到了获取导入表的RAW, 并将文件位置指针移动到此处
Import_View(fp, chrfp);

fseek(fp, RVA_to_RAW(Export_RVA), SEEK_SET);
Export_View(fp, chrfp);

INT_View(fp);//里面再改变fp, 这里只需要传入文件指针就行了

IAT_View(fp);
}

DOS头解析

这一个头的作用:

  • 文件的基本描述: 文件的页, 段, 初始sp和ip, 重定位表的文件偏移等
  • 历史遗留问题: 判断是否是DOS环境, 如是则运行DOS存根
  • 指引: 给出下一个文件头的地址

解析实现就是创建一个IMAGE_DOS_HEADER结构体变量, 然后读取文件到该结构体中, 最后打印.

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
void DOS_head(FILE* fp) {//注意传入的文件指针参数并不会影响调用进程的指针变量
IMAGE_DOS_HEADER dos_header;//定义DOS头结构体
fread(&dos_header, sizeof(IMAGE_DOS_HEADER), 1, fp);
printf("----------------------------------DOS头----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("魔数 %08lx %x\n", (long)&(dos_header.e_magic) - (long)&dos_header, dos_header.e_magic);
printf("文件最后一页的字节数 %08lx %x\n", (long)&(dos_header.e_cblp) - (long)&dos_header, dos_header.e_cblp);
printf("文件中的页数 %08lx %x\n", (long)&(dos_header.e_cp) - (long)&dos_header, dos_header.e_cp);
printf("重定位 %08lx %x\n", (long)&(dos_header.e_crlc) - (long)&dos_header, dos_header.e_crlc);
printf("段头的大小 %08lx %x\n", (long)&(dos_header.e_cparhdr) - (long)&dos_header, dos_header.e_cparhdr);
printf("所需额外段落的最小值 %08lx %x\n", (long)&(dos_header.e_minalloc) - (long)&dos_header, dos_header.e_minalloc);
printf("所需额外段落的最大值 %08lx %x\n", (long)&(dos_header.e_maxalloc) - (long)&dos_header, dos_header.e_maxalloc);
printf("初始(相对)SS值 %08lx %x\n", (long)&(dos_header.e_ss) - (long)&dos_header, dos_header.e_ss);
printf("初始sp值 %08lx %x\n", (long)&(dos_header.e_sp) - (long)&dos_header, dos_header.e_sp);
printf("校验和 %08lx %x\n", (long)&(dos_header.e_csum) - (long)&dos_header, dos_header.e_csum);
printf("初始IP值 %08lx %x\n", (long)&(dos_header.e_ip) - (long)&dos_header, dos_header.e_ip);
printf("初始(相对)CS值 %08lx %x\n", (long)&(dos_header.e_cs) - (long)&dos_header, dos_header.e_cs);
printf("重定位表的文件地址 %08lx %x\n", (long)&(dos_header.e_lfarlc) - (long)&dos_header, dos_header.e_lfarlc);
printf("叠加层数 %08lx %x\n", (long)&(dos_header.e_ovno) - (long)&dos_header, dos_header.e_ovno);
printf("保留字 %08lx %x %x %x %x\n", (long)dos_header.e_res - (long)&dos_header, dos_header.e_res[0], dos_header.e_res[1], dos_header.e_res[2], dos_header.e_res[3]);
printf("OEM标识符 %08lx %x\n", (long)&(dos_header.e_oemid) - (long)&dos_header, dos_header.e_oemid);
printf("OEM信息 %08lx %x\n", (long)&(dos_header.e_oeminfo) - (long)&dos_header, dos_header.e_oeminfo);
printf("保留字2 %08lx %x %x %x %x %x %x %x %x %x %x\n", (long)dos_header.e_res2 - (long)&dos_header, dos_header.e_res2[0], dos_header.e_res2[1], dos_header.e_res2[2], dos_header.e_res2[3], dos_header.e_res2[4], dos_header.e_res2[5], dos_header.e_res2[6], dos_header.e_res2[7], dos_header.e_res2[8], dos_header.e_res2[9]);
printf("下一个文件头地址 %08lx %x\n", (long)&(dos_header.e_lfanew) - (long)&dos_header, dos_header.e_lfanew);//下一个头即是NT头
NTheader_Offset = dos_header.e_lfanew;
printf("-------------------------------------------------------------------------\n");
printf("\n按回车键继续...\n");
getchar();
}

NT头解析

这个头的作用

  • NT头签名
  • 两个重要的头: 文件头和可选头都是NT头的成员
  • 其中可选头是最重要的头: 文件大小描述, DataDirectory, 对段的各种描述

解析实现跟上面差不多, 创建一个IMAGE_NT_HEADER32结构体变量然后读取文件并打印, 不过需要对一些我们创建的全局变量(重要的数据)进行赋值.

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
void NT_head(FILE* fp)
{
int option;
IMAGE_NT_HEADERS32 nt_header;//创建NT头结构体变量
fread(&nt_header, sizeof(IMAGE_NT_HEADERS32), 1, fp);
printf("----------------------------------NT头----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("NT头签名 %08lx %08lx\n", (long)&nt_header.Signature - (long)&nt_header + NTheader_Offset, nt_header.Signature);
printf("文件头 %08lx \n", (long)&nt_header.FileHeader - (long)&nt_header + NTheader_Offset);
printf("可选头 %08lx \n", (long)&nt_header.OptionalHeader - (long)&nt_header + NTheader_Offset);
printf("-------------------------------------------------------------------------\n");
printf("----------------------------------文件头----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("机器 %08lx %04x\n", (long)&nt_header.FileHeader.Machine - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.Machine);
printf("节区数 %08lx %04x\n", (long)&nt_header.FileHeader.NumberOfSections - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.NumberOfSections);
printf("时间戳 %08lx %08lx\n", (long)&nt_header.FileHeader.TimeDateStamp - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.TimeDateStamp);
printf("符号表偏移量 %08lx %08lx\n", (long)&nt_header.FileHeader.PointerToSymbolTable - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.PointerToSymbolTable);
printf("符号表中的符号数 %08lx %08lx\n", (long)&nt_header.FileHeader.NumberOfSymbols - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.NumberOfSymbols);
printf("可选头大小 %08lx %08lx\n", (long)&nt_header.FileHeader.SizeOfOptionalHeader - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.SizeOfOptionalHeader);//重要成员
printf("映射特征 %08lx %08lx\n", (long)&nt_header.FileHeader.Characteristics - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.Characteristics);
printf("-------------------------------------------------------------------------\n");
printf("----------------------------------可选头----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("魔数 %08lx %04x\n", (long)&nt_header.OptionalHeader.Magic - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.Magic);
printf("链接器主版本 %08lx %.2x\n", (long)&nt_header.OptionalHeader.MajorLinkerVersion - (long)&nt_header + NTheader_Offset, *((unsigned char* ) & nt_header.OptionalHeader.MajorLinkerVersion));//单字节数据需要进行类型转换
printf("链接器次版本 %08lx %.2x\n", (long)&nt_header.OptionalHeader.MinorLinkerVersion - (long)&nt_header + NTheader_Offset, *((unsigned char* ) & nt_header.OptionalHeader.MinorLinkerVersion));
printf("代码段的大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfCode - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfCode);
printf("已初始化数据段大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfInitializedData - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfInitializedData);
printf("未初始化数据段大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfUninitializedData - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfUninitializedData);
printf("入口函数指针 %08lx %08lx\n", (long)&nt_header.OptionalHeader.AddressOfEntryPoint - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.AddressOfEntryPoint);
printf("代码段基址 %08lx %08lx\n", (long)&nt_header.OptionalHeader.BaseOfCode - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.BaseOfCode);
printf("数据段基址 %08lx %08lx\n", (long)&nt_header.OptionalHeader.BaseOfData - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.BaseOfData);
printf("基址 %08lx %08lx\n", (long)&nt_header.OptionalHeader.ImageBase - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.ImageBase);
printf("节对齐量 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SectionAlignment - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SectionAlignment);
printf("文件对齐量 %08lx %08lx\n", (long)&nt_header.OptionalHeader.FileAlignment - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.FileAlignment);
printf("系统主版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MajorOperatingSystemVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MajorOperatingSystemVersion);
printf("系统次版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MinorOperatingSystemVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MinorOperatingSystemVersion);
printf("映像主版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MajorImageVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MajorImageVersion);
printf("映像次版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MinorImageVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MinorImageVersion);
printf("子系统主版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MajorSubsystemVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MajorSubsystemVersion);
printf("子系统次版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MinorSubsystemVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MinorSubsystemVersion);
printf("保留成员 %08lx %08lx\n", (long)&nt_header.OptionalHeader.Win32VersionValue - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.Win32VersionValue);
printf("映像大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfImage - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfImage);
printf("头总大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfHeaders - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfHeaders);
printf("校验和 %08lx %08lx\n", (long)&nt_header.OptionalHeader.CheckSum - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.CheckSum);
printf("子系统 %08lx %04x\n", (long)&nt_header.OptionalHeader.Subsystem - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.Subsystem);
printf("DLL特征 %08lx %04x\n", (long)&nt_header.OptionalHeader.DllCharacteristics - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DllCharacteristics);
printf("堆栈保留大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfStackReserve - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfStackReserve);
printf("堆栈提交大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfStackCommit - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfStackCommit);
printf("本地保留大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfHeapReserve - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfHeapReserve);
printf("本地提交大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfHeapCommit - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfHeapCommit);
printf("无用成员 %08lx %08lx\n", (long)&nt_header.OptionalHeader.LoaderFlags - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.LoaderFlags);
printf("可选头其余条目数 %08lx %08lx\n", (long)&nt_header.OptionalHeader.NumberOfRvaAndSizes - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.NumberOfRvaAndSizes);
printf("映射数据表 %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory - (long)&nt_header + NTheader_Offset);
printf("-------------------------------------------------------------------------\n");
printf("----------------------------------DataDirectory----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("导出表RVA %08lx %08lx\n", (long) & nt_header.OptionalHeader.DataDirectory[0].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[0].VirtualAddress);
printf("导出表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[0].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[0].Size);
printf("导入表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[1].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[1].VirtualAddress);
printf("导入表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[1].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[1].Size);
printf("异常表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[2].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[2].VirtualAddress);
printf("异常表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[2].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[2].Size);
printf("资源表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[3].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[3].VirtualAddress);
printf("资源表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[3].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[3].Size);
printf("证书表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[4].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[4].VirtualAddress);
printf("证书表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[4].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[4].Size);
printf("基址重定位表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[5].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[5].VirtualAddress);
printf("基址重定位表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[5].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[5].Size);
printf("调试信息RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[6].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[6].VirtualAddress);
printf("调试信息大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[6].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[6].Size);
printf("特定体系结构数据RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[7].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[7].VirtualAddress);
printf("特定体系结构数据大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[7].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[7].Size);
printf("全局指针寄存器RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[8].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[8].VirtualAddress);
printf("全局指针寄存器大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[8].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[8].Size);
printf("TLS表RVA %08lx %08x\n", (long)&nt_header.OptionalHeader.DataDirectory[9].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[9].VirtualAddress);
printf("TLS表大小 %08lx %08x\n", (long)&nt_header.OptionalHeader.DataDirectory[9].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[9].Size);
printf("加载配置表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[10].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[10].VirtualAddress);
printf("加载配置表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[10].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[10].Size);
printf("绑定导入表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[11].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[11].VirtualAddress);
printf("绑定导入表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[11].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[11].Size);
printf("导入地址表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[12].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[12].VirtualAddress);
printf("导入地址表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[12].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[12].Size);
printf("延迟导入表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[13].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[13].VirtualAddress);
printf("延迟导入表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[13].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[13].Size);
printf("CLR运行时头部数据RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[14].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[14].VirtualAddress);
printf("CLR运行时头部数据大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[14].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[14].Size);
printf("保留RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[15].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[15].VirtualAddress);
printf("保留大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[15].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[15].Size);
printf("-------------------------------------------------------------------------\n");
printf("\n按回车键继续\n");
getchar();
//将重要的成员赋值给全局变量, 便于后面使用
NumberOfSections = nt_header.FileHeader.NumberOfSections;
SectionHeader_Offset = (long)&nt_header.OptionalHeader.Magic - (long)&nt_header + NTheader_Offset + nt_header.FileHeader.SizeOfOptionalHeader;//使用文件头中对可选头大小的描述加上可选头的文件偏移即可得到节区头的起始地址
SectionAlignment = nt_header.OptionalHeader.SectionAlignment;
FileAlignment = nt_header.OptionalHeader.FileAlignment;
ImageBase = nt_header.OptionalHeader.ImageBase;
AddressOfEntryPoint = nt_header.OptionalHeader.AddressOfEntryPoint;
//Import_Addr = (unsigned long)nt_header.OptionalHeader.DataDirectory[1].VirtualAddress - (unsigned long)nt_header.OptionalHeader.ImageBase;//还不确定这个是不是就是VA, 文件头中的地址时相对虚拟地址
Import_RVA = nt_header.OptionalHeader.DataDirectory[1].VirtualAddress;
Import_Size = nt_header.OptionalHeader.DataDirectory[1].Size;
Export_RVA = nt_header.OptionalHeader.DataDirectory[0].VirtualAddress;
Export_Size = nt_header.OptionalHeader.DataDirectory[1].Size;
//printf("测试: Import_Addr = %08lx\n", Import_Addr);

节区头

原理同上, 只不过这里我们要记录每个节区的起始RVA和虚拟大小, 这些值用于我们的RVA_to_RAW中的节区范围确定.

然后就是节区数量(NumberOfSection)是在可选头中得到的, 根据节区的数量, 我们可以知道节区头的数量, 因为二者是一一对应的.

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
void Section_head(FILE* fp)
{
long i;
IMAGE_SECTION_HEADER sh;
for (i = 0; i < NumberOfSections; i++)//根据节区数来判断节区头数量
{
fread(&sh, sizeof(IMAGE_SECTION_HEADER), 1, fp);//读取一个节区头, 并移动文件位置指针

printf("----------------------------------%s节区头----------------------------------\n", sh.Name);
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n");
printf("-------------------------------------------------------------------------\n");
printf("名称 %08lx %s\n", (long)sh.Name - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.Name);// + (long)sizeof(IMAGE_SECTION_HEADER) * i + SectionHeader_Offset
printf("加载至内存的虚拟大小 %08lx %08lx\n", (long)&sh.Misc.VirtualSize - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.Misc.VirtualSize);
printf("RVA %08lx %08lx\n", (long)&sh.VirtualAddress - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.VirtualAddress);
printf("对齐后的尺寸 %08lx %08lx\n", (long)&sh.SizeOfRawData - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.SizeOfRawData);
printf("RAW %08lx %08lx\n", (long)&sh.PointerToRawData - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.PointerToRawData);
printf("重定位偏移 %08lx %08lx\n", (long)&sh.PointerToRelocations - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.PointerToRelocations);
printf("行号表偏移 %08lx %08lx\n", (long)&sh.PointerToLinenumbers - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.PointerToLinenumbers);
printf("重定位项数 %08lx %04x\n", (long)&sh.NumberOfRelocations - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.NumberOfRelocations);
printf("行号表行数 %08lx %04x\n", (long)&sh.NumberOfLinenumbers - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.NumberOfLinenumbers);
printf("节区属性 %08lx %08lx\n", (long)&sh.Characteristics - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.Characteristics);
printf("-------------------------------------------------------------------------\n");
//这里开始记录每个节区的起始位置, 方便后面我们计算导入导出表的文件偏移, 要再for循环里面
Start_of_section_VA[i] = sh.VirtualAddress;
Start_of_section_RAW[i] = sh.PointerToRawData;
Size_of_section[i] = sh.Misc.VirtualSize;
}
printf("\n按回车键继续...\n");
getchar();
}

导入表解析

导入表的RVA是在可选头中的DataDirectory[1]中得到的, 我们通过RVA_to_RAW转换得到了其文件偏移, 然后通过更新IMAGE_IMPORT_DESCRIPTOR变量IID获取每一个导入描述符, 因为导入描述符是连在一起的, 而且结尾为空结构体, 所以可以以此来作为判断依据.

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
void Import_View(FILE* fp, FILE* chrfp)//chrfp用来在文件中找到相应字符串, 针对Name这种双重间接指针才需要使用
{
int i;
IMAGE_IMPORT_DESCRIPTOR IID;
long Import_RAW = RVA_to_RAW(Import_RVA);

Number_Import = 0;

printf("----------------------------------导出表----------------------------------\n");
if (Import_Size == 0) {//其实按逻辑应该写在外面的PE_Viewer中的, 不过因为是全局变量而且为了美观就写在这里面了
printf("由于Size为0, 所以导入表为空\n");
printf("\n按回车键继续\n");
getchar();
return;
}

printf("----------------------------------导入表----------------------------------\n");
for (i = 0; i < 20; i++)//找到每一个导入描述符, 起始相当与while, 只不过有个i方便一点
{ //一开始在RVA_to_RAW函数中有错误, 返回值if一直判断错, 导致返回值一直为0, 但是修改i < 999为i < 50以后又可以正常运行, 佛系
//换了一个程序有再一次出现了这个错误, 调试发现使全局变量Start_of_section_VA[20]直接被一个字符串给覆盖掉了, 试了挺久发现初始化全局变量为1解决了, 初始化后的全局变量位置从bss到了数据段, 而且使强符号(因为没有同名符号, 所以跟这个没关系), 原因应该是全局变量所在的位置
fread(&IID, sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, fp);
if (!IID.OriginalFirstThunk)//最后会有一个空结构体, 表示结构体数组的结尾, 我们检测是否是空结构体来判断导入了多少个模块
return;
//printf("当前RVA = %08lx\n", IID.Name);
fseek(chrfp, RVA_to_RAW(IID.Name), SEEK_SET);//注意Name为RVA, 需要转换
fscanf(chrfp, "%s", Import_Module_Name[i]);
printf("----------------------------------%s导入描述符----------------------------------\n", Import_Module_Name[i]);
printf("成员 文件偏移 值\n");
printf("-------------------------------------------------------------------------\n");
printf("INT名称表的RVA %08lx %08lx\n", (long) & IID.OriginalFirstThunk - (long) & IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.OriginalFirstThunk);
printf("日期戳 %08lx %08lx\n", (long)&IID.TimeDateStamp - (long)&IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.TimeDateStamp);
printf("ForwarderChain %08lx %08lx\n", (long)&IID.ForwarderChain - (long)&IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.ForwarderChain);
printf("导入映像名称指针 %08lx %08lx\n", (long)&IID.Name - (long)&IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.Name);
printf("IAT地址表的RVA %08lx %08lx\n", (long)&IID.FirstThunk - (long)&IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.FirstThunk);
printf("-------------------------------------------------------------------------\n");
INT_RVA[i] = IID.OriginalFirstThunk;
IAT_RVA[i] = IID.FirstThunk;
Number_Import++;//记录导入的数量
}
printf("\n按回车键继续\n");
getchar();
}

同时我们还要记录INT和IAT的RVA方便后面解析INT和IAT使用.

INT解析

之前看的逆向核心工程原理看得还是不够仔细所以在编写解析INT时会一卡一卡的, 不翻书就不知道怎么去写

这里画一个图来看看会比较直观一些

image.png

我的误区在于认为INT中直接存储的就是我们所需要的IMAGE_IMPORT_BY_NAME结构体, 而实际上存有的时该结构体的指针. 为什么要这样做, 是因为我们不是对模块中所有的函数进行导入, 而是要进行一个筛选的过程, 而这个筛选的过程就是通过将需要的函数的IMAGE_IMPORT_BY_NAME指针放入INT中.

INT的RVA是在解析导入描述符时获取的, 下面的IAT也是同理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void INT_View(FILE* fp)
{
long i, j = 1, tmp;
long IIBN_RVA;//INT是, IIBN指针的数组, 每个元素都是IIBN的指针, 注意是RVA需要转换
for (i = 0; i < Number_Import; i++)
{
tmp = RVA_to_RAW(INT_RVA[i]);
fseek(fp, tmp, SEEK_SET);//由于INT只针对单个导入模块, 所以需要循环
fread(&IIBN_RVA, 4, 1, fp);//首先先读取一个结构体指针, 后面的while用IIBN_RVA的值是否为NULL来作为循环判断依据
printf("----------------------------------<%02ld>INT----------------------------------\n", i + 1);
printf("成员 文件偏移 值\n");
printf("-------------------------------------------------------------------------\n");
while (IIBN_RVA)
{
printf("%04d %08lx %08lx\n", j, tmp + 4 * (j - 1), IIBN_RVA);
fread(&IIBN_RVA, 4, 1, fp);//更新下一个INT条目
j++;
}
}

IAT解析

思路同INT几乎一致, 只不过原本存有IIBN结构体指针变成了函数的位置指针.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void IAT_View(FILE* fp)
{
long i, j = 1, tmp;
long RVA;//IAT是, 函数指针的数组, 每个元素都是函数的指针, 注意是RVA, 需要转换

for (i = 0; i < Number_Import; i++)
{
tmp = RVA_to_RAW(IAT_RVA[i]);
fseek(fp, tmp, SEEK_SET);//由于IAT只针对单个导入模块, 所以需要循环
fread(&RVA, 4, 1, fp);//首先先读取一个函数指针, 后面的while用RVA的值是否为NULL来作为循环判断依据
printf("----------------------------------<%02ld>IAT----------------------------------\n", i + 1);
printf("成员 文件偏移 值\n");
printf("-------------------------------------------------------------------------\n");
while (RVA)
{
printf("%04ld %08lx %08lx\n", j, tmp + 4 * (j - 1), RVA);
fread(&RVA, 4, 1, fp);//更新下一个IAT条目
j++;
}
}
}

代码

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
#define MAX_LEN 120
#include <Windows.h>
#include <stdio.h>
#include <string.h>

void ELF_Viewer(FILE* fp);

void PE_Viewer(FILE* fp, FILE* chrfp, FILE* fp2);
void DOS_head(FILE* fp);
void NT_head(FILE* fp);
void Section_head(FILE* fp);
long RVA_to_RAW(long RVA);
void Import_View(FILE* fp, FILE* chrfp);
void Export_View(FILE* fp, FILE* chrfp);
void INT_View(FILE* fp);
void IAT_View(FILE* fp);

//重要数据设置为全局变量
long NTheader_Offset = 1;//在解析DOS头时记录NT头的文件偏移
long NumberOfSections = 1;
long ImageBase = 1;
long AddressOfEntryPoint = 1;
long SectionAlignment = 1;
long FileAlignment = 1;
long SectionHeader_Offset = 1;
long Import_RVA = 1;
long Import_Size = 1;//还没用到, 不知道后面会不会用, 要用的话后面去NT_head()里面赋值就行了
long Export_RVA = 1;
long Export_Size = 1;//同上
long Start_of_section_VA[20] = {1, };//用于存储节区的VA起始地址
long Size_of_section[20] = {1, };
long Start_of_section_RAW[20] = {1, };//用于存储节区的RAW起始地址
char Import_Module_Name[50][20] = {1, };
long INT_RVA[20] = {1 , };
long IAT_RVA[20] = {1 , };
long Number_Import = 1;

int main(void)
{
FILE* fp = NULL, *chrfp = NULL, *fp2 = NULL;
char file_path[MAX_LEN];//用于保存打开文件的路径
char flag_ELF[4];

printf("请输入文件路径:");
scanf("%s", file_path);

if ((fp = fopen(file_path, "rb")) == NULL) {//以二进制模式打开文件
printf("打开文件<%s>失败\n请重试", file_path);
return 0;
}
chrfp = fopen(file_path, "r");//上打开成功后直接再用文本模式打开
fp2 = fopen(file_path, "rb");//备用的文件指针
printf("打开文件<%s>成功\n", file_path);

//打开文件成功了以后读取文件头确定是PE还是ELF, 判断的依据就是ELF文件开头的第2, 3, 4字节为"ELF"
fseek(fp, 1, SEEK_SET);//移动文件指针
fread(flag_ELF, 3, 1, fp);//读取看是否是ELF
flag_ELF[3] = '\0';
fseek(fp, 0, SEEK_SET);//回到文件开头

if (!strcmp(flag_ELF, "ELF")) {//如果是ELF
printf("打开文件为ELF文件\n");
ELF_Viewer(fp);
}
else {
printf("打开文件为PE文件\n");
PE_Viewer(fp, chrfp, fp2);
}

fclose(fp);
fclose(fp2);
fclose(chrfp);

return 0;
}

void ELF_Viewer(FILE* fp) {

}

void PE_Viewer(FILE* fp, FILE* chrfp, FILE* fp2) {
//char option;

DOS_head(fp);//解析DOS头的信息, 并打印出来, 注意文件指针是否要复原到开头位置

fseek(fp, NTheader_Offset, SEEK_SET);//用DOS头中记录的NT头文件偏移找到NT头的起始文件偏移
NT_head(fp);

fseek(fp, SectionHeader_Offset, SEEK_SET);
Section_head(fp);

fseek(fp, RVA_to_RAW(Import_RVA), SEEK_SET);//使用通过RVA_to_RAW的得到了获取导入表的RAW, 并将文件位置指针移动到此处
Import_View(fp, chrfp);

fseek(fp, RVA_to_RAW(Export_RVA), SEEK_SET);
Export_View(fp, chrfp);

INT_View(fp);//里面再改变fp, 这里只需要传入文件指针就行了

IAT_View(fp);
}

void DOS_head(FILE* fp) {//注意传入的文件指针参数并不会影响调用进程的指针变量
IMAGE_DOS_HEADER dos_header;//定义DOS头结构体
fread(&dos_header, sizeof(IMAGE_DOS_HEADER), 1, fp);
printf("----------------------------------DOS头----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("魔数 %08lx %x\n", (long)&(dos_header.e_magic) - (long)&dos_header, dos_header.e_magic);
printf("文件最后一页的字节数 %08lx %x\n", (long)&(dos_header.e_cblp) - (long)&dos_header, dos_header.e_cblp);
printf("文件中的页数 %08lx %x\n", (long)&(dos_header.e_cp) - (long)&dos_header, dos_header.e_cp);
printf("重定位 %08lx %x\n", (long)&(dos_header.e_crlc) - (long)&dos_header, dos_header.e_crlc);
printf("段头的大小 %08lx %x\n", (long)&(dos_header.e_cparhdr) - (long)&dos_header, dos_header.e_cparhdr);
printf("所需额外段落的最小值 %08lx %x\n", (long)&(dos_header.e_minalloc) - (long)&dos_header, dos_header.e_minalloc);
printf("所需额外段落的最大值 %08lx %x\n", (long)&(dos_header.e_maxalloc) - (long)&dos_header, dos_header.e_maxalloc);
printf("初始(相对)SS值 %08lx %x\n", (long)&(dos_header.e_ss) - (long)&dos_header, dos_header.e_ss);
printf("初始sp值 %08lx %x\n", (long)&(dos_header.e_sp) - (long)&dos_header, dos_header.e_sp);
printf("校验和 %08lx %x\n", (long)&(dos_header.e_csum) - (long)&dos_header, dos_header.e_csum);
printf("初始IP值 %08lx %x\n", (long)&(dos_header.e_ip) - (long)&dos_header, dos_header.e_ip);
printf("初始(相对)CS值 %08lx %x\n", (long)&(dos_header.e_cs) - (long)&dos_header, dos_header.e_cs);
printf("重定位表的文件地址 %08lx %x\n", (long)&(dos_header.e_lfarlc) - (long)&dos_header, dos_header.e_lfarlc);
printf("叠加层数 %08lx %x\n", (long)&(dos_header.e_ovno) - (long)&dos_header, dos_header.e_ovno);
printf("保留字 %08lx %x %x %x %x\n", (long)dos_header.e_res - (long)&dos_header, dos_header.e_res[0], dos_header.e_res[1], dos_header.e_res[2], dos_header.e_res[3]);
printf("OEM标识符 %08lx %x\n", (long)&(dos_header.e_oemid) - (long)&dos_header, dos_header.e_oemid);
printf("OEM信息 %08lx %x\n", (long)&(dos_header.e_oeminfo) - (long)&dos_header, dos_header.e_oeminfo);
printf("保留字2 %08lx %x %x %x %x %x %x %x %x %x %x\n", (long)dos_header.e_res2 - (long)&dos_header, dos_header.e_res2[0], dos_header.e_res2[1], dos_header.e_res2[2], dos_header.e_res2[3], dos_header.e_res2[4], dos_header.e_res2[5], dos_header.e_res2[6], dos_header.e_res2[7], dos_header.e_res2[8], dos_header.e_res2[9]);
printf("下一个文件头地址 %08lx %x\n", (long)&(dos_header.e_lfanew) - (long)&dos_header, dos_header.e_lfanew);//下一个头即是NT头
NTheader_Offset = dos_header.e_lfanew;
printf("-------------------------------------------------------------------------\n");
printf("\n按回车键继续...\n");
getchar();
}
void NT_head(FILE* fp)
{
int option;
IMAGE_NT_HEADERS32 nt_header;//创建NT头结构体变量
fread(&nt_header, sizeof(IMAGE_NT_HEADERS32), 1, fp);
printf("----------------------------------NT头----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("NT头签名 %08lx %08lx\n", (long)&nt_header.Signature - (long)&nt_header + NTheader_Offset, nt_header.Signature);
printf("文件头 %08lx \n", (long)&nt_header.FileHeader - (long)&nt_header + NTheader_Offset);
printf("可选头 %08lx \n", (long)&nt_header.OptionalHeader - (long)&nt_header + NTheader_Offset);
printf("-------------------------------------------------------------------------\n");
printf("----------------------------------文件头----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("机器 %08lx %04x\n", (long)&nt_header.FileHeader.Machine - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.Machine);
printf("节区数 %08lx %04x\n", (long)&nt_header.FileHeader.NumberOfSections - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.NumberOfSections);
printf("时间戳 %08lx %08lx\n", (long)&nt_header.FileHeader.TimeDateStamp - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.TimeDateStamp);
printf("符号表偏移量 %08lx %08lx\n", (long)&nt_header.FileHeader.PointerToSymbolTable - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.PointerToSymbolTable);
printf("符号表中的符号数 %08lx %08lx\n", (long)&nt_header.FileHeader.NumberOfSymbols - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.NumberOfSymbols);
printf("可选头大小 %08lx %08lx\n", (long)&nt_header.FileHeader.SizeOfOptionalHeader - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.SizeOfOptionalHeader);//重要成员
printf("映射特征 %08lx %08lx\n", (long)&nt_header.FileHeader.Characteristics - (long)&nt_header + NTheader_Offset, nt_header.FileHeader.Characteristics);
printf("-------------------------------------------------------------------------\n");
printf("----------------------------------可选头----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("魔数 %08lx %04x\n", (long)&nt_header.OptionalHeader.Magic - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.Magic);
printf("链接器主版本 %08lx %.2x\n", (long)&nt_header.OptionalHeader.MajorLinkerVersion - (long)&nt_header + NTheader_Offset, *((unsigned char* ) & nt_header.OptionalHeader.MajorLinkerVersion));//单字节数据需要进行类型转换
printf("链接器次版本 %08lx %.2x\n", (long)&nt_header.OptionalHeader.MinorLinkerVersion - (long)&nt_header + NTheader_Offset, *((unsigned char* ) & nt_header.OptionalHeader.MinorLinkerVersion));
printf("代码段的大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfCode - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfCode);
printf("已初始化数据段大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfInitializedData - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfInitializedData);
printf("未初始化数据段大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfUninitializedData - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfUninitializedData);
printf("入口函数指针 %08lx %08lx\n", (long)&nt_header.OptionalHeader.AddressOfEntryPoint - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.AddressOfEntryPoint);
printf("代码段基址 %08lx %08lx\n", (long)&nt_header.OptionalHeader.BaseOfCode - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.BaseOfCode);
printf("数据段基址 %08lx %08lx\n", (long)&nt_header.OptionalHeader.BaseOfData - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.BaseOfData);
printf("基址 %08lx %08lx\n", (long)&nt_header.OptionalHeader.ImageBase - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.ImageBase);
printf("节对齐量 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SectionAlignment - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SectionAlignment);
printf("文件对齐量 %08lx %08lx\n", (long)&nt_header.OptionalHeader.FileAlignment - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.FileAlignment);
printf("系统主版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MajorOperatingSystemVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MajorOperatingSystemVersion);
printf("系统次版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MinorOperatingSystemVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MinorOperatingSystemVersion);
printf("映像主版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MajorImageVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MajorImageVersion);
printf("映像次版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MinorImageVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MinorImageVersion);
printf("子系统主版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MajorSubsystemVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MajorSubsystemVersion);
printf("子系统次版本 %08lx %04x\n", (long)&nt_header.OptionalHeader.MinorSubsystemVersion - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.MinorSubsystemVersion);
printf("保留成员 %08lx %08lx\n", (long)&nt_header.OptionalHeader.Win32VersionValue - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.Win32VersionValue);
printf("映像大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfImage - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfImage);
printf("头总大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfHeaders - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfHeaders);
printf("校验和 %08lx %08lx\n", (long)&nt_header.OptionalHeader.CheckSum - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.CheckSum);
printf("子系统 %08lx %04x\n", (long)&nt_header.OptionalHeader.Subsystem - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.Subsystem);
printf("DLL特征 %08lx %04x\n", (long)&nt_header.OptionalHeader.DllCharacteristics - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DllCharacteristics);
printf("堆栈保留大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfStackReserve - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfStackReserve);
printf("堆栈提交大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfStackCommit - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfStackCommit);
printf("本地保留大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfHeapReserve - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfHeapReserve);
printf("本地提交大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.SizeOfHeapCommit - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.SizeOfHeapCommit);
printf("无用成员 %08lx %08lx\n", (long)&nt_header.OptionalHeader.LoaderFlags - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.LoaderFlags);
printf("可选头其余条目数 %08lx %08lx\n", (long)&nt_header.OptionalHeader.NumberOfRvaAndSizes - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.NumberOfRvaAndSizes);
printf("映射数据表 %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory - (long)&nt_header + NTheader_Offset);
printf("-------------------------------------------------------------------------\n");
printf("----------------------------------DataDirectory----------------------------------\n");
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n\n");
printf("导出表RVA %08lx %08lx\n", (long) & nt_header.OptionalHeader.DataDirectory[0].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[0].VirtualAddress);
printf("导出表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[0].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[0].Size);
printf("导入表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[1].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[1].VirtualAddress);
printf("导入表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[1].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[1].Size);
printf("异常表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[2].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[2].VirtualAddress);
printf("异常表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[2].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[2].Size);
printf("资源表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[3].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[3].VirtualAddress);
printf("资源表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[3].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[3].Size);
printf("证书表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[4].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[4].VirtualAddress);
printf("证书表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[4].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[4].Size);
printf("基址重定位表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[5].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[5].VirtualAddress);
printf("基址重定位表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[5].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[5].Size);
printf("调试信息RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[6].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[6].VirtualAddress);
printf("调试信息大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[6].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[6].Size);
printf("特定体系结构数据RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[7].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[7].VirtualAddress);
printf("特定体系结构数据大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[7].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[7].Size);
printf("全局指针寄存器RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[8].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[8].VirtualAddress);
printf("全局指针寄存器大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[8].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[8].Size);
printf("TLS表RVA %08lx %08x\n", (long)&nt_header.OptionalHeader.DataDirectory[9].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[9].VirtualAddress);
printf("TLS表大小 %08lx %08x\n", (long)&nt_header.OptionalHeader.DataDirectory[9].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[9].Size);
printf("加载配置表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[10].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[10].VirtualAddress);
printf("加载配置表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[10].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[10].Size);
printf("绑定导入表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[11].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[11].VirtualAddress);
printf("绑定导入表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[11].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[11].Size);
printf("导入地址表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[12].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[12].VirtualAddress);
printf("导入地址表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[12].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[12].Size);
printf("延迟导入表RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[13].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[13].VirtualAddress);
printf("延迟导入表大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[13].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[13].Size);
printf("CLR运行时头部数据RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[14].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[14].VirtualAddress);
printf("CLR运行时头部数据大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[14].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[14].Size);
printf("保留RVA %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[15].VirtualAddress - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[15].VirtualAddress);
printf("保留大小 %08lx %08lx\n", (long)&nt_header.OptionalHeader.DataDirectory[15].Size - (long)&nt_header + NTheader_Offset, nt_header.OptionalHeader.DataDirectory[15].Size);
printf("-------------------------------------------------------------------------\n");
printf("\n按回车键继续\n");
getchar();
//将重要的成员赋值给全局变量, 便于后面使用
NumberOfSections = nt_header.FileHeader.NumberOfSections;
SectionHeader_Offset = (long)&nt_header.OptionalHeader.Magic - (long)&nt_header + NTheader_Offset + nt_header.FileHeader.SizeOfOptionalHeader;//使用文件头中对可选头大小的描述加上可选头的文件偏移即可得到节区头的起始地址
SectionAlignment = nt_header.OptionalHeader.SectionAlignment;
FileAlignment = nt_header.OptionalHeader.FileAlignment;
ImageBase = nt_header.OptionalHeader.ImageBase;
AddressOfEntryPoint = nt_header.OptionalHeader.AddressOfEntryPoint;
Import_RVA = nt_header.OptionalHeader.DataDirectory[1].VirtualAddress;
Import_Size = nt_header.OptionalHeader.DataDirectory[1].Size;
Export_RVA = nt_header.OptionalHeader.DataDirectory[0].VirtualAddress;
Export_Size = nt_header.OptionalHeader.DataDirectory[1].Size;
//printf("测试: Import_Addr = %08lx\n", Import_Addr);
}
void Section_head(FILE* fp)
{
long i;
IMAGE_SECTION_HEADER sh;
for (i = 0; i < NumberOfSections; i++)//根据节区数来判断节区头数量
{
fread(&sh, sizeof(IMAGE_SECTION_HEADER), 1, fp);//读取一个节区头, 并移动文件位置指针

printf("----------------------------------%s节区头----------------------------------\n", sh.Name);
printf("-------------------------------------------------------------------------\n");
printf("成员 地址 值\n");
printf("-------------------------------------------------------------------------\n");
printf("名称 %08lx %s\n", (long)sh.Name - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.Name);// + (long)sizeof(IMAGE_SECTION_HEADER) * i + SectionHeader_Offset
printf("加载至内存的虚拟大小 %08lx %08lx\n", (long)&sh.Misc.VirtualSize - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.Misc.VirtualSize);
printf("RVA %08lx %08lx\n", (long)&sh.VirtualAddress - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.VirtualAddress);
printf("对齐后的尺寸 %08lx %08lx\n", (long)&sh.SizeOfRawData - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.SizeOfRawData);
printf("RAW %08lx %08lx\n", (long)&sh.PointerToRawData - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.PointerToRawData);
printf("重定位偏移 %08lx %08lx\n", (long)&sh.PointerToRelocations - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.PointerToRelocations);
printf("行号表偏移 %08lx %08lx\n", (long)&sh.PointerToLinenumbers - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.PointerToLinenumbers);
printf("重定位项数 %08lx %04x\n", (long)&sh.NumberOfRelocations - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.NumberOfRelocations);
printf("行号表行数 %08lx %04x\n", (long)&sh.NumberOfLinenumbers - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.NumberOfLinenumbers);
printf("节区属性 %08lx %08lx\n", (long)&sh.Characteristics - (long)&sh + SectionHeader_Offset + (long)sizeof(IMAGE_SECTION_HEADER) * i, sh.Characteristics);
printf("-------------------------------------------------------------------------\n");
//这里开始记录每个节区的起始位置, 方便后面我们计算导入导出表的文件偏移, 要再for循环里面
Start_of_section_VA[i] = sh.VirtualAddress;
Start_of_section_RAW[i] = sh.PointerToRawData;
Size_of_section[i] = sh.Misc.VirtualSize;
}
printf("\n按回车键继续...\n");
getchar();
}
void Import_View(FILE* fp, FILE* chrfp)//chrfp用来在文件中找到相应字符串, 针对Name这种双重间接指针才需要使用
{
int i;
IMAGE_IMPORT_DESCRIPTOR IID;
long Import_RAW = RVA_to_RAW(Import_RVA);

Number_Import = 0;

printf("----------------------------------导出表----------------------------------\n");
if (Import_Size == 0) {//其实按逻辑应该写在外面的PE_Viewer中的, 不过因为是全局变量而且为了美观就写在这里面了
printf("由于Size为0, 所以导入表为空\n");
printf("\n按回车键继续\n");
getchar();
return;
}

printf("----------------------------------导入表----------------------------------\n");
for (i = 0; i < 20; i++)//找到每一个导入描述符, 起始相当与while, 只不过有个i方便一点
{ //一开始在RVA_to_RAW函数中有错误, 返回值if一直判断错, 导致返回值一直为0, 但是修改i < 999为i < 50以后又可以正常运行, 佛系
//换了一个程序有再一次出现了这个错误, 调试发现使全局变量Start_of_section_VA[20]直接被一个字符串给覆盖掉了, 试了挺久发现初始化全局变量为1解决了, 初始化后的全局变量位置从bss到了数据段, 而且使强符号(因为没有同名符号, 所以跟这个没关系), 原因应该是全局变量所在的位置
fread(&IID, sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, fp);
if (!IID.OriginalFirstThunk)//最后会有一个空结构体, 表示结构体数组的结尾, 我们检测是否是空结构体来判断导入了多少个模块
return;
//printf("当前RVA = %08lx\n", IID.Name);
fseek(chrfp, RVA_to_RAW(IID.Name), SEEK_SET);//注意Name为RVA, 需要转换
fscanf(chrfp, "%s", Import_Module_Name[i]);
printf("----------------------------------%s导入描述符----------------------------------\n", Import_Module_Name[i]);
printf("成员 文件偏移 值\n");
printf("-------------------------------------------------------------------------\n");
printf("INT名称表的RVA %08lx %08lx\n", (long) & IID.OriginalFirstThunk - (long) & IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.OriginalFirstThunk);
printf("日期戳 %08lx %08lx\n", (long)&IID.TimeDateStamp - (long)&IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.TimeDateStamp);
printf("ForwarderChain %08lx %08lx\n", (long)&IID.ForwarderChain - (long)&IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.ForwarderChain);
printf("导入映像名称指针 %08lx %08lx\n", (long)&IID.Name - (long)&IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.Name);
printf("IAT地址表的RVA %08lx %08lx\n", (long)&IID.FirstThunk - (long)&IID + Import_RAW + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), IID.FirstThunk);
printf("-------------------------------------------------------------------------\n");
INT_RVA[i] = IID.OriginalFirstThunk;
IAT_RVA[i] = IID.FirstThunk;
Number_Import++;//记录导入的数量
}
printf("\n按回车键继续\n");
getchar();
}
void INT_View(FILE* fp)
{
long i, j = 1, tmp;
long IIBN_RVA;//INT是, IIBN指针的数组, 每个元素都是IIBN的指针, 注意是RVA需要转换
for (i = 0; i < Number_Import; i++)
{
tmp = RVA_to_RAW(INT_RVA[i]);
fseek(fp, tmp, SEEK_SET);//由于INT只针对单个导入模块, 所以需要循环
fread(&IIBN_RVA, 4, 1, fp);//首先先读取一个结构体指针, 后面的while用IIBN_RVA的值是否为NULL来作为循环判断依据
printf("----------------------------------<%02ld>INT----------------------------------\n", i + 1);
printf("成员 文件偏移 值\n");
printf("-------------------------------------------------------------------------\n");
while (IIBN_RVA)
{
printf("%04d %08lx %08lx\n", j, tmp + 4 * (j - 1), IIBN_RVA);
fread(&IIBN_RVA, 4, 1, fp);//更新下一个INT条目
j++;
}
}

printf("\n按回车键继续\n");
getchar();
}
void IAT_View(FILE* fp)
{
long i, j = 1, tmp;
long RVA;//IAT是, 函数指针的数组, 每个元素都是函数的指针, 注意是RVA, 需要转换

for (i = 0; i < Number_Import; i++)
{
tmp = RVA_to_RAW(IAT_RVA[i]);
fseek(fp, tmp, SEEK_SET);//由于IAT只针对单个导入模块, 所以需要循环
fread(&RVA, 4, 1, fp);//首先先读取一个函数指针, 后面的while用RVA的值是否为NULL来作为循环判断依据
printf("----------------------------------<%02ld>IAT----------------------------------\n", i + 1);
printf("成员 文件偏移 值\n");
printf("-------------------------------------------------------------------------\n");
while (RVA)
{
printf("%04ld %08lx %08lx\n", j, tmp + 4 * (j - 1), RVA);
fread(&RVA, 4, 1, fp);//更新下一个IAT条目
j++;
}
}
}
void Export_View(FILE* fp, FILE* chrfp)
{
IMAGE_EXPORT_DIRECTORY IED;
long Export_RAW;
//首先要知道PE仅有一个IMAGE_EXPORT_DIRECTORY结构体
printf("----------------------------------导出表----------------------------------\n");
if (Export_Size == 0) {//其实按逻辑应该写在外面的PE_Viewer中的, 不过因为是全局变量而且为了美观就写在这里面了
printf("由于Size为0, 所以导入表为空\n");
printf("\n按回车键继续\n");
getchar();
return;
}

Export_RAW = RVA_to_RAW(Export_RVA);
fseek(fp, Export_RAW, SEEK_SET);
fread(&IED, sizeof(IMAGE_EXPORT_DIRECTORY), 1, fp);

printf("----------------------------------导出描述符----------------------------------\n");
printf("成员 文件偏移 值\n");
printf("未使用 %08lx %08lx\n", (long)&IED.Characteristics - (long)&IED + Export_RAW, IED.Characteristics);
printf("时间戳 %08lx %08lx\n", (long)&IED.TimeDateStamp - (long)&IED + Export_RAW, IED.TimeDateStamp);
printf("未使用 %08lx %04x\n", (long)&IED.MajorVersion - (long)&IED + Export_RAW, IED.MajorVersion);
printf("未使用 %08lx %04x\n", (long)&IED.MinorVersion - (long)&IED + Export_RAW, IED.MinorVersion);
printf("导出表文件名指针 %08lx %08lx\n", (long)&IED.Name - (long)&IED + Export_RAW, IED.Name);
printf("导出表的起始序号 %08lx %08lx\n", (long)&IED.Base - (long)&IED + Export_RAW, IED.Base);
printf("导出函数个数 %08lx %08lx\n", (long)&IED.NumberOfFunctions - (long)&IED + Export_RAW, IED.NumberOfFunctions);
printf("以函数名导出函数个数 %08lx %08lx\n", (long)&IED.NumberOfNames - (long)&IED + Export_RAW, IED.NumberOfNames);
printf("EAT_RVA %08lx %08lx\n", (long)&IED.AddressOfFunctions - (long)&IED + Export_RAW, IED.AddressOfFunctions);
printf("ENT_RVA %08lx %08lx\n", (long)&IED.AddressOfNames - (long)&IED + Export_RAW, IED.AddressOfNames);
printf("导出函数序号表 %08lx %08lx\n", (long)&IED.AddressOfNameOrdinals - (long)&IED + Export_RAW, IED.AddressOfNameOrdinals);
printf("-------------------------------------------------------------------------\n");

printf("\n请按回车键继续\n");
getchar();
}

long RVA_to_RAW(long RVA)//将RVA转换成RAW
{
long RAW, i;
for (i = 0; i < NumberOfSections; i++)
{
if (RVA >= Start_of_section_VA[i] && RVA <= Start_of_section_VA[i] + Size_of_section[i])//遍历节区地址范围, 找到该RVA所属节区
{
RAW = RVA - Start_of_section_VA[i] + Start_of_section_RAW[i];
return RAW;
}
}
return 0;
}

/*
有一个思路就是先把函数都执行一遍, 这时全局变量都已经赋值完成, system("cls"), 然后再使用一个while循环提供选项来查看
*/

问题

如果有问题, 希望能指正, 这是我的邮箱lam3cr0w@gmail.com, 非常感谢!!!!

文章作者: LamのCrow
文章链接: http://example.com/2022/05/08/文件解析器编写and375a52cb87b22005816fe7a418ec6660/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 LamのCrow