今天把导入函数表弄明白了,在昨天代码的基础上增加列出导入函数部分。
主程序部分略有改动:现在电脑 CPU 快、内存大,操作内存比硬盘快多了,所以直接把整个文件拷贝到内存再分析!
/* Typedef.h 数据类型 四彩 2015-11-29 */ #ifndef _TYPEDEF_H #define _TYPEDEF_H #ifndef _STDBOOL_H typedef char BOOL; #define TRUE 1 #define FALSE 0 #endif typedef unsigned char BYTE; // 无符号 1 字节 typedef unsigned short int WORD; // 无符号 2 字节 typedef unsigned int DWORD; // 无符号 4 字节 // 得到一个 DWORD 的高位 WORD 和低位 WORD #define LWordOfDW(value) ((WORD)((DWORD)(value) & 0xFFFF)) #define HWordOfDW(value) ((WORD)((DWORD)(value) >> 16)) // 得到一个 WORD 的高位 BYTE 和低位 BYTE #define LByteOfW(value) ((BYTE)((WORD)(value) & 0xFF)) #define HByteOfW(value) ((BYTE)((WORD)(value) >> 8)) // 把两个 WORD 转化为一个 DWORD #define Words2DW(HighWord, LowWord) ((((DWORD)(HighWord)) << 16) + (LowWord)) // 把两个 BYTE 转化为一个 WORD #define Bytes2W(HighByte, LowByte) ((((WORD)(HighByte)) << 8) + (LowByte)) #endif
/* PE.h PE 文件格式 四彩 2015-12-02 */ /* PE 文件的总体结构:(内存地址有低到高) IMAGE_DOS_HEADER(DOS 头) DOS stub "PE00"(PE 标志) IMAGE_FILE_HEADER(文件头) IMAGE_OPTIONAL_HEADER(可选头) IMAGE_DATA_DIRECTORY(数据块目录) IMAGE_SECTION_HEADER(节表) .text节区 .data节区 其它节区 不能被映射的其他数据 */ /* 虚拟地址:Virtual Address(VA),保护模式下访问内存所使用的逻辑地址。 装载基址:Image Base,文件装入内存的基址。 默认情况下,EXE 文件的基址为 0x00400000,DLL 文件的基址为 0x10000000。 相对虚拟地址:Relative Virtual Address(RVA),在内存中相对于装载基址的偏移量。 文件偏移地址:File Offset Address(FOA),文件在磁盘上存放时相对于文件开头的偏移量。 文件偏移地址从文件的第一个字节开始计数,起始值为 0。 */ /* IMAGE_DATA_DIRECTORY 直接定义在 IMAGE_NT_HEADER 里面,而不在 IMAGE_OPTIONAL_HEADER 里, 这导致 IMAGE_FILE_HEADER 中的 SizeOfOptionalHeader 字段数值不准。 */ #ifndef _PE_H #define _PE_H #include "Typedef.h" // ======================================================================================= // DOS 头结构:偏移地址 = 0 // #define IMAGE_DOS_SIGNATURE 0x5A4D // e_magic 预定义值,即字符 "MZ" typedef struct tag_IMAE_DOS_HEADER { // 偏移 说明 WORD e_magic; // 0x00 DOS 可执行文件标识符(= "MZ") WORD e_cblp; // 0x02 WORD e_cp; // 0x04 WORD e_crlc; // 0x06 WORD e_cparhdr; // 0x08 WORD e_minalloc; // 0x0A WORD e_maxalloc; // 0x0C WORD e_ss; // 0x0E WORD e_sp; // 0x10 WORD e_csum; // 0x12 WORD e_ip; // 0x14 WORD e_cs; // 0x16 WORD e_lfarlc; // 0x18 WORD e_ovno; // 0x1A WORD e_res[4]; // 0x1C WORD e_oemid; // 0x24 WORD e_oeminfo; // 0x26 WORD e_res2[10]; // 0x28 DWORD e_lfanew; // 0x3C PE 签名的文件偏移地址 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; // *************************************************************************************** // ======================================================================================= // NT 头结构:偏移地址 = IMAGE_DOS_HEADER.e_lfanew // // --------------------------------------------------------------------------------------- // NT 头结构 —— File 头结构 // Machine 字段预定义值: Value Meaning #define IMAGE_FILE_MACHINE_UNKNOWN 0 #define IMAGE_FILE_MACHINE_I386 0x014c // x86 #define IMAGE_FILE_MACHINE_AMD64 0x8664 // x64 #define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel Itanium /* Characteristics 是一个标志的集合 位 预定义值 含义 0 IMAGE_FILE_RELOCS_STRIPPED 文件中没有重定向信息。在可执行文件中没有使用。 可执行文件用基址重定向目录表来表示重定向信息。 1 IMAGE_FILE_EXECUTABLE_IMAGE 可执行文件。 2 IMAGE_FILE_LINE_NUMS_STRIPPED 没有行数信息。在可执行文件中没有使用。 3 IMAGE_FILE_LOCAL_SYMS_STRIPPED 没有局部符号信息。在可执行文件中没有使用。 4 IMAGE_FILE_AGGRESIVE_WS_TRIM 已无效。 5 IMAGE_FILE_LARGE_ADDRESS_AWARE 应用程序可以处理超过 2 GB 的地址。 6 未使用 7 IMAGE_FILE_BYTES_REVERSED_LO 已无效。 8 IMAGE_FILE_32BIT_MACHINE 希望机器为32位机。这个值永远为 1。 9 IMAGE_FILE_DEBUG_STRIPPED 没有调试信息。在可执行文件中没有使用。 10 IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 该程序不能运行于可移动介质中(如软驱或CD)。 11 IMAGE_FILE_NET_RUN_FROM_SWAP 程序不能在网上运行。(必须拷贝到内存中执行) 12 IMAGE_FILE_SYSTEM 系统文件,如驱动程序。在可执行文件中没有使用。 13 IMAGE_FILE_DLL 动态链接库(DLL)。 14 IMAGE_FILE_UP_SYSTEM_ONLY 不能运行于多处理器系统中。 15 IMAGE_FILE_BYTES_REVERSED_HI 已无效。 */ // Characteristics 字段预定义值 #define IMAGE_FILE_RELOCS_STRIPPED 0x0001 #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 #define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 #define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 #define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 #define IMAGE_FILE_32BIT_MACHINE 0x0100 #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 #define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 #define IMAGE_FILE_SYSTEM 0x1000 #define IMAGE_FILE_DLL 0x2000 #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 typedef struct tag_IMAGE_FILE_HEADER { WORD Machine; // 0x04 运行所要求的 CPU 类型。 WORD NumberOfSections; // 0x06 节数。 DWORD TimeDateStamp; // 0x08 文件创建日期和时间 DWORD PointerToSymbolTable; // 0x0C 指向符号表(用于调试) DWORD NumberOfSymbols; // 0x10 符号表中符号个数(用于调试) WORD SizeOfOptionalHeader; // 0x14 IMAGE_OPTIONAL_HEADER 结构大小 WORD Characteristics; // 0x16 属性 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; // --------------------------------------------------------------------------------------- // NT 头结构 —— NT 可选头结构 typedef struct tag_IMAGE_OPTIONAL_HEADER { // 偏移 说明 // Standard fields. WORD Magic; // 0x18 标志字(Win32 下总是 0x010B) BYTE MajorLinkerVersion; // 0x1A 链接程序的主版本号 BYTE MinorLinkerVersion; // 0x1B 链接程序的次版本号 DWORD SizeOfCode; // 0x1C 所有含代码的节的总大小 DWORD SizeOfInitializedData; // 0x20 所有含已初始化数据的节的总大小 DWORD SizeOfUninitializedData; // 0x24 所有含未初始化数据的节的大小 DWORD AddressOfEntryPoint; // 0x28 程序执行入口地址(RVA) DWORD BaseOfCode; // 0x2C 代码的区块的起始地址(RVA) DWORD BaseOfData; // 0x30 数据的区块的起始地址(RVA) // NT additional fields. DWORD ImageBase; // 0x34 首选装载基址 DWORD SectionAlignment; // 0x38 内存中节的对齐单位(32 位下为 4K) DWORD FileAlignment; // 0x3C 磁盘文件中节的对齐单位(一个扇区大小) WORD MajorOperatingSystemVersion; // 0x40 要求操作系统最低版本号的主版本号 WORD MinorOperatingSystemVersion; // 0x42 要求操作系统最低版本号的副版本号 WORD MajorImageVersion; // 0x44 可运行于操作系统的主版本号 WORD MinorImageVersion; // 0x46 可运行于操作系统的次版本号 WORD MajorSubsystemVersion; // 0x48 要求最低子系统版本的主版本号 WORD MinorSubsystemVersion; // 0x4A 要求最低子系统版本的次版本号 DWORD Win32VersionValue; // 0x4C 不明。一般为 0 。 DWORD SizeOfImage; // 0x50 装入内存后的总大小 DWORD SizeOfHeaders; // 0x54 所有头 + 节表的总大小 DWORD CheckSum; // 0x58 校检和 WORD Subsystem; // 0x5C 可执行文件期望的界面子系统 WORD DllCharacteristics; // 0x5E DllMain() 函数何时被调用,默认为 0 DWORD SizeOfStackReserve; // 0x60 初始化时的栈大小 DWORD SizeOfStackCommit; // 0x64 初始化时实际提交的栈大小 DWORD SizeOfHeapReserve; // 0x68 初始化时保留的堆大小 DWORD SizeOfHeapCommit; // 0x6C 初始化时实际提交的堆大小 DWORD LoaderFlags; // 0x70 与调试有关,默认为 0 DWORD NumberOfRvaAndSizes; // 0x74 数据目录表的项数 } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER; // --------------------------------------------------------------------------------------- // NT 头结构 —— 数据目录结构 typedef struct tag_IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; // 数据的相对虚拟地址(RVA) DWORD Size; // 数据的长度 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; // --------------------------------------------------------------------------------------- // NT 头结构 #define IMAGE_NT_SIGNATURE 0x00004550 // Signature 字段预定义值,即字符"PE00" #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 // 数据目录表的项数一直是 16 /* 数据目录表各项的含义 索引号 预定义值 对应的数据块 0 IMAGE_DIRECTORY_ENTRY_EXPORT 导出函数表,主要用于 DLL 中的导出函数 1 IMAGE_DIRECTORY_ENTRY_IMPORT 导入函数表,使用外部函数的数据表 2 IMAGE_DIRECTORY_ENTRY_RESOURCE 资源数据表 3 IMAGE_DIRECTORY_ENTRY_EXCEPTION 异常处理表(具体资料不详) 4 IMAGE_DIRECTORY_ENTRY_SECURITY 安全处理数据表(具体资料不详) 5 IMAGE_DIRECTORY_ENTRY_BASERELOC 重定位信息表,一般和 DLL 相关 6 IMAGE_DIRECTORY_ENTRY_DEBUG 调试信息表 7 IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 版权信息表 8 IMAGE_DIRECTORY_ENTRY_GLOBALPTR 机器值(MIPS GP)(具体资料不详) 9 IMAGE_DIRECTORY_ENTRY_TLS 线程信息本地存储表 10 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 装配信息表(具体资料不详) 11 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 输入函数绑定信息表(具体资料不详) 12 IMAGE_DIRECTORY_ENTRY_IAT 导入函数地址表(与 ImportTable 对应, 由 Loader 填写的输入函数地址) 13 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 延迟装入的函数信息表(具体资料不详) 14 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 公用组件信息表(具体资料不详) 15 未使用 */ #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 #define IMAGE_DIRECTORY_ENTRY_TLS 9 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 #define IMAGE_DIRECTORY_ENTRY_IAT 12 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 typedef struct tag_IMAGE_NT_HEADERS { // 偏移 说明 DWORD Signature; // 0x00 PE 文件签名(= "PE00") IMAGE_FILE_HEADER FileHeader; // 0x04 文件头结构 IMAGE_OPTIONAL_HEADER OptionalHeader; // 0x18 可选 NT 头结构 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 0x78 数据目录表 } IMAGE_NT_HEADER, *PIMAGE_NT_HEADER; // *************************************************************************************** // ======================================================================================= // 节表:偏移地址 = IMAGE_DOS_HEADER.e_lfanew + sizeof(IMAGE_NT_HEADER) // 项数由 IMAGE_NT_HEADER 中的 FileHeader.NumberOfSections 指定 /* 一个节中的数据只是属性相同,并不一定是同一种用途的内容,因此仅依靠节表是无法确定和定位 的,而要由数据目录表来定位。 节表是节的目录,数据目录表是存储在节里的逻辑元素的目录。 无论是在内存中还是在磁盘文件中,节都是按基址排列的,而且都要对齐,但对齐值一般不同。 映射到内存后,所有头和节表的偏移位置与大小均没有变化,而各节基址的偏移位置发生了变化。 不管节是在文件中还是被加载到内存中,节中数据的位置相对该节的基址是不变的。 在内存中:数据相对节的起始位置的偏移h = 数据的RVA - 节的RVA 在文件中:数据相对节的起始位置的偏移h = 数据的FOA – 节的FOA */ // // 节(区块)头结构 // #define IMAGE_SIZEOF_SHORT_NAME 8 // Name 字段最长 8 字节 // Characteristics 字段预定义值 #define IMAGE_SCN_CNT_CODE 0x20 // 包含代码 #define IMAGE_SCN_CNT_INITIALIZED_DATA 0x40 // 包含已初始化数据 #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x80 // 包含未初始化数据 #define IMAGE_SCN_MEM_DISCARDABLE 0x2000000 // 数据在进程开始后将被丢弃 #define IMAGE_SCN_MEM_NOT_CACHED 0x4000000 // 数据不经过缓存 #define IMAGE_SCN_MEM_NOT_PAGED 0x8000000 // 数据不被交换出内存 #define IMAGE_SCN_MEM_SHARED 0x10000000 // 数据可共享 #define IMAGE_SCN_MEM_EXECUTE 0x20000000 // 可执行 #define IMAGE_SCN_MEM_READ 0x40000000 // 可读 #define IMAGE_SCN_MEM_WRITE 0x80000000 // 可写 typedef struct tag_IMAGE_SECTION_HEADER { // 偏移 说明 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 0x00 节表名称(仅供编程参考) union // 0x08 一般是取后一个,即节的真实长度 { DWORD PhysicalAddress; // 物理地址(在目标文件中使用,重定位到的地址) DWORD VirtualSize; // 真实长度(在可执行文件中使用,未做对齐处理的实际大小) } Misc; DWORD VirtualAddress; // 0x0C 装载到内存的基址(内存对齐后的 RVA) DWORD SizeOfRawData; // 0x10 在文件中的大小(在磁盘中对齐后的大小) DWORD PointerToRawData; // 0x14 在文件中的偏移量(从文件头开始算起) DWORD PointerToRelocations; // 0x18 重定位的偏移(在 OBJ 文件中供调试用) DWORD PointerToLinenumbers; // 0x1C 行号表的偏移(同上) WORD NumberOfRelocations; // 0x20 重定位项的数目(同上) WORD NumberOfLinenumbers; // 0x22 行号表中行号的数目(同上) DWORD Characteristics; // 0x24 节的属性(如可读、可写、可执行等) } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; // *************************************************************************************** // ======================================================================================= // --------------------------------------------------------------------------------------- // 导入函数表:偏移地址 = IMAGE_NT_HEADER.IMAGE_DATA_DIRECTORY[1].VirtualAddress // 以一个全 0 成员的数组项结尾。 /* 1、导入函数是被某模块调用,但又不在调用者模块中(实际位于别的 DLL 文件里)的函数。 调用者模块里只保留一些函数信息,包括函数名及其驻留的 DLL 文件名。 2、导入函数表就是一个 IMAGE_IMPORT_DESCRIPTOR 数组。 每个数组项(一个 IMAGE_IMPORT_DESCRIPTOR 结构)对应一个导入的 DLL。 3、每个 IMAGE_IMPORT_DESCRIPTOR 结构指向两个不同的 IMAGE_THUNK_DATA 结构数组。 加载前 OriginalFirstThunk 与 FirstThunk 指向的数组内容是相同的,都包含导入信息; 加载后 FirstThunk 指向的数组指向实际的函数地址。 */ // // 导入函数表数组项结构 // typedef struct tag_IMAGE_IMPORT_BY_NAME { WORD Hint; // 本函数在其所驻留 DLL 的导出表中的索引号(非必须) char Name[1]; // 函数名(可变尺寸域) } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME; typedef struct tag_IMAGE_THUNK_DATA { union { DWORD ForwarderString; DWORD Function; // 函数地址 DWORD Ordinal; // 函数序号 DWORD AddressOfData; // 指向一个 IMAGE_IMPORT_BY_NAME 结构的指针 } u1; } IMAGE_THUNK_DATA, *PIMAGE_THUNK_DATA; /* 通过二进制的最高位判断是否按序号导入:1,按序号导入(Ordinal 的低16位就是导入序号); 0,按名字导入(AddressOfData 指向名字信息)。 */ #define IMAGE_SNAP(Ordinal) ((Ordinal) >> 31) // 判断是否按序号导入 #define IMAGE_ORDINAL(Ordinal) ((Ordinal) & 0xFFFF) // 获取导入序号 typedef struct tag_IMAGE_IMPORT_DESCRIPTOR { union // 0x00 { DWORD Characteristics; DWORD OriginalFirstThunk; // 指向一个 IMAGE_THUNK_DATA 数组 } DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; // 0x0C 指向 DLL 文件名的指针 DWORD FirstThunk; // 0x10 指向另一个 IMAGE_THUNK_DATA 数组(RVA) } IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR; // *************************************************************************************** // ======================================================================================= // 输出函数表 // --------------------------------------------------------------------------------------- /* 函数功能: 相对虚拟地址转换为文件偏移地址 参数表: pBase = 将整个文件都拷贝到内存的缓冲区基址 dwRva = 相对虚拟地址 返回值: 文件偏移地址 */ DWORD Rva2Foa(BYTE *pBase, DWORD dwRva); /* 函数功能: 文件偏移地址转换为相对虚拟地址 参数表: pBase = 将整个文件都拷贝到内存的缓冲区基址 dwFoa = 文件偏移地址 返回值: 相对虚拟地址 */ DWORD Foa2Rva(BYTE *pBase, DWORD dwFoa); // --------------------------------------------------------------------------------------- /* 函数功能: 统计 PE 文件全部导入函数的个数 参数表: pBase = 将整个文件都拷贝到内存的缓冲区基址 返回值: 全部导入函数的个数 */ DWORD GetNumOfImpFun(BYTE *pBase); /* 函数功能: 取得 PE 文件中指定顺序号的导入函数的函数名或导入序号 入口参数表: pBase = 将整个文件都拷贝到内存的缓冲区基址 iSN = 顺序号(从 0 开始) 出口参数表: sDllName = 包含该函数的 dll 模块名 sFunName = 函数名 wOrd = 序号 返回值: 函数的导入方式:0 = 函数名导入,1 = 序号导入 -1 = 顺序号超过导入函数总数 */ int GetSnapOfImpFun(BYTE *pBase, DWORD iSN, char *sDllName, char *sFunName, WORD *wOrd); // *************************************************************************************** #endif
/* PE.c PE 文件格式输出函数 四彩 2015-12-01 */ #include <stdlib.h> #include <string.h> #include "PE.h" // ======================================================================================= // RVA 与 FOA 的互相转换 // --------------------------------------------------------------------------------------- /* 相对虚拟地址转换为文件偏移地址的转换思路: (1)扫描节表,将每个节在内存中的基址加上节的大小得到节的终址, 根据基址、终址判断 dwRva 是否在该节内范围。 (2)用 dwRva 减去该节的内存基址,得到 dwRva 相对于该节内存基址的偏移量 RVA'。 将该节在文件中的基址加上 RVA',得到文件偏移地址。 */ /* 函数功能: 相对虚拟地址转换为文件偏移地址 参数表: pBase = 将整个文件都拷贝到内存的缓冲区基址 dwRva = 相对虚拟地址 返回值: 文件偏移地址 */ DWORD Rva2Foa(BYTE *pBase, DWORD dwRva) { WORD i; IMAGE_NT_HEADER *pNtH; IMAGE_SECTION_HEADER *pSH; pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(dwRva < pNtH->OptionalHeader.SizeOfHeaders) // 在头部 return dwRva; // 遍历各节 pSH = (IMAGE_SECTION_HEADER *)((BYTE *)pNtH + sizeof(IMAGE_NT_HEADER)); for(i = 0; i < pNtH->FileHeader.NumberOfSections; i++) { if(dwRva >= pSH->VirtualAddress && dwRva < pSH->VirtualAddress + pSH->Misc.VirtualSize) return pSH->PointerToRawData + dwRva - pSH->VirtualAddress; pSH++; } return 0; } // --------------------------------------------------------------------------------------- /* 文件偏移地址转换为相对虚拟地址的转换思路: (1)扫描节表,将每个节在文件中的基址加上节的大小得到节的终址, 根据基址、终址判断 dwFoa 是否在该节范围内。 (2)用 dwFoa 减去该节的文件基址,得到 dwFoa 相对于该节文件基址的偏移量 FOA'。 将该节在内存中的基址加上 FOA',得到相对虚拟地址。 */ /* 函数功能: 文件偏移地址转换为相对虚拟地址 参数表: pBase = 将整个文件都拷贝到内存的缓冲区基址 dwFoa = 文件偏移地址 返回值: 相对虚拟地址 */ DWORD Foa2Rva(BYTE *pBase, DWORD dwFoa) { WORD i; IMAGE_NT_HEADER *pNtH; IMAGE_SECTION_HEADER *pSH; pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(dwFoa < pNtH->OptionalHeader.SizeOfHeaders) return dwFoa; pSH = (IMAGE_SECTION_HEADER *)((BYTE *)pNtH + sizeof(IMAGE_NT_HEADER)); for(i = 0; i < pNtH->FileHeader.NumberOfSections; i++) { if(dwFoa >= pSH->PointerToRawData && dwFoa < pSH->PointerToRawData + pSH->Misc.VirtualSize) return pSH->VirtualAddress + dwFoa - pSH->PointerToRawData; pSH++; } return 0; } // *************************************************************************************** // ======================================================================================= // 导出函数与导入函数 // // --------------------------------------------------------------------------------------- /* 获取所有导入函数的函数名或序号: 载入 PE 文件到内存,用内存缓冲区基址定位 IMAGE_NT_HEADER 地址。 用数据目录表中第二项的 VirtualAddress 值,定位第一个 IMAGE_IMPORT_DESCRIPTOR 地址。 检查 FirstThunk :若不为 0,用 OriginalFirstThunk 定位第一个 IMAGE_THUNK_DATA 数组。 检查 u1.Ordinal 的二进制最高位: 若为 1,那么函数是由序号导入的,可以从 Ordinal 的低字提取序号; 若为 0,那么函数是由函数名导入的,可以由 AddressOfData 指向的 IMAGE_IMPORT_BY_NAME 取得函数名。 */ /* 函数功能: 统计 PE 文件全部导入函数的个数 参数表: pBase = 将整个文件都拷贝到内存的缓冲区基址 返回值: 全部导入函数的个数 */ DWORD GetNumOfImpFun(BYTE *pBase) { DWORD i = 0; IMAGE_NT_HEADER *pNtH; IMAGE_IMPORT_DESCRIPTOR *pImpDsc; IMAGE_THUNK_DATA *pThk; pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0) return 0; pImpDsc = (IMAGE_IMPORT_DESCRIPTOR *)(pBase + Rva2Foa(pBase, pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); while(pImpDsc->Name) { pThk = (IMAGE_THUNK_DATA *)(pBase + Rva2Foa(pBase, pImpDsc->FirstThunk)); while(pThk->u1.Ordinal) { pThk++; i++; } pImpDsc++; } return i; } /* 函数功能: 取得 PE 文件中指定顺序号的导入函数的函数名或导入序号 入口参数表: pBase = 将整个文件都拷贝到内存的缓冲区基址 iSN = 顺序号(从 0 开始) 出口参数表: sDllName = 包含该函数的 dll 模块名 sFunName = 函数名 wOrd = 序号 返回值: 函数的导入方式:0 = 函数名导入,1 = 序号导入 -1 = 顺序号超过导入函数总数 */ int GetSnapOfImpFun(BYTE *pBase, DWORD iSN, char *sDllName, char *sFunName, WORD *wOrd) { DWORD i = 0; IMAGE_NT_HEADER *pNtH; IMAGE_IMPORT_DESCRIPTOR *pImpDsc; IMAGE_THUNK_DATA *pThk; pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0) return 0; pImpDsc = (IMAGE_IMPORT_DESCRIPTOR *)(pBase + Rva2Foa(pBase, pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); while(pImpDsc->Name) { pThk = (IMAGE_THUNK_DATA *)(pBase + Rva2Foa(pBase, pImpDsc->FirstThunk)); while(pThk->u1.Ordinal) { if(i == iSN) // 找到了 { strcpy(sDllName, (char *)(pBase + Rva2Foa(pBase, (pImpDsc->Name)))); if(IMAGE_SNAP(pThk->u1.Ordinal)) // 序号导入 { *wOrd = IMAGE_ORDINAL(pThk->u1.Ordinal); return 1; } else // 函数名导入 { strcpy(sFunName, ((IMAGE_IMPORT_BY_NAME *)(pBase + Rva2Foa(pBase, pThk->u1.AddressOfData)))->Name); return 0; } } pThk++; i++; } pImpDsc++; } return -1; } // ***************************************************************************************
/* PEM.c 显示 PE 文件格式信息 四彩 2015-11-30 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "PE.h" #define MAX_PATH 260 // 最长字符串长度 void PrintDosHeader(IMAGE_DOS_HEADER *pDosHeader); void PrintFileHeader(IMAGE_FILE_HEADER FileHeader); void PrintOptionalHeader(IMAGE_OPTIONAL_HEADER OptHeader); void PrintDataDircrory(IMAGE_DATA_DIRECTORY DataDirectory); void PrintSectionHeader(IMAGE_SECTION_HEADER *pSectionHeader); int main(int argc, char **argv) { FILE *fp; DWORD i, dwFileLen ; BYTE *pBase; IMAGE_DOS_HEADER *pDosHeader; IMAGE_NT_HEADER *pNtHeader; IMAGE_SECTION_HEADER *pFstSctHeader, *pSctHeader; DWORD dwNumOfImptFun; int snap; char sDllName[MAX_PATH], sFunName[MAX_PATH]; WORD wOrd; if(argc != 2) { printf("Usage : %s exeFileName\n", argv[0]); return -1; } // 将整个文件都拷贝到内存 fp = fopen(argv[1], "rb"); if (fp == NULL) { printf("Error : open file\n"); return -1; } fseek(fp, 0, SEEK_END); dwFileLen = ftell(fp); pBase = (BYTE *)malloc(dwFileLen); if(pBase == NULL) { printf("Error : malloc memory\n"); return -1; } fseek(fp, 0, SEEK_SET); fread(pBase, dwFileLen, 1, fp); fclose(fp); // 检查是否为 PE 文件,并给相关结构指针赋值 pDosHeader = (IMAGE_DOS_HEADER *)pBase; if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { printf("Error : NOT DOS file\n"); free(pBase); return -1; } pNtHeader = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(pNtHeader->Signature != IMAGE_NT_SIGNATURE) { printf("Error : NOT PE file\n"); free(pBase); return -1; } pFstSctHeader = (IMAGE_SECTION_HEADER *)((BYTE *)pNtHeader + sizeof(IMAGE_NT_HEADER)); // 显示 IMAGE_DOS_HEADER 信息 printf("IMAGE_DOS_HEADER :\n"); PrintDosHeader(pDosHeader); // 显示 IMAGE_NT_HEADER 信息 printf("\nIMAGE_NT_HEADER :\n"); // Signature printf("Signature : %c%c%d%d\n", // 低低高高 LByteOfW(LWordOfDW(pNtHeader->Signature)), HByteOfW(LWordOfDW(pNtHeader->Signature)), LByteOfW(HWordOfDW(pNtHeader->Signature)), HByteOfW(HWordOfDW(pNtHeader->Signature))); printf("\nIMAGE_FILE_HEADER :\n"); // IMAGE_FILE_HEADER PrintFileHeader(pNtHeader->FileHeader); printf("\nIMAGE_OPTIONAL_HEADER :\n"); // IMAGE_OPTIONAL_HEADER PrintOptionalHeader(pNtHeader->OptionalHeader); printf("\nIMAGE_DATA_DIRECTORY : "); // 遍历全部 IMAGE_DATA_DIRECTORY printf("\n NO. VirtualAddress Size"); for(i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) { printf("\n %2d", i); PrintDataDircrory(pNtHeader->DataDirectory[i]); } // 遍历显示全部 IMAGE_SECTION_HEADER 信息 pSctHeader = pFstSctHeader; printf("\n\nIMAGE_SECTION_HEADER :"); for(i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++) { printf("\nSECTION_HEADER NO : %02d\nFOA : 0x%08X\n", i, Rva2Foa(pBase, (DWORD)((BYTE *)pSctHeader - pBase))); PrintSectionHeader(pSctHeader); pSctHeader++; } // 遍历显示全部导入函数信息 dwNumOfImptFun = GetNumOfImpFun(pBase); printf("\nIMAGE_IMPORT_DESCRIPTOR : %d", dwNumOfImptFun); printf("\n SN DllName FunName"); for(i = 0; i < dwNumOfImptFun; i++) { snap = GetSnapOfImpFun(pBase, i, sDllName, sFunName, &wOrd); if(snap == 0) // 函数名导入 { printf("\n %02d % 12s %s", i, sDllName, sFunName); } else if(snap == 1) // 序号导入 { printf("\n %02d % 12s 0x%04X", i, sDllName, wOrd); } } free(pBase); return 0; } // 显示 IMAGE_DOS_HEADER 信息 void PrintDosHeader(IMAGE_DOS_HEADER *pDosHeader) { int i; printf("e_magic : %c%c\n", LByteOfW(pDosHeader->e_magic), HByteOfW(pDosHeader->e_magic)); printf("e_cblp : 0x%04X\n", pDosHeader->e_cblp); printf("e_cp : 0x%04X\n", pDosHeader->e_cp); printf("e_crlc : 0x%04X\n", pDosHeader->e_crlc); printf("e_cparhdr : 0x%04X\n", pDosHeader->e_cparhdr); printf("e_minalloc : 0x%04X\n", pDosHeader->e_minalloc); printf("e_maxalloc : 0x%04X\n", pDosHeader->e_maxalloc); printf("e_ss : 0x%04X\n", pDosHeader->e_ss); printf("e_sp : 0x%04X\n", pDosHeader->e_sp); printf("e_csum : 0x%04X\n", pDosHeader->e_csum); printf("e_ip : 0x%04X\n", pDosHeader->e_ip); printf("e_cs : 0x%04X\n", pDosHeader->e_cs); printf("e_lfarlc : 0x%04X\n", pDosHeader->e_lfarlc); printf("e_ovno : 0x%04X\n", pDosHeader->e_ovno); printf("e_res : "); for(i = 0; i < 4; i++) printf("0x%X, ", pDosHeader->e_res[i]); printf("\ne_oemid : 0x%04X\n", pDosHeader->e_oemid); printf("e_oeminfo : 0x%04X\n", pDosHeader->e_oeminfo); printf("e_res2 : "); for(i = 0; i < 10; i++) printf("0x%X, ", pDosHeader->e_res2[i]); printf("\ne_lfanew : 0x%04X\n", pDosHeader->e_lfanew); } // 显示 IMAGE_FILE_HEADER 信息 void PrintFileHeader(IMAGE_FILE_HEADER FileHeader) { printf("Machine : 0x%04X\n", FileHeader.Machine); printf("NumberOfSections : 0x%04X\n", FileHeader.NumberOfSections); printf("TimeDateStamp : 0x%08X\n", FileHeader.TimeDateStamp); printf("PointerToSymbolTable : 0x%08X\n", FileHeader.PointerToSymbolTable); printf("NumberOfSymbols : 0x%08X\n", FileHeader.NumberOfSymbols); printf("SizeOfOptionalHeader : 0x%04X\n", FileHeader.SizeOfOptionalHeader); printf("Characteristics : 0x%04X\n", FileHeader.Characteristics); } // 显示 IMAGE_OPTIONAL_HEADER 信息 void PrintOptionalHeader(IMAGE_OPTIONAL_HEADER OptHeader) { // Standard fields. printf("Magic : 0x%04X\n", OptHeader.Magic); printf("MajorLinkerVersion : 0x%02X\n", OptHeader.MajorLinkerVersion); printf("MinorLinkerVersion : 0x%02X\n", OptHeader.MinorLinkerVersion); printf("SizeOfCode : 0x%08X\n", OptHeader.SizeOfCode); printf("SizeOfInitializedData : 0x%08X\n", OptHeader.SizeOfInitializedData); printf("SizeOfUninitializedData : 0x%08X\n", OptHeader.SizeOfUninitializedData); printf("AddressOfEntryPoint : 0x%08X\n", OptHeader.AddressOfEntryPoint); printf("BaseOfCode : 0x%08X\n", OptHeader.BaseOfCode); printf("BaseOfData : 0x%08X\n", OptHeader.BaseOfData); // NT additional fields. printf("ImageBase : 0x%08X\n", OptHeader.ImageBase); printf("SectionAlignment : 0x%08X\n", OptHeader.SectionAlignment); printf("FileAlignmen : 0x%08X\n", OptHeader.FileAlignment); printf("MajorOperatingSystemVersion : 0x%04X\n", OptHeader.MajorOperatingSystemVersion); printf("MinorOperatingSystemVersion : 0x%04X\n", OptHeader.MinorOperatingSystemVersion); printf("MajorImageVersion : 0x%04X\n", OptHeader.MajorImageVersion); printf("MinorImageVersion : 0x%04X\n", OptHeader.MinorImageVersion); printf("MajorSubsystemVersion : 0x%04X\n", OptHeader.MajorSubsystemVersion); printf("MinorSubsystemVersion : 0x%04X\n", OptHeader.MinorSubsystemVersion); printf("Win32VersionValue : 0x%08X\n", OptHeader.Win32VersionValue); printf("SizeOfImage : 0x%08X\n", OptHeader.SizeOfImage); printf("SizeOfHeaders : 0x%08X\n", OptHeader.SizeOfHeaders); printf("CheckSum : 0x%08X\n", OptHeader.CheckSum); printf("Subsystem : 0x%04X\n", OptHeader.Subsystem); printf("DllCharacteristics : 0x%04X\n", OptHeader.DllCharacteristics); printf("SizeOfStackReserve : 0x%08X\n", OptHeader.SizeOfStackReserve); printf("SizeOfStackCommit : 0x%08X\n", OptHeader.SizeOfStackCommit); printf("SizeOfHeapReserve : 0x%08X\n", OptHeader.SizeOfHeapCommit); printf("SizeOfHeapCommit : 0x%08X\n", OptHeader.SizeOfHeapCommit); printf("LoaderFlags : 0x%08X\n", OptHeader.LoaderFlags); printf("NumberOfRvaAndSizes : 0x%08X\n", OptHeader.NumberOfRvaAndSizes); } // 显示 IMAGE_DATA_DIRECTORY 信息 void PrintDataDircrory(IMAGE_DATA_DIRECTORY DataDirectory) { printf(" 0x%08X 0x%08X", DataDirectory.VirtualAddress, DataDirectory.Size); } // 显示 IMAGE_SECTION_HEADER 信息 void PrintSectionHeader(IMAGE_SECTION_HEADER *pSectionHeader) { if(pSectionHeader->Name) printf("Name : %s\n", pSectionHeader->Name); else printf("Name : NULL\n"); printf("Misc.VirtualSize : 0x%08X\n", pSectionHeader->Misc.VirtualSize); printf("VirtualAddress : 0x%08X\n", pSectionHeader->VirtualAddress); printf("SizeOfRawData : 0x%08X\n", pSectionHeader->SizeOfRawData); printf("PointerToRawData : 0x%08X\n", pSectionHeader->PointerToRawData); printf("PointerToRelocations : 0x%08X\n", pSectionHeader->PointerToRelocations); printf("PointerToLinenumbers : 0x%08X\n", pSectionHeader->PointerToLinenumbers); printf("NumberOfRelocations : 0x%04X\n", pSectionHeader->NumberOfRelocations); printf("NumberOfLinenumbers : 0x%04X\n", pSectionHeader->NumberOfLinenumbers); printf("Characteristics : 0x%08X\n", pSectionHeader->Characteristics); }
照例贴出本程序的分析结果(受字数限制,只贴了今天新增的):
IMAGE_IMPORT_DESCRIPTOR : 59 SN DllName FunName 00 KERNEL32.dll DeleteCriticalSection 01 KERNEL32.dll EnterCriticalSection 02 KERNEL32.dll FreeLibrary 03 KERNEL32.dll GetCurrentProcess 04 KERNEL32.dll GetCurrentProcessId 05 KERNEL32.dll GetCurrentThreadId 06 KERNEL32.dll GetLastError 07 KERNEL32.dll GetModuleHandleA 08 KERNEL32.dll GetProcAddress 09 KERNEL32.dll GetStartupInfoA 10 KERNEL32.dll GetSystemTimeAsFileTime 11 KERNEL32.dll GetTickCount 12 KERNEL32.dll InitializeCriticalSection 13 KERNEL32.dll LeaveCriticalSection 14 KERNEL32.dll LoadLibraryA 15 KERNEL32.dll QueryPerformanceCounter 16 KERNEL32.dll SetUnhandledExceptionFilter 17 KERNEL32.dll Sleep 18 KERNEL32.dll TerminateProcess 19 KERNEL32.dll TlsGetValue 20 KERNEL32.dll UnhandledExceptionFilter 21 KERNEL32.dll VirtualProtect 22 KERNEL32.dll VirtualQuery 23 msvcrt.dll __dllonexit 24 msvcrt.dll __getmainargs 25 msvcrt.dll __initenv 26 msvcrt.dll __lconv_init 27 msvcrt.dll __set_app_type 28 msvcrt.dll __setusermatherr 29 msvcrt.dll _acmdln 30 msvcrt.dll _amsg_exit 31 msvcrt.dll _cexit 32 msvcrt.dll _fmode 33 msvcrt.dll _initterm 34 msvcrt.dll _iob 35 msvcrt.dll _lock 36 msvcrt.dll _onexit 37 msvcrt.dll _unlock 38 msvcrt.dll abort 39 msvcrt.dll calloc 40 msvcrt.dll exit 41 msvcrt.dll fclose 42 msvcrt.dll fopen 43 msvcrt.dll fprintf 44 msvcrt.dll fread 45 msvcrt.dll free 46 msvcrt.dll fseek 47 msvcrt.dll ftell 48 msvcrt.dll fwrite 49 msvcrt.dll malloc 50 msvcrt.dll memset 51 msvcrt.dll memcpy 52 msvcrt.dll printf 53 msvcrt.dll puts 54 msvcrt.dll signal 55 msvcrt.dll strcpy 56 msvcrt.dll strlen 57 msvcrt.dll strncmp 58 msvcrt.dll vfprint