快速查询PE文件知识点和PE文件解析(上)
本帖最后由 fz12 于 2022-5-5 10:48 编辑PE(Portable Executable)
PE文件的全称是Portable Executable ,意为可移植的可执行文件,常见的有EXE,DLL,SYS,COM,OCX,PE文件是微软Windows操作系统上的程序文件
PE节
节名说明
.text.text 节是供机器指令使用的默认节。一般情况下,在最终生成的可执行文件中,链接器将把每个.OBJ文件的.text节合并成一个巨大的、统一的.text节。
.data全局和静态变量存储(在编译时初始化)
.bss全局和静态变量存储(在编译时不初始化)
.textbss启用增量链接
.rsrc.rsrc节用于储存模块资源,这些资源是可以嵌入到可执行文件中的二进制对象。例如,定制的鼠标光标、字体、程序图标、字符串表及版本信息都是标准的资源。资源还可以是应用程序需要的任意数据块(例如另一个可执行文件)。
.idata存储有关导入库例程信息
.edata存储有关导出库例程信息
.reloc重定位
.rdata数据段(只读)
.crtc++ 运行时库 runtime
.tls线程局部存储
基础知识
[*]基地址(ImageBase):当PE文件通过Windows加载器载入内存后,内存中的版本称为模块,映射文件的起始地址称为模块句柄,可通过模块句柄访问内存中其他数据结构,这个内存起始地址就称为基地址。
[*]虚拟地址(VA,Virtual Address):在Windows系统中,PE文件被系统加载到内存后,每个程序都有自己的虚拟空间,这个虚拟空间的内存地址称为虚拟地址。
[*]相对虚拟地址(RVA,Relative Virtual Address):可执行文件中,有许多地方需要指定内存中的地址。例如,应用全局变量时需要指定它的地址。为了避免在PE文件中出现绝对内存地址引入了相对虚拟地址,它就是在内存中相对于PE文件载入地址的偏移量。
它们之间的关系:虚拟地址(VA)= 基地址(Image Base)+相对虚拟地址(RVA)
[*]文件偏移地址(Offset):当PE文件存储在磁盘中时,某个数据的位置相对于文件头的偏移量称为文件偏移地址(File Offset)。文件偏移地址从PE文件的第一个字节开始计数,起始值为0
PE 头解析
DOS头
DOS头和DOS存根,它们的存在主要是用来兼容DOS系统。当我们的程序运行在DOS系统的时候,就会运行DOS存根中的代码,代码内容就是输出一段字符串告诉用户,这个程序不能在16位系统运行。
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // DOS头签名,4D5A
WORD e_cblp; // 最后(部分)页中的字节数
WORD e_cp; // 文件中的全部和部分页数
WORD e_crlc; // 重定位表中的指针数
WORD e_cparhdr; // 头部尺寸,以段落为单位
WORD e_minalloc; // 所需的最小附加段
WORD e_maxalloc; // 所需的最大附加段
WORD e_ss; // 初始化的SS值(相对偏移量)
WORD e_sp; // 初始化的SP值
WORD e_csum; // 补码检验值
WORD e_ip; // 初始化的IP值
WORD e_cs; // 初始化的CS值(相对偏移量)
WORD e_lfarlc; // 重定位表的字节偏移量
WORD e_ovno; // 覆盖号
WORD e_res; // 保留字
WORD e_oemid; // OEM 标识符(相对 e_oeminfo)
WORD e_oeminfo; // OEM 信息; e_oemid specific
WORD e_res2; // 保留字
LONG e_lfanew; // PE头的偏移位置
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
DOS存根
在DOS头下方,是个可选项,大小不固定,由代码与数据混合而成,一般从0x40开始
标识头
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_OS2_SIGNATURE 0x454E // NE
#define IMAGE_OS2_SIGNATURE_LE 0x454C // LE
#define IMAGE_VXD_SIGNATURE 0x454C // LE
#define IMAGE_NT_SIGNATURE 0x00004550// PE00
解析DOS头
#include<Windows.h>
#include<iostream>
using namespace std;
void ImageDosHeader(_In_z_ const char* path)
{
// 获取文件对象
HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// 获取文件大小
DWORD fSize = GetFileSize(hfile, NULL);
char* pBuff = new char;
DWORD dwReadSize = 0;
// 读文件
BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
if (bSuccess)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
// DOS头签名
cout << hex << pDosHeader->e_magic << endl;
// PE头的偏移位置
cout << hex << pDosHeader->e_lfanew << endl;
}
else (cout.write("打开文件失败", 20));
CloseHandle(hfile);
delete[] pBuff;
}
void main()
{
ImageDosHeader(R"(C:\Users\11981\Desktop\Project1\1.exe)");
}
NT头
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature; // 标识,0x00004550
IMAGE_FILE_HEADER FileHeader; // 文件头
IMAGE_OPTIONAL_HEADER64 OptionalHeader; // 可选头
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
NT头:文件头
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; // 运行平台
WORD NumberOfSections; // 文件存在的区段数量
DWORD TimeDateStamp; // PE文件的创建时间,一般有连接器填写
DWORD PointerToSymbolTable; // COFF文件符号表在文件中的偏移
DWORD NumberOfSymbols; // 符号表的数量
WORD SizeOfOptionalHeader; // 可选PE头的大小
WORD Characteristics; // 文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
Machine:运行平台
#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_TARGET_HOST 0x0001// Useful for indicating we want to interact with the host and not a WoW guest.
#define IMAGE_FILE_MACHINE_I386 0x014c// Intel 386.
#define IMAGE_FILE_MACHINE_R3000 0x0162// MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x0166// MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000 0x0168// MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169// MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA 0x0184// Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3 0x01a2// SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define IMAGE_FILE_MACHINE_SH3E 0x01a4// SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6// SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5 0x01a8// SH5
#define IMAGE_FILE_MACHINE_ARM 0x01c0// ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2// ARM Thumb/Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4// ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0// IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200// Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266// MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284// ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366// MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466// MIPS
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE 0x0520// Infineon
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC// EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64 0x8664// AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041// M32R little-endian
#define IMAGE_FILE_MACHINE_ARM64 0xAA64// ARM64 Little-Endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
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// 应用程序可处理大于2GB的地址
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080// Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100// 只在32为平台上运行
#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// DLL文件
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000// 文件不能在多处理器计算机上运行
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000// Bytes of machine word are reversed.
NE头:可选头
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; // PE类型
BYTE MajorLinkerVersion; // 链接器的版本号
BYTE MinorLinkerVersion; // 链接器的版本号
DWORD SizeOfCode; // 代码段的长度,如果有多个代码段,则是代码段长度的总和
DWORD SizeOfInitializedData; // 初始化的数据长度
DWORD SizeOfUninitializedData; // 未初始化的数据长度
DWORD AddressOfEntryPoint; // 程序入口的RVA,对于exe这个地址可以理解为WinMain的RVA。对于DLL,这个地址可以理解为DllMain的RVA,如果是驱动程序,可以理解为DriverEntry的RVA
DWORD BaseOfCode; // 代码段起始地址的RVA
DWORD BaseOfData; // 数据段起始地址的RVA
DWORD ImageBase; // 映象(加载到内存中的PE文件)的基地址,这个基地址是建议,对于DLL来说,如果无法加载到这个地址,系统会自动为其选择地址。
DWORD SectionAlignment; // 节对齐,PE中的节被加载到内存时会按照这个域指定的值来对齐,比如这个值是0x1000,那么每个节的起始地址的低12位都为0
DWORD FileAlignment; // 节在文件中按此值对齐,SectionAlignment必须大于或等于FileAlignment
WORD MajorOperatingSystemVersion; // 所需操作系统的版本号
WORD MinorOperatingSystemVersion; // 所需操作系统的版本号
WORD MajorImageVersion; // 映象的版本号,这个是开发者自己指定的,由连接器填写
WORD MinorImageVersion; // 映象的版本号,这个是开发者自己指定的,由连接器填写
WORD MajorSubsystemVersion; // 所需子系统版本号
WORD MinorSubsystemVersion; // 所需子系统版本号
DWORD Win32VersionValue; // 保留,必须为0
DWORD SizeOfImage; // 映象的大小,PE文件加载到内存中空间是连续的,这个值指定占用虚拟空间的大小
DWORD SizeOfHeaders; // 所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的
DWORD CheckSum; // 映象文件的校验和
WORD Subsystem; // 运行该PE文件所需的子系统
WORD DllCharacteristics; // 映像文件的DLL特性
DWORD SizeOfStackReserve; // 运行时为每个线程栈保留内存的大小
DWORD SizeOfStackCommit; // 运行时每个线程栈初始占用内存大小
DWORD SizeOfHeapReserve; // 运行时为进程堆保留内存大小
DWORD SizeOfHeapCommit; // 运行时进程堆初始占用内存大小
DWORD LoaderFlags; // 保留,必须为0
DWORD NumberOfRvaAndSizes; // 数据目录的项数
IMAGE_DATA_DIRECTORY DataDirectory; // 数据目录的结构体
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG 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;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory;
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
Magic:PE类型
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107
Subsystem:子系统
#define IMAGE_SUBSYSTEM_UNKNOWN 0 // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10//
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER11 //
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12//
#define IMAGE_SUBSYSTEM_EFI_ROM 13
#define IMAGE_SUBSYSTEM_XBOX 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
#define IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG 17
DllCharacteristics:DLL特性
// IMAGE_LIBRARY_PROCESS_INIT 0x0001 // Reserved.
// IMAGE_LIBRARY_PROCESS_TERM 0x0002 // Reserved.
// IMAGE_LIBRARY_THREAD_INIT 0x0004 // Reserved.
// IMAGE_LIBRARY_THREAD_TERM 0x0008 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020// Image can handle a high entropy 64-bit virtual address space.
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 // DLL can move.
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080 // Code Integrity Image
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 // Image is NX compatible
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 // Image understands isolation and doesn't want it
#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 // Image does not use SEH.No SE handler may reside in this image
#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 // Do not bind this image.
#define IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000 // Image should execute in an AppContainer
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 // Driver uses WDM model
#define IMAGE_DLLCHARACTERISTICS_GUARD_CF 0x4000 // Image supports Control Flow Guard.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
数据目录项
该字段定义了PE 文件中出现的所有不同类型的数据的目录信息
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // 就是数据目录表的RVA
DWORD Size; // 数据块的长度,起始地址+尺寸 = 结束的位置
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
IMAGE_DATA_DIRECTORY
#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 // 调试信息地址和大小
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // 预留为0
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS地址和大小
#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 // COM Runtime descriptor
区段头
定义了每个区段的属性
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name; // 节表名称,如“.text”
union {
DWORD PhysicalAddress; // 物理地址
DWORD VirtualSize; // 内存中区段所占大小
} Misc;
DWORD VirtualAddress; // 内存中区段起始位置(RVA)
DWORD SizeOfRawData; // 硬盘文件中区段所占大小
DWORD PointerToRawData; // 硬盘文件中区段起始位置
DWORD PointerToRelocations; // 在OBJ文件中使用,重定位的偏移
DWORD PointerToLinenumbers; // 行号表的偏移(供调试使用地)
WORD NumberOfRelocations; // 在OBJ文件中使用,重定位项数目
WORD NumberOfLinenumbers; // 行号表中行号的数目
DWORD Characteristics; // 区段属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Characteristics:区段属性
// IMAGE_SCN_TYPE_REG 0x00000000// Reserved.
// IMAGE_SCN_TYPE_DSECT 0x00000001// Reserved.
// IMAGE_SCN_TYPE_NOLOAD 0x00000002// Reserved.
// IMAGE_SCN_TYPE_GROUP 0x00000004// Reserved.
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008// Reserved.
// IMAGE_SCN_TYPE_COPY 0x00000010// Reserved.
#define IMAGE_SCN_CNT_CODE 0x00000020// Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040// Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080// Section contains uninitialized data.
#define IMAGE_SCN_LNK_OTHER 0x00000100// Reserved.
#define IMAGE_SCN_LNK_INFO 0x00000200// Section contains comments or some other type of information.
// IMAGE_SCN_TYPE_OVER 0x00000400// Reserved.
#define IMAGE_SCN_LNK_REMOVE 0x00000800// Section contents will not become part of image.
#define IMAGE_SCN_LNK_COMDAT 0x00001000// Section contents comdat.
// 0x00002000// Reserved.
// IMAGE_SCN_MEM_PROTECTED - Obsolete 0x00004000
#define IMAGE_SCN_NO_DEFER_SPEC_EXC 0x00004000// Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL 0x00008000// Section content can be accessed relative to GP
#define IMAGE_SCN_MEM_FARDATA 0x00008000
// IMAGE_SCN_MEM_SYSHEAP- Obsolete 0x00010000
#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
#define IMAGE_SCN_MEM_16BIT 0x00020000
#define IMAGE_SCN_MEM_LOCKED 0x00040000
#define IMAGE_SCN_MEM_PRELOAD 0x00080000
#define IMAGE_SCN_ALIGN_1BYTES 0x00100000//
#define IMAGE_SCN_ALIGN_2BYTES 0x00200000//
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000//
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000//
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000// Default alignment if no others are specified.
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000//
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000//
#define IMAGE_SCN_ALIGN_128BYTES 0x00800000//
#define IMAGE_SCN_ALIGN_256BYTES 0x00900000//
#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000//
#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000//
#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000//
#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000//
#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000//
// Unused 0x00F00000
#define IMAGE_SCN_ALIGN_MASK 0x00F00000
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000// Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000// Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000// Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000// Section is not pageable.
#define IMAGE_SCN_MEM_SHARED 0x10000000// Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE 0x20000000// Section is executable.
#define IMAGE_SCN_MEM_READ 0x40000000// Section is readable.
#define IMAGE_SCN_MEM_WRITE 0x80000000// Section is writeable.
NT头解析
#include<Windows.h>
#include<iostream>
using namespace std;
void ImageNtHeader(_In_z_ const char* path)
{
// 获取文件对象
HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// 获取文件大小
DWORD fSize = GetFileSize(hfile, NULL);
char* pBuff = new char;
DWORD dwReadSize = 0;
// 读文件
BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
if (bSuccess)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
PIMAGE_NT_HEADERS64 pNtHeader{ 0 };
pNtHeader = (PIMAGE_NT_HEADERS64)(pDosHeader->e_lfanew + pBuff);
// 标识
cout << hex << pNtHeader->Signature << endl;
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
// 运行平台
cout << pFileHeader->Machine << endl;
PIMAGE_OPTIONAL_HEADER64 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER64)&pNtHeader->OptionalHeader;
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
for (int i = 0; i < pFileHeader->NumberOfSections; i++)
{
// 节表名称
cout << pSectionHeader->Name << endl;
// 硬盘文件中区段所占大小
cout << pSectionHeader->SizeOfRawData << endl;
pSectionHeader++;
}
}
else (cout.write("打开文件失败", 20));
CloseHandle(hfile);
delete[] pBuff;
}
void main()
{
ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\1.exe)");
}
导入表
当使用到一个dll中的函数时,会出现在导入表中(隐式调用)导入表位于数据目录表中第1项
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // 指向导入名称表(INT)的RAV
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // 转发链,如果不转发则此值为0
DWORD Name; // 指向导入映像文件的名字
DWORD FirstThunk; // 指向导入地址表(IAT)的RAV
} IMAGE_IMPORT_DESCRIPTOR;
OriginalFirstThunk和FirstThunk分别指向两个不同的IMAGE_THUNK_DATA结构的数组。这两个数组都以一个空的IMAGE_THUNK_DATA结构结尾。一般情况下,导入表只需要关注OriginalFirstThunk和FirstThunk这两个字段。
typedef struct _IMAGE_THUNK_DATA64 {
union {
ULONGLONG ForwarderString;// PBYTE
ULONGLONG Function; // PDWORD
ULONGLONG Ordinal;
ULONGLONG AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;
#include "poppack.h" // Back to 4 byte packing
//@
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString; // 指向一个转向者字符串的RVA
DWORD Function; // 被输入的函数的内存地址;
DWORD Ordinal;// 被输入的API的序数值
DWORD AddressOfData; // 指向IMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
导入表解析
#include<Windows.h>
#include<iostream>
using namespace std;
DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
for (int i = 0; i < f->NumberOfSections; i++)
{
// FOA = 数据的RVA + 区段的RVA - 区段的FOA
if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->SizeOfRawData))
{
return rva - p->VirtualAddress + p->PointerToRawData;
}
f++;
p++;
}
return 0;
}
void ImageNtHeader(_In_z_ const char* path)
{
// 获取文件对象
HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// 获取文件大小
DWORD fSize = GetFileSize(hfile, NULL);
char* pBuff = new char;
DWORD dwReadSize = 0;
// 读文件
BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
if (bSuccess)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory;
// 导入表地址
PIMAGE_IMPORT_DESCRIPTOR pImportDescrtiptor = (PIMAGE_IMPORT_DESCRIPTOR)(RvaToFoa(dataDirectory->VirtualAddress, pSectionHeader, pFileHeader) + pBuff);
while (pImportDescrtiptor->OriginalFirstThunk)
{
char* dllName = (RvaToFoa(pImportDescrtiptor->Name, pSectionHeader, pFileHeader) + pBuff);
cout << "DLL名称:" << dllName << endl;
cout << "日期时间标志:" << hex << pImportDescrtiptor->TimeDateStamp << endl;
cout << "转发链:" << hex << pImportDescrtiptor->ForwarderChain << endl;
cout << "名称OFFSET:" << hex << pImportDescrtiptor->Name << endl;
cout << "FirstThunk:" << hex << pImportDescrtiptor->FirstThunk << endl;
cout << "OriginalFirstThunk:" << hex << pImportDescrtiptor->OriginalFirstThunk << endl;
//指向地址表中的RVA
PIMAGE_THUNK_DATA pInt = (PIMAGE_THUNK_DATA)(RvaToFoa(pImportDescrtiptor->OriginalFirstThunk, pSectionHeader, pFileHeader) + pBuff);
DWORD Index = 0;
DWORD ImportOffset = 0;
//被导入函数的序号
while (pInt->u1.Ordinal)
{
// 说明序号导入,这个函数没有名字
if (pInt->u1.Ordinal & 0x80000000)
{
int a = pInt->u1.Ordinal & 0x7FFFFFFF;
cout << a << endl;
}
//最高位是0,表示这个是一个有名称的函数 RVA
else
{
PIMAGE_IMPORT_BY_NAME importName = (PIMAGE_IMPORT_BY_NAME)(RvaToFoa(pInt->u1.AddressOfData, pSectionHeader, pFileHeader) + pBuff);
cout << "API名称:" << importName->Name << endl;
cout << "API序号:" << importName->Hint << endl;
cout << "ThunkValue:" << pInt->u1.Function << endl;
}
pInt++;
}
pImportDescrtiptor++;
}
}
else (cout.write("打开文件失败", 20));
CloseHandle(hfile);
delete[] pBuff;
}
void main()
{
ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\2.exe)");
}
导出表
导出的行为一般是dll给其他程序提供函数,变量,类导出表位于数据目录表的第0项
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 未使用,总是定义为0
DWORD TimeDateStamp; // 文件生成时间
WORD MajorVersion; // 未使用,总是定义为0
WORD MinorVersion; // 未使用,总是定义为0
DWORD Name; // 模块的真实名称
DWORD Base; // 基数,加上序数就是函数地址数组的索引值
DWORD NumberOfFunctions; // 导出函数的总数
DWORD NumberOfNames; // 以名称方式导出的函数的总数
DWORD AddressOfFunctions; // 导出地址表(EAT),指向输出函数地址的RVA
DWORD AddressOfNames;// 导出名称表(ENT),指向输出函数名字的RVA
DWORD AddressOfNameOrdinals;//指向导出序号数组(EOT),指向输出函数序号的RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
导出表解析
#include<Windows.h>
#include<iostream>
using namespace std;
DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
for (int i = 0; i < f->NumberOfSections; i++)
{
// FOA = 数据的RVA + 区段的RVA - 区段的FOA
if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->SizeOfRawData))
{
return rva - p->VirtualAddress + p->PointerToRawData;
}
f++;
p++;
}
return 0;
}
void ImageNtHeader(_In_z_ const char* path)
{
// 获取文件对象
HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// 获取文件大小
DWORD fSize = GetFileSize(hfile, NULL);
char* pBuff = new char;
DWORD dwReadSize = 0;
// 读文件
BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
if (bSuccess)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory;
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(RvaToFoa(dataDirectory->VirtualAddress, pSectionHeader, pFileHeader) + pBuff);
char* szName = (char*)(RvaToFoa(pExport->Name, pSectionHeader, pFileHeader) + pBuff);
if (pExport->AddressOfFunctions == 0)
{
cout.write("没有导出表", 20);
return;
}
cout << "导出表OFFSET:" << RvaToFoa(dataDirectory->VirtualAddress, pSectionHeader, pFileHeader) << endl;
cout << "特征值:" << pExport->Characteristics << endl;
cout << "基:" << pExport->Base << endl;
cout << "名称OFFSET:" << pExport->Name << endl;
cout << "名称:" << szName << endl;
cout << "函数数量:" << pExport->NumberOfFunctions << endl;
cout << "函数名数量:" << pExport->NumberOfNames << endl;
cout << "函数地址:" << pExport->AddressOfFunctions << endl;
cout << "函数名称地址:" << pExport->AddressOfNames << endl;
cout << "函数名称序号地址:" << pExport->AddressOfNameOrdinals << endl;
//函数地址数量
DWORD dwNumOfFun = pExport->NumberOfFunctions;
//函数名数量
DWORD dwNumOfNames = pExport->NumberOfNames;
//基
DWORD dwBase = pExport->Base;
//导出地址表
PDWORD pEAt32 = (PDWORD)(RvaToFoa(pExport->AddressOfFunctions, pSectionHeader, pFileHeader) + pBuff);
//导出名称表
PDWORD pENt32 = (PDWORD)(RvaToFoa(pExport->AddressOfNames, pSectionHeader, pFileHeader) + pBuff);
//导出序号表
PWORD pId = (PWORD)(RvaToFoa(pExport->AddressOfNameOrdinals, pSectionHeader, pFileHeader) + pBuff);
for (DWORD i = 0; i < dwNumOfFun; i++)
{
if (pEAt32 == 0)
{
continue;
}
DWORD Id = 0;
for (; Id < dwNumOfNames; Id++)
{
if (pId == i)
{
break;
}
}
if (Id == dwNumOfNames)
{
cout << "Id:%x" << i + dwBase << endl;
cout << "Address:" << pEAt32;
}
else
{
char* szFunName = (char*)(RvaToFoa(pENt32, pSectionHeader, pFileHeader) + pBuff);
cout << "Id:" << i + dwBase << endl;
cout << "Address" << pEAt32;
cout << "Name" << szFunName;
}
}
}
else (cout.write("打开文件失败", 20));
CloseHandle(hfile);
delete[] pBuff;
}
void main()
{
ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\2.exe)");
}
重定位表
Windows使用重定位机制保证代码无论模块加载到哪个基址都能正确被调用
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress; // 重定位内存页的起始地址RVA
DWORD SizeOfBlock; // 重定位块的长度
//WORD TypeOffset; 偏移12位,类型4位
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
TypeOffset高位字节
#define IMAGE_REL_BASED_ABSOLUTE 0 // 无意义,对齐用
#define IMAGE_REL_BASED_HIGH 1 // 双字中,仅高16位被修正
#define IMAGE_REL_BASED_LOW 2 // 双字中,仅低16位被修正
#define IMAGE_REL_BASED_HIGHLOW 3 // 双字32位都需要修正
#define IMAGE_REL_BASED_HIGHADJ 4// 进行基地址重定位时将差值的高16位加到指定偏移处的一个16位域上
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_5 5
#define IMAGE_REL_BASED_RESERVED 6
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_7 7
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_8 8
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_9 9
#define IMAGE_REL_BASED_DIR64 10 // 进行基地址重定位时将差值加到指定偏移处的一个64位域上
//
// Platform-specific based relocation types.
//
#define IMAGE_REL_BASED_IA64_IMM64 9
#define IMAGE_REL_BASED_MIPS_JMPADDR 5// 对MIPS平台的跳转指令进行基地址重定位
#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 // 对MIPS16平台的跳转指令进行基地址重定位
#define IMAGE_REL_BASED_ARM_MOV32 5
#define IMAGE_REL_BASED_THUMB_MOV32 7
重定位表解析
#include<Windows.h>
#include<iostream>
using namespace std;
DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
for (int i = 0; i < f->NumberOfSections; i++)
{
// FOA = 数据的RVA + 区段的RVA - 区段的FOA
if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->Misc.VirtualSize))
{
return rva - p->VirtualAddress + p->PointerToRawData;
}
f++;
p++;
}
return 0;
}
void ImageNtHeader(_In_z_ const char* path)
{
// 获取文件对象
HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// 获取文件大小
DWORD fSize = GetFileSize(hfile, NULL);
char* pBuff = new char;
DWORD dwReadSize = 0;
// 读文件
BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
if (bSuccess)
{
typedef struct _TYPE {
WORD Offset : 12; //大小 2bit 重定位的偏移
WORD Tyoe : 4;
}TYPE, * PTYPE;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
// PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory;
PIMAGE_BASE_RELOCATION pRel = (PIMAGE_BASE_RELOCATION)(pBuff + RvaToFoa(pOptionalHeader->DataDirectory.VirtualAddress, pSectionHeader, pFileHeader));
DWORD dwCount = 0;
while (*(PLONGLONG)pRel)
{
printf("[%d] VirtualAddress-> SizeOfBlock-> \r\n", dwCount++, pRel->VirtualAddress, pRel->SizeOfBlock);
//需要修复重定位项个数
DWORD dwRelEntry = (pRel->SizeOfBlock - 8) / 2;
//指向重定位项
PWORD pRelData = (PWORD)pRel + 8;
for (size_t i = 0; i < dwRelEntry; i++)
{
//判断高4位
//32位高4位0011
//64位高4位1010
if ((pRelData & 0x3000) == 0x3000)
{
//低12位 + VirtualAddress为真正需要修复数据的RVA
DWORD dwData = pRelData & 0xFFF;
DWORD dwDataRVA = dwData + pRel->VirtualAddress;
printf("[%d] DATA RVA\r\n", i, dwData, dwDataRVA);
}
}
//指向下一个重定位结构
pRel = (PIMAGE_BASE_RELOCATION)((PUCHAR)pRel + pRel->SizeOfBlock);
}
}
else (cout.write("打开文件失败", 20));
CloseHandle(hfile);
delete[] pBuff;
}
void main()
{
ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\2.exe)");
}
参考文章
https://blog.csdn.net/Gamma_lab/article/details/123869956
https://blog.csdn.net/adam001521/article/details/84658708
查考书
加密与解密
Windows PE权威指南
最后说明
导出和导入表,重定位代码可能有点问题,可以参考别的链接,目前不知道什么原因,有大佬发现可以指出
点赞支持,感谢楼主 代码源文件能够一起发一下么? metoo2 发表于 2022-5-7 13:42
代码源文件能够一起发一下么?
链接:https://pan.baidu.com/s/16iinLPqRWpcMAQKLUOd8GA
提取码:fkvz
--来自百度网盘超级会员V5的分享 谢谢大神,非常的有用。
页:
[1]