参考链接

https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_nt_headers64

https://www.cnblogs.com/Hekeats-L/p/17997054

《逆向工程核心原理》

https://chatgpt.com/c/66dc593d-c970-8013-8cae-88fcd3d98948

DOS HEADER

兼容老版本

DOS STUB

DOS stub 是一种嵌入在 Windows 可执行文件(PE 文件)中的小程序,主要用于在 DOS 环境下运行这些文件时提供提示信息。它的主要作用可以概括为以下几点:

  1. 兼容性:在 16 位 DOS 系统中运行 32 位或 64 位的 Windows 可执行文件(PE 文件)时,DOS stub 提供一个简单的提示,告知用户此文件不能在 DOS 环境下运行。通常会显示类似于 This program cannot be run in DOS mode 的信息。

  2. 历史原因:DOS stub 起源于早期的 Windows 系统,当时 Windows 是基于 DOS 系统开发的。为了兼容旧的 DOS 环境,Windows 的可执行文件保留了 DOS stub。虽然现代系统不再需要这种功能,但为了保持格式的一致性,DOS stub 依然被保留。

  3. 最小化程序:在某些情况下,开发者可以自定义 DOS stub 的内容,甚至可以让它执行一些简单的功能。但通常情况下,DOS stub 仅仅是一个输出错误提示的占位符。

典型的 DOS stub 是一个非常小的 16 位程序,占用几乎忽略不计的空间,作用也相当有限,主要用于历史兼容性。

_IMAGE_NT_HEADERS64

typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

IMAGE_FILE_HEADER

个人理解类似于文件签名的东西(函数签名),用来区分文件的信息

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;

IMAGE_OPTIONAL_HEADER64

和文件的执行信息有关,总体的内存分配以及入口点之类的信息,其中描述了一些个导入表,导出表加载的RVA

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_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

_IMAGE_SECTION_HEADER

为下面结构体的数组组成,描述了每一个section的基本信息,主要描述了每一个节加载的虚拟地址也就是(RVA)。

typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
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-20240907220600172

image-20240907220617402

一些节(用section_header定位)

下面学习的是节中的重要的数据结构

IMAGE_IMPORT_DESCRIPTOR(比较重要的部分,也是存放再section中的)

类似于linux中的got表,可以通过optional header来定位,其中描述了其的RVA,先定位属于哪一个section,之后再根据与section rva的差值加上其文件偏移地址(point to raw data)来定位

一个descriptior描述的是一个动态动态链接库的导入函数的内容

INT存储的是导入函数名称的地址,每个占8字节(其中的值都是rva因此需要换算)

IAT存储的是导入函数的地址,但是一开始存放的和INT是一样的东西

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;//指向导入名称表(INT)
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;//库名称
DWORD FirstThunk;//指向导入地址表(IAT)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

直观图解

image-20240908000341512

struct IMAGE_EXPORT_DIRECTORY

一般在库中记录这函数的名称以及地址,结构如下:

image-20240908004842227

image-20240908004857794

010editor比较方便,详细记录着每个函数的名称所在位置,以及地址

image-20240908004936344

总体函数查找流程为:1.查找nameadd,找到对应的name,然后几下相应的索引,从original中取值,将此值作为add的查找索引。

关于patchpe