什么是文件
存储数据的实体。
不同的文件是给不同的软件去使用的。不同的文件主要是格式不同。
格式就是数组的排列组织方式
。软件读取文件,按照固定的形式去解析文件的。
什么是PE文件
(Portable Executable)可执行 文件的缩写。这种类型的文件,是供windows系统解析,解析完了之后能够创建出进程去运行的文件。
PE头部信息
(DOS头,NT头,区段表)
我们学习PE文件学习的是什么呢??学习的就是PE文件的格式,学习格式就是在学习一堆结构体。很多东西需要记忆。
为了便于我们记忆,需要一些辅助性的工具。
PE头部粗略图
DOS头
简介
在windows系统中的可执行文件在设计的时候,考虑到了兼容性问题。在正常的可执行文件的一开始的部分。嵌入了一个DOS可执行文件。作用就是在MS-DOS系统下能够输出一行这个程序不是运行在此系统下的。
这里有两个字段是有用的:
第一个 e_magic 永远都是 0x4D 0x5A 0x5A4D 你需要知道大端和小端的知识。
最后一个 e_lfanew 它是真正的可执行文件的起始位置。
- 实验一:假如我们修改了e_magic字段或者e_lfanew,PE文件是否还能运行?
- 将e_magic修改为”OZ”,保存执行
- 将e_magic改回”MZ”,e_lfanew改为0x1000,保存执行
尝试之后不行,这两个字段是重要字段,抹掉程序就无法运行了。
- 实验二:抹掉除了e_magic和e_lfanew之外的字段,可不可以。
怎么找到DOS头
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
| PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage;
pDos->e_magic == IMAGE_DOS_SIGNATURE
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)pDos->e_lfanew;
HANDLE hFile = CreateFile( PATH, GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); DWORD dwFileSize = GetFileSize(hFile, NULL); char* buf = new char[dwFileSize] {0}; DWORD dwRealSize = 0; ReadFile(hFile, buf, dwFileSize, &dwRealSize, NULL);
|
NT头
1 2 3 4 5 6
| typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
|
怎么找到Nt头?
通过DOS头的最后一个字段:e_lfanew 指定的是NT头的位置
Signature:
标识:PE00
可以和魔数配合,判断是否是PE文件。
永远都是 0x50 0x45 0x00 0x00 0x00004550
1 2 3 4
| PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)pDos->e_lfanew;
|
IMAGE_FILE_HEADER:
1 2 3 4 5 6 7 8 9
| typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
|
重要的:
NumberOfSection:区段的数量
SizeOfOptionalHeader:扩展头的大小。因为扩展头中数据目录表的个数是不确定的。所以这里需要一个大小
有用的:
Machine:运行平台
TimeDateStamp:时间戳 表明是在什么时候编译的
手工解析
1 2
| 50 45 00 00 4C 01 05 00 91 CF 44 54 00 00 00 00 00 00 00 00 E0 00 02 01
|
50 45 00 00 PE标识
01 4C 运行平台
00 没用
05 区段数量
91 CF 44 54 时间戳
E0 扩展头大小
0201 属性
因为大端小端,我们需要反过来看
关于镜像,映像,虚拟地址,相对虚拟地址
镜像:就是PE文件自身
映像:就是根据PE文件映射出来的,
虚拟地址:程序中的内存地址,就是虚拟地址。
相对虚拟地址:就是相对于加载基址的偏移。
文件头
IMAGE_FILE_HEADER
文件头结构体
1 2 3 4 5 6 7 8 9
| typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
|
怎么找到文件头
1 2 3 4 5 6
| PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)pDos->e_lfanew;
PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)&pNt->FileHeader;
|
扩展头
IMAGE_OPTIONAL_HEADER:
找到扩展头
1 2 3 4 5 6
| PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)pDos->e_lfanew;
PIMAGE_OPTIONAL_HEADER pOption = (PIMAGE_OPTIONAL_HEADER)&pNt->OptionalHeader;
|
扩展头结构体
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
| typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[0x10]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
|
非常重要的:
ImageBase:程序的默认加载基址。
AddressOfEntryPoint: 程序的入口点(EP)。
比较重要的:
SectionAlignment: 内存对齐 0x1000(因为一页内存是4KB)
FileAlignment: 文件对齐 0x200
SizeOfImage: 映像大小(我这个PE文件被加载到内存,占用空间应该是多大)
SIzeOfHeader: 头部大小 DOS头+NT头+区块表的大小。
NumberOfRvaAndSizes: 数据目录表的元素个数
DllCharacteristics: PE的一组属性。。。。
极为重要的:
数据目录表
DataDirectory
1 2 3 4 5 6 7 8
| //打开PE文件后,第一个段就是DOS头部 PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; //pDos的e_lfanew字段可以指向NT头的空间,使用NT头接收 PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)pDos->e_lfanew; //NT头结构体的第三个参数就是指向扩展头 PIMAGE_OPTIONAL_HEADER pOption = (PIMAGE_OPTIONAL_HEADER)&pNt->OptionalHeader; //扩展头最后一个参数就是数据目录表 PIMAGE_DATA_DIRECTORY pDataDir = pOption->DataDirectory;
|
描述了PE文件中16个非常重要的数据块的大小和位置。
导出表
什么是导出
提供函数给其他模块使用的一种行为
怎么导出
方法1:声明导出
1 2 3
| #pragma once extern "C" _declspec(dllexport) void Fun1(); extern "C" _declspec(dllexport) void Fun2();
|
方法2:def文件导出
1 2 3
| EXPORTS Fun3 @1 Fun4 @2 NONAME
|
注意:NONAME 是只导出序号,没有名字
导出表的作用是什么? 没有它exe能运行吗?
用以记录本模块能够给其他模块提供的函数的信息。
函数名 函数地址 函数的序号
程序运行的时候,会检查主模块的导入表,看用了哪些其他模块,就会将此模块加载到进程空间中。加载进来之后,分析INT(IAT)得到函数名称,用这个名称去导出表中找到函数地址的RVA,RVA+模块基址,就是真正的函数地址,将此函数地址填充到IAT中,从而完成加载后的IAT功能。
一个PE文件,可以没有导出表的,比如exe文件,一般都没有。
已知一个dll名,和一个dll导出函数的名字, 如何得到这个函数名的地址?
得到DLL的导出表,然后在函数名称表中找函数名,如果找到了,由于序号表和名称表位置是一一对应的,就会得到序号表的下标,然后就将序号当成地址表的下标,从而得到地址。
GetProcAddress();
怎么才能知道一个exe都使用了哪些API?
分析exe的导入表即可,其中记录了模块名和函数名。
如何判断导入函数是以序号导入或是以名称导入?
IMAGE_THUNK_DATA32这个结构体,他的最高位是1的话,那么就只有序号,如果最高位是0的话,那么久有序号,也有名称。
怎么才知道导出函数是仅以序号导出还是以名称导出?
对于一个导出函数而言,他的地址表的下标,就是此函数的序号,如果这个序号,在序号表中,没有记录,那么他就是一个虚序号,也就是没有名称,只有序号。
怎么找到一个PE文件的导出信息
通过数据目录表的第0项:
手工分析过程:
获得信息:
导出表的RVA:00018D90 ——>FOA: 7590 (FOA= RVA - RVA区段+ FOA区段)
备注:
FOA:文件的偏移位置
RVA:相对虚拟地址
RVA区段:该段段首地址
FOA区段:文件偏移的段首地址
SIZE:5F21
模块名称在12个字节后的四个字节中,也就是0x0001DBEA
Base:00 00 00 01 索引基数
NumberOfFunctions:00 00 13 88 导出地址表中成员个数
NumberOfNames:00 00 00 03
AddressOfFunctions(rva):00 01 8D B8 导出地址表(EAT)
AddressOfNames(rva):00 01 DB D8 导出名称表(ENT)
AddressOfNameOrdinals;:0x1DBE4 指向导出序号表
1 2 3 4 5 6 7 8 9 10 11 12 13
| typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; // (1) 保留,恒为0x00000000 DWORD TimeDateStamp; // (2) 时间戳 WORD MajorVersion; // (3) 主版本号,一般不赋值 WORD MinorVersion; // (4) 子版本号,一般不赋值 DWORD Name; // (5) 模块名称* DWORD Base; // (6) 索引基数* DWORD NumberOfFunctions; // (7) 导出地址表中成员个数* DWORD NumberOfNames; // (8) 导出名称表中成员个数* DWORD AddressOfFunctions; // (9) 导出地址表(EAT)* DWORD AddressOfNames; // (10) 导出名称表(ENT)* DWORD AddressOfNameOrdinals; // (11) 指向导出序号表* }IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
|
模块名称0x0001DBEA 转换RVA —FOA: C3EA
找到dll.dll
地址表成员个数为1388转换成字节:1388h=5000d 十六进制转换为十进制
1388*4=4E20 十六进制数乘4
5000*4=20000 十进制数乘4
我们从地址表:0x 18DB8开始找,由于18DB8是RVA,这里需要转换FOA:75B8
AddressOfNameOrdinals;:1DBE4 指向导出序号表 转换FOA:C3E4
测试代码
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
|
#include <iostream> #include <Windows.h> #define PATH L"C:\\Users\\SouLinker\\Desktop\\dll.dll"
DWORD RvaToFoa(char* lpImage, DWORD dwRva); BOOL IsPE_File(char* lpImage); void AnalyzeExportsTabel(char* lpImage); int main() { HANDLE hFile = CreateFile( PATH, GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); DWORD dwFileSize = GetFileSize(hFile, NULL); char* buf = new char[dwFileSize] {0}; DWORD dwRealSize = 0; ReadFile(hFile, buf, dwFileSize, &dwRealSize, NULL); if (IsPE_File(buf) == TRUE) { AnalyzeExportsTabel(buf); } else { printf("这不是一个PE文件"); } delete[]buf; buf = nullptr; return 0; }
DWORD RvaToFoa(char* lpImage, DWORD dwRva) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_SECTION_HEADER pHeader = IMAGE_FIRST_SECTION(pNt);
if (dwRva < pNt->OptionalHeader.SizeOfHeaders) { return dwRva; } for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { DWORD dwSectionRva = pHeader[i].VirtualAddress; DWORD dwSectionEndRva = dwSectionRva + pHeader[i].SizeOfRawData; DWORD dwSectionFOA = pHeader[i].PointerToRawData; if (dwRva >= dwSectionRva && dwRva <= dwSectionEndRva) { pHeader[i].VirtualAddress; DWORD dwFOA = dwRva - dwSectionRva + dwSectionFOA; return dwFOA; } } return -1; } BOOL IsPE_File(char* lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; if (pDos->e_magic != IMAGE_DOS_SIGNATURE) { return FALSE; } PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage);
if (pNt->Signature != IMAGE_NT_SIGNATURE) { return FALSE; } return TRUE; }
void AnalyzeExportsTabel(char* lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_DATA_DIRECTORY pExportDir = &pNt->OptionalHeader.DataDirectory[0]; DWORD dwExportFOA = RvaToFoa(lpImage, pExportDir->VirtualAddress); PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(lpImage + dwExportFOA); DWORD dwBase = pExport->Base; DWORD EatFoa = RvaToFoa(lpImage,pExport->AddressOfFunctions); DWORD EntFoa = RvaToFoa(lpImage, pExport->AddressOfNames); DWORD EotFoa = RvaToFoa(lpImage, pExport->AddressOfNameOrdinals); PDWORD pEat= (PDWORD)(lpImage + EatFoa); PDWORD pEnt = (PDWORD)(lpImage + EntFoa); PWORD pEot = (PWORD)(lpImage + EotFoa); for (int i = 0; i < pExport->NumberOfFunctions; i++) { if (pEat[i] == 0) { continue; } int j = 0; int nSign = FALSE; for (; j < pExport->NumberOfNames; j++) { if (i == pEot[j]) { nSign = TRUE; break; } } if (nSign== TRUE) { DWORD dwFunNameFOA = RvaToFoa(lpImage, pEnt[j]); char* pFunName = lpImage + dwFunNameFOA; printf("序号:%4x 地址:%x 名称:%s\n", i+ dwBase, pEat[i], pFunName); } else { printf("序号:%4x 地址:%x 名称:NULL\n", i + dwBase, pEat[i]); } } }
|
导入表
什么是导入
当一个可执行文件使用到了其他模块中的函数的时候,就是导入行为。在PE文件中,有一个位置记录了此可执行文件 使用的所有其他模块的函数信息。这个位置就是导入表。
导入表的作用是什么?
在加载之前,导入表里面记录本模块所使用的哪些DLL中的哪些函数的名称信息。
在加载之后,导入表能够记录所使用的函数的地址。供程序运行期间,找到所使用的函数。
导入表结构体
1 2 3 4 5 6 7 8 9 10
| typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR;
|
OriginalFirstThunk与FirstThunk得到位置,也是一个结构体数组,定义如下:
1 2 3 4 5 6 7 8
| typedef struct _IMAGE_THUNK_DATA32 { union { PBYTE ForwarderString; PDWORD Function; DWORD Ordinal; PIMAGE_IMPORT_BY_NAME AddressOfData; } u1; } IMAGE_THUNK_DATA32;
|
- 当此结构体最高位为0的时候,且此时存储的是导入名称信息,PIMAGE_IMPORT_BY_NAME起作用
- 当此结构体最高位为1的时候,且此时存储的是导入名称信息, DWORD Ordinal起作用
- 当此结构体存储的是导入地址信息的时候,PDWORD Function起作用
手工解析
数据目录表的第1项,就是导入表信息:
RVA:19CE90 ———》 转为FOA: B890
SIZE:0168
1 2 3 4 5 6 7 8 9 10
| typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR;
|
Name: 0019E460 —–> 19CE60
OriginalFirstThunk: 0019D1CC —–>19BBCC (INT)
FirstThunk: 001504D4 —–> 14EED4(IAT)
加载之后,INT中的内容还是原来的内容
在OD中,加载之后,IAT里面,存储的已经是各个API的地址了。
测试代码
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
|
#include <iostream> #include <Windows.h> #define PATH L"C:\\Users\\hugan\\Desktop\\FileCleaner2.0.exe"
DWORD RvaToFoa(char* lpImage, DWORD dwRva); BOOL IsPE_File(char* lpImage); void AnalyzeImportTabel(char* lpImage, bool bAnalyzeInt); int main() { HANDLE hFile = CreateFile( PATH, GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); DWORD dwFileSize = GetFileSize(hFile, NULL); char* buf = new char[dwFileSize] {0}; DWORD dwRealSize = 0; ReadFile(hFile, buf, dwFileSize, &dwRealSize, NULL); if (IsPE_File(buf) == TRUE) { AnalyzeImportTabel(buf,true); } else { printf("这不是一个PE文件"); } delete[]buf; buf = nullptr; return 0; }
DWORD RvaToFoa(char* lpImage, DWORD dwRva) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_SECTION_HEADER pHeader = IMAGE_FIRST_SECTION(pNt);
if (dwRva < pNt->OptionalHeader.SizeOfHeaders) { return dwRva; } for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { DWORD dwSectionRva = pHeader[i].VirtualAddress; DWORD dwSectionEndRva = dwSectionRva + pHeader[i].SizeOfRawData; DWORD dwSectionFOA = pHeader[i].PointerToRawData; if (dwRva >= dwSectionRva && dwRva <= dwSectionEndRva) { pHeader[i].VirtualAddress; DWORD dwFOA = dwRva - dwSectionRva + dwSectionFOA; return dwFOA; } } return -1; } BOOL IsPE_File(char* lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; if (pDos->e_magic != IMAGE_DOS_SIGNATURE) { return FALSE; } PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage);
if (pNt->Signature != IMAGE_NT_SIGNATURE) { return FALSE; } return TRUE; }
void AnalyzeImportTabel(char* lpImage,bool bAnalyzeInt) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_DATA_DIRECTORY dwImportDir = &pNt->OptionalHeader.DataDirectory[1]; DWORD dwImportFOA = RvaToFoa(lpImage, dwImportDir->VirtualAddress); PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)(lpImage + dwImportFOA); while (pImportTable->Name!=0) { DWORD dwNameFoa = RvaToFoa(lpImage,pImportTable->Name); char* pDllName = (char*)(dwNameFoa + lpImage); printf("DllName:%s\n", pDllName);
PIMAGE_THUNK_DATA32 pNameTable = NULL; if (bAnalyzeInt == true) { DWORD Foa = RvaToFoa(lpImage, pImportTable->OriginalFirstThunk); pNameTable = (PIMAGE_THUNK_DATA32)(lpImage + Foa); } else { DWORD Foa = RvaToFoa(lpImage, pImportTable->FirstThunk); pNameTable = (PIMAGE_THUNK_DATA32)(lpImage + Foa); } while (pNameTable->u1.Ordinal!=0) { if (IMAGE_SNAP_BY_ORDINAL32(pNameTable->u1.Ordinal)==1) { printf(" 序号:%x,名称:NULL\n", pNameTable->u1.Ordinal&0x7FFFFFFF); } else { DWORD dwNameFoa = RvaToFoa(lpImage, pNameTable->u1.AddressOfData); PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)(dwNameFoa + lpImage); printf(" 序号:%x,名称:%s\n", pName->Hint,pName->Name); } pNameTable++; } printf("----------------------------\n"); pImportTable++; } }
|
重定位表
什么叫做重定位
一般情况下,exe的默认加载基址是0x0040 0000
会有一些代码,是这样的:
lea eax, ds:[0x0040 1100]
push eax
call printf
但是目前来看,几乎都不会加载到默认基址上。
比如说,exe加载到0x0050 0000上,那么以上的代码还对么??
不对了,怎么才能对呢??
lea eax, ds:[0x0050 1100]
push eax
call printf
这样就对了。
由于一个模块加载到什么位置,几乎是不确定的,所以每次程序运行,都需要将使用了VA的地方进行一次修改。这个修改的过程就称之为重定位。
有一个区域就记录着,程序中的哪些位置使用了VA。这个区域就叫做重定位表。
为什么模块不加载到默认基址上??
- 对于dll来说,他默认基址上经常已经被别的模块使用了。
- 对于exe来说,每次都加载到默认基址,是一个危险的行为。数据和代码的地址每次都是固定的,就很危险。对于exe来说,就有一个随机基址的功能,使得每次运行,基址都不一样。
手工解析重定位
1 2 3 4 5 6
| typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock;
} IMAGE_BASE_RELOCATION; typedef IMAGE_BASE_RELOCATION UNALIGNED IMAGE_BASE_RELOCATION;
|
VirtualAddress:0A9000 ——->FOA:76E00
size:3ACC
VirtualAddress:031000
SizeofBlock:30
数据 |
偏移 |
重定位位置 |
FOA |
要重定位的数据 |
3A7F |
A7F |
031000+A7F |
2E7F |
004A4840 |
3A85 |
A85 |
031000+A85 |
2E85 |
0048FC88 |
3A92 |
A92 |
031000+A92 |
2E92 |
004A4844 |
咱们再OD中,去看RVA是031000+A7F的地址,发现确实是被重定位过的了。
怎么重定位的呢??
原始VA - 默认基址 = 新VA - 新基址
验证:
0x00D6 0000 - 0x0040 0000 + 004A4840 得到就是0xE04840
测试代码
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
| #include <iostream> #include <Windows.h> #define PATH L"C:\\Users\\hugan\\Desktop\\FileCleaner2.0.exe"
DWORD RvaToFoa(char* lpImage, DWORD dwRva); BOOL IsPE_File(char* lpImage); void AnalyzeReloc(char* lpImage); int main() { setlocale(LC_ALL, "chs"); HANDLE hFile = CreateFile( PATH, GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); DWORD dwFileSize = GetFileSize(hFile, NULL); char* buf = new char[dwFileSize] {0}; DWORD dwRealSize = 0; ReadFile(hFile, buf, dwFileSize, &dwRealSize, NULL); if (IsPE_File(buf) == TRUE) { AnalyzeReloc(buf); } else { printf("这不是一个PE文件"); } delete[]buf; buf = nullptr; return 0; }
DWORD RvaToFoa(char* lpImage, DWORD dwRva) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_SECTION_HEADER pHeader = IMAGE_FIRST_SECTION(pNt);
if (dwRva < pNt->OptionalHeader.SizeOfHeaders) { return dwRva; } for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { DWORD dwSectionRva = pHeader[i].VirtualAddress; DWORD dwSectionEndRva = dwSectionRva + pHeader[i].SizeOfRawData; DWORD dwSectionFOA = pHeader[i].PointerToRawData; if (dwRva >= dwSectionRva && dwRva < dwSectionEndRva) { pHeader[i].VirtualAddress; DWORD dwFOA = dwRva - dwSectionRva + dwSectionFOA; return dwFOA; } } return -1; } BOOL IsPE_File(char* lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; if (pDos->e_magic != IMAGE_DOS_SIGNATURE) { return FALSE; } PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage);
if (pNt->Signature != IMAGE_NT_SIGNATURE) { return FALSE; } return TRUE; } struct TYPEOFFSET { WORD OFFSET : 12; WORD TYPE : 4; }; void AnalyzeReloc(char* lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_OPTIONAL_HEADER pOption = &pNt->OptionalHeader; PIMAGE_DATA_DIRECTORY pRelocDir = &pOption->DataDirectory[5];
PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION) (RvaToFoa(lpImage, pRelocDir->VirtualAddress)+ lpImage); while (pReloc->SizeOfBlock!=0) { TYPEOFFSET* typeoffset = (TYPEOFFSET*)(pReloc + 1); DWORD dwCount = (pReloc->SizeOfBlock - 8) / 2; DWORD dwBeginRva = pReloc->VirtualAddress; printf("----------------------------------\n"); for (int i = 0; i < dwCount; i++) { if (typeoffset[i].TYPE==3) { DWORD dwRelocRva = (dwBeginRva + typeoffset[i].OFFSET); printf("要重定位的位置RVA:%p\n", dwRelocRva); PDWORD pRelocData = (PDWORD)(RvaToFoa(lpImage, dwRelocRva) + lpImage); printf("要重定位的数据:%p\n", *pRelocData); } else { printf("类型是%d", typeoffset[i].TYPE); }
} printf("----------------------------------\n"); pReloc = (PIMAGE_BASE_RELOCATION)((char*)pReloc + pReloc->SizeOfBlock); }
}
|
资源表
菜单,对话框,图标,光标,位图,工具栏…..他们称之为资源,资源就是PE文件在运行的时候需要使用到的一些通用数据。编译的时候,将他们独立保存在一个区域中。
记录这些区域的一个结构,就是资源表了。
理解资源表
当咱们通过数据目录表找到资源表的时候,这个资源表分成了3层结构:
第一层:一共有多少种资源
第二层:这种资源有多少个
第三层:这个资源的位置
手工解析PE文件
找到资源表
找到资源表的数据目录:

RVA:1AF000———–> FOA:1A5400
SIZE:153058
分析第一层
NumberOfNamedEntries:0x2
NumberOfIdEntries:0xC
代表着后面有14个下面的结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { union { struct { DWORD NameOffset :31; DWORD NameIsString:1; }; DWORD Name; WORD Id; }; union { DWORD OffsetToData; struct { DWORD OffsetToDirectory:31; DWORD DataIsDirectory :1; }; }; }IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY;
|
第一个资源的信息:
Id:0x80008CC8 最高位是1,所以是以字符串为标识所以NameOffset:8CC8 这是一个相对于资源表起始位置的偏移
他不是RVA
1A5400+8CC8 = 1AE0C8
OffsetToData:0x80 00 00 80 最高位为1,所以下一层是目录
OffsetToDirectory:80 这是一个相对于资源表起始位置的偏移
他不是RVA
1A5400+80 = 1A5480
这个就是第二层的位置
Id:0x800095E0
NameOffset:95E0
1A5400+95E0 = 1AE9E0
OffsetToData:0x80001890
OffsetToDirectory:1890
1A5400+1890 = 1A6C90
这个就是第三层的位置了
OffsetToData:5F20
1A5400+5E20 =1AB220
和LordPE种解析出来的是一样的
代码解析
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
| #include <iostream> #include <Windows.h> #define PATH L"C:\\Users\\SouLinker\\Desktop\\FileCleaner2.0.exe"
#include <map> using std::map;
map<int, const char*> g_mapResourceInfo; map<const char*, const char*> abc; void InitResourceInfo() { g_mapResourceInfo[0x1] = "Cursor"; g_mapResourceInfo[0x2] = "BitMap"; g_mapResourceInfo[0x3] = "Icon"; g_mapResourceInfo[0x4] = "Menu"; g_mapResourceInfo[0x5] = "Dialog"; g_mapResourceInfo[0x6] = "String Table"; g_mapResourceInfo[0x7] = "Font Directory"; g_mapResourceInfo[0x8] = "Font"; g_mapResourceInfo[0x9] = "Accelerators"; g_mapResourceInfo[0xA] = "UnFormatted"; g_mapResourceInfo[0xB] = "Message Table"; g_mapResourceInfo[0xC] = "Group Cursor"; g_mapResourceInfo[0xE] = "Group Icon"; g_mapResourceInfo[0x10] = "Version Information"; }
DWORD RvaToFoa(char* lpImage, DWORD dwRva); BOOL IsPE_File(char* lpImage); void AnalyzeResource(char* lpImage); int main() { setlocale(LC_ALL, "chs"); InitResourceInfo(); HANDLE hFile = CreateFile( PATH, GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); DWORD dwFileSize = GetFileSize(hFile, NULL); char* buf = new char[dwFileSize] {0}; DWORD dwRealSize = 0; ReadFile(hFile, buf, dwFileSize, &dwRealSize, NULL); if (IsPE_File(buf) == TRUE) { AnalyzeResource(buf); } else { printf("这不是一个PE文件"); } delete[]buf; buf = nullptr; return 0; }
DWORD RvaToFoa(char* lpImage, DWORD dwRva) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_SECTION_HEADER pHeader = IMAGE_FIRST_SECTION(pNt);
if (dwRva < pNt->OptionalHeader.SizeOfHeaders) { return dwRva; } for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { DWORD dwSectionRva = pHeader[i].VirtualAddress; DWORD dwSectionEndRva = dwSectionRva + pHeader[i].SizeOfRawData; DWORD dwSectionFOA = pHeader[i].PointerToRawData; if (dwRva >= dwSectionRva && dwRva < dwSectionEndRva) { pHeader[i].VirtualAddress; DWORD dwFOA = dwRva - dwSectionRva + dwSectionFOA; return dwFOA; } } return -1; } BOOL IsPE_File(char* lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; if (pDos->e_magic != IMAGE_DOS_SIGNATURE) { return FALSE; } PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage);
if (pNt->Signature != IMAGE_NT_SIGNATURE) { return FALSE; } return TRUE; }
void AnalyzeResource(char* lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_OPTIONAL_HEADER pOption = &pNt->OptionalHeader; PIMAGE_DATA_DIRECTORY pResourceDir= &pOption->DataDirectory[2];
DWORD dwResourceFOA = RvaToFoa(lpImage, pResourceDir->VirtualAddress); PIMAGE_RESOURCE_DIRECTORY pFirstDir =(PIMAGE_RESOURCE_DIRECTORY)(dwResourceFOA + lpImage); PIMAGE_RESOURCE_DIRECTORY_ENTRY pFirstRes = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pFirstDir + 1); DWORD dwFirstCount = pFirstDir->NumberOfIdEntries + pFirstDir->NumberOfNamedEntries; for (int i = 0; i < dwFirstCount; i++) { if (pFirstRes[i].NameIsString == TRUE) { PIMAGE_RESOURCE_DIR_STRING_U pNameInfo = (PIMAGE_RESOURCE_DIR_STRING_U)(pFirstRes[i].NameOffset + (DWORD)pFirstDir); WCHAR* pName = new WCHAR[pNameInfo->Length + 1]{0}; for (int m = 0; m < pNameInfo->Length; m++) { pName[m] = pNameInfo->NameString[m]; } wprintf(L"资源种类标识:%s\n", pName); } else { if (g_mapResourceInfo.count(pFirstRes[i].Id) == 1) { printf("资源种类标识%s\n", g_mapResourceInfo[pFirstRes[i].Id]); } else { printf("资源种类标识%d\n", pFirstRes[i].Id); } } if (pFirstRes[i].DataIsDirectory == 1) { PIMAGE_RESOURCE_DIRECTORY pSecondDir = (PIMAGE_RESOURCE_DIRECTORY)(pFirstRes[i].OffsetToDirectory + (DWORD)pFirstDir); PIMAGE_RESOURCE_DIRECTORY_ENTRY pSecondRes = PIMAGE_RESOURCE_DIRECTORY_ENTRY(pSecondDir + 1); DWORD dwSecondCount = pSecondDir->NumberOfIdEntries + pSecondDir->NumberOfNamedEntries; for (int j = 0; j < dwSecondCount; j++) { if (pSecondRes[j].NameIsString == TRUE) { PIMAGE_RESOURCE_DIR_STRING_U pNameInfo = (PIMAGE_RESOURCE_DIR_STRING_U)(pSecondRes[j].NameOffset + (DWORD)pFirstDir); WCHAR* pName = new WCHAR[pNameInfo->Length + 1]{ 0 }; for (int m = 0; m < pNameInfo->Length; m++) { pName[m] = pNameInfo->NameString[m]; } wprintf(L" 资源标识:%s\n", pName); } else { wprintf(L" 资源标识:%d\n", pSecondRes[j].Id); } if (pSecondRes[j].DataIsDirectory == TRUE) { PIMAGE_RESOURCE_DIRECTORY pThirdDir = (PIMAGE_RESOURCE_DIRECTORY) (pSecondRes[j].OffsetToDirectory+ (DWORD)pFirstDir); PIMAGE_RESOURCE_DIRECTORY_ENTRY pThirdRes = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pThirdDir + 1);
PIMAGE_RESOURCE_DATA_ENTRY pData = (PIMAGE_RESOURCE_DATA_ENTRY)(pThirdRes->OffsetToData + (DWORD)pFirstDir); printf(" 资源的起始RVA:%x 资源的大小:%x\n", pData->OffsetToData, pData->Size); unsigned char* pRes = (unsigned char *) (RvaToFoa(lpImage, pData->OffsetToData) + lpImage); printf(" "); for (size_t m= 0; m < 10; m++) { printf("%x ", pRes[m]); } printf("\n"); } } } } }
|
TLS表
简介
线程局部存储:可以将TLS全局变量,在每一个线程中都创建一份,从而解决一定的线程同步问题。
测试代码
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
|
#include <iostream> #include <Windows.h> #pragma comment(linker, "/INCLUDE:__tls_used") int g_nNum1 = 100; _declspec(thread) int g_nNum2 = 200;
void NTAPI t_TlsCallBack_A(PVOID DllHandle, DWORD Reason, PVOID Red) { if (DLL_PROCESS_ATTACH == Reason) { printf("i am a mimidaima!\r\n"); } if (DLL_THREAD_DETACH == Reason) printf("t_TlsCallBack_A -> ThreadDetach!\r\n"); return; }
void NTAPI t_TlsCallBack_B(PVOID DllHandle, DWORD Reason, PVOID Red) { if (DLL_THREAD_DETACH == Reason) printf("t_TlsCallBack_B -> ThreadDetach!\r\n"); return; }
#pragma data_seg(".CRT$XLB") PIMAGE_TLS_CALLBACK p_thread_callback[] = { t_TlsCallBack_A, t_TlsCallBack_B, NULL }; #pragma data_seg()
DWORD WINAPI t_ThreadFun(PVOID pParam) { printf("%p ", &g_nNum1); printf("%p\n", &g_nNum2); return 0; }
int main() { printf("%p ", &g_nNum1); printf("%p\n", &g_nNum2); HANDLE hThread = CreateThread(NULL, 0, t_ThreadFun, NULL, 0, 0); WaitForSingleObject(hThread, -1); return 0; }
|
区段表
结构体数组
数组的元素个数由文件头中的NumberOfSection决定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| typedef struct _IMAGE_SECTION_HEADER { BYTE Name[0x8]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
|
怎么找到区段表
宏:IMAGE_FIRST_SECTION(NT头的地址)
计算:NT头的地址+0x4+0x14+0xE0
1 2 3 4 5 6
| PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)pDos->e_lfanew;
PIMAGE_SECTION_HEADER pHeader = IMAGE_FIRST_SECTION(pNt);
|
区段表中的一个元素描述的就是一个区段的信息
1 Name:区段名
2 PointerToRawData 在文件中的位置 FOA
3 SizeOfRawData在文件中的大小
4 VirtualAddress在内存中的位置 RVA
5 Misc.VirtualSize:在内存中的大小
6 Characteristics:区段的属性:可读 可写 可执行。。。。
区段表中的以下四个字段保存的是什么?
VirtualAddress: 区段起始位置的RVA
PointerToRawData: 区段在文件中的起始偏移
VirtualSize: 区段在内存中的大小(没有对齐)
SizeOfRawData: 区段在文件中的大小(对齐过的)
是否VirtualSize 一定会小于SizeOfRawData?
不是的,有可能是这个区段在文件中没有数据,运行起来之后,才有数据。
测试代码
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
| #include <Windows.h> void AnalyzeNTHeader(char* lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_FILE_HEADER pFileHeader = &pNt->FileHeader; PIMAGE_OPTIONAL_HEADER pOption = &pNt->OptionalHeader; printf("运行平台:%x\n",pFileHeader->Machine); printf("区段数量:%x\n", pFileHeader->NumberOfSections); printf("扩展头大小:%x\n", pFileHeader->SizeOfOptionalHeader); printf("时间戳:%x\n", pFileHeader->TimeDateStamp); printf("属性:%x\n", pFileHeader->Characteristics); } void AnalyzeSectionTable(char* lpImage) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_SECTION_HEADER pHeader = IMAGE_FIRST_SECTION(pNt); for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { printf("区段名:%s\t", pHeader[i].Name); printf("内存中大小:%X\t", pHeader[i].Misc.VirtualSize); printf("内存中RVA:%X\t", pHeader[i].VirtualAddress); printf("文件中大小:%X\t", pHeader[i].SizeOfRawData); printf("文件中FOA:%X\t", pHeader[i].PointerToRawData); printf("属性:%X\t", pHeader[i].Characteristics); printf("\n"); } }
|
RVA和FOA之间的转换
文件偏移(FOA或者Offset): 某一个数据距离文件开头的偏移
RVA怎么转换成FOA
RVA - RVA区段 = FOA -FOA区段
FOA = RVA - RVA区段+FOA区段
一般情况下,PE文件的头部在文件中是多大?
0x400
PE文件的头部在内存中是多大
0x1000
RVA为0x600的时候,转为FOA 转不了,在文件中没有对应的位置
虚拟地址(VA):程序在运行的时候,是将PE文件加载到进程的内存空间中。进程的这块内存空间就称之为 虚拟内存空间 32位程序虚拟内存空间是以字节为单位的,每一个字节都有一个编号从0x0000 0000到0xFFFFFFFF之间。这些编号就是虚拟地址。
相对虚拟地址(RVA):PE文件不会占满整个虚拟内存空间,而是会占用一部分。那么就会有一个起始位置,这个起始位置也成为加载基址。PE文件中的数据相对于加载基址的偏移就是相对虚拟地址。
如果系统加载PE文件的时候,是将PE文件原封不动的复制到了内存中,那么某一个数据的FOA和RVA就是相等的。
但是现实情况并非如此,系统加载PE文件到内存的之后,PE文件是被扩展了的。
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
| DWORD RvaToFoa(char* lpImage,DWORD dwRva) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_SECTION_HEADER pHeader = IMAGE_FIRST_SECTION(pNt); if (dwRva< pNt->OptionalHeader.SizeOfHeaders) { return dwRva; } for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { DWORD dwSectionRva = pHeader[i].VirtualAddress; DWORD dwSectionEndRva = dwSectionRva + pHeader[i].SizeOfRawData; DWORD dwSectionFOA = pHeader[i].PointerToRawData; if (dwRva>= dwSectionRva &&dwRva<= dwSectionEndRva) { pHeader[i].VirtualAddress; DWORD dwFOA = dwRva - dwSectionRva+ dwSectionFOA; return dwFOA; } } return -1; }
|
小功能的实现
位置计算
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
|
void CPEDlg::OnBnClickedButton2() { UpdateData(TRUE); if (m_strEditTest1 != L"") { CMGpsCalcDlg* pPrcPopDg = new CMGpsCalcDlg;
pPrcPopDg->Create(IDD_DIALOG1, this); pPrcPopDg->ShowWindow(SW_SHOW); pPrcPopDg->GetPath(buf); }
}
void CMGpsCalcDlg::RvaToFoa() { UpdateData(TRUE); CString svPid = m_strEditRva.GetString(); DWORD dwRva = _tcstoul(svPid, NULL, 16); PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buf; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buf); PIMAGE_SECTION_HEADER pHeader = IMAGE_FIRST_SECTION(pNt); PIMAGE_OPTIONAL_HEADER pOption = &pNt->OptionalHeader;
if (dwRva < pNt->OptionalHeader.SizeOfHeaders) { CString cd; cd.Format(L"%x", dwRva); m_strEditFoa.SetString(cd); UpdateData(FALSE); return; } for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { DWORD dwSectionRva = pHeader[i].VirtualAddress; DWORD dwSectionEndRva = dwSectionRva + pHeader[i].SizeOfRawData; DWORD dwSectionFOA = pHeader[i].PointerToRawData; if (dwRva >= dwSectionRva && dwRva <= dwSectionEndRva) { pHeader[i].VirtualAddress; DWORD dwFOA = dwRva - dwSectionRva + dwSectionFOA; CString foa; foa.Format(L"%x",dwFOA); m_strEditFoa.SetString(foa);
DWORD dwVA = pOption->ImageBase + dwRva; CString va; va.Format(L"%x", dwVA); m_strEditVa.SetString(va);
CString name; name.Format(L"%S", pHeader[i].Name); m_strEditName.SetString(name);
UpdateData(FALSE); return; } } MessageBox(L"不存在该地址"); }
|
响应拖拽
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void CPEDlg::OnDropFiles(HDROP hDropInfo) {
wchar_t filePath[MAX_PATH] = {};
DragQueryFile(hDropInfo, 0, filePath, MAX_PATH);
m_strEditPath = filePath; UpdateData(FALSE);
CDialogEx::OnDropFiles(hDropInfo); }
|
时间换算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void CPEDlg::OnBnClickedButton3() {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buf; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buf); PIMAGE_FILE_HEADER pFileHeader = &pNt->FileHeader; struct tm* p;
time_t t =pFileHeader->TimeDateStamp; p = localtime(&t); CString Time; Time.Format(L"%d年%d月%d日%d时%d分%d秒", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec); MessageBox(Time); }
|
PE文件结构图
