概述

花费了好长时间,总算弄清楚了PE文件

学完了之后,总的感觉对PE文件的学习,就是学习各种各样的结构体,以及各种结构体关系

正是因为学会不易,所以做这个记录,基本是各种结构体的定义说明,已经找函数的方法什么的,,,以便下次忘记了能很快想起来

PE文件基本结构


PE内存映射关系


MS-DOS 头部


IMAGE_DOS_HEADER

IMAGE_DOS_HEADER STRUCT
{
+0h WORD e_magic // Magic DOS signature MZ(4Dh 5Ah) DOS可执行文件标记
+2h WORD e_cblp // 文件最后页的字节数
+4h WORD e_cp // //文件页数
+6h WORD e_crlc //重定义元素个数
+8h WORD e_cparhdr //头部尺寸 以段落为单位
+0ah WORD e_minalloc //所需的最小附加段
+0ch WORD e_maxalloc //所需的最大附加段
+0eh WORD e_ss //DOS代码的初始化堆栈SS
+10h WORD e_sp //DOS代码的初始化堆栈指针SP
+12h WORD e_csum //校验和
+14h WORD e_ip // DOS代码的初始化指令入口[指针IP]
+16h WORD e_cs // DOS代码的初始堆栈入口
+18h WORD e_lfarlc //重分配表文件地址
+1ah WORD e_ovno //覆盖号
+1ch WORD e_res[4] // 保留字
+24h WORD e_oemid //OEM标识符
+26h WORD e_oeminfo //OEM信息
+29h WORD e_res2[10] //保留字
+3ch DWORD e_lfanew //指向PE文件头
} IMAGE_DOS_HEADER ENDS

没什么重要信息,需要注意的是 标志字,还有指向PE头的指针

PE头


IMAGE_NT_HEADER

typedef struct _IMAGE_NT_HEADERS {		//由 e_lfanew指向
+0h DWORD Signature; //标志位 PE00
+4h IMAGE_FILE_HEADER FileHeader; //20字节
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

标志位 PE00 基本上通过十六进制编辑器很容易找到

紧接着20字节的 IMAGE_FILE_HEADER PE文件头结构

然后就是可选头,虽然是可选但是可选头实际上是PE文件最重要的地方

IMAGE_FILE_HEADER


typedef struct _IMAGE_FILE_HEADER {
WORD Machine; //运行平台
WORD NumberOfSections; //文件的区块数目 节的数目
DWORD TimeDateStamp; //文件创建日期和时间
DWORD PointerToSymbolTable; //指向符号表(调试)
DWORD NumberOfSymbols; //符号表中符号个数(调试)
WORD SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER32结构大小 一般是224 (E0)
WORD Characteristics; //文件属性 具体查表
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

Machine 运行平台


Value   Meaning
IMAGE_FILE_MACHINE_I386 0x014c x86
IMAGE_FILE_MACHINE_IA64 0x0200 Intel Itanium
IMAGE_FILE_MACHINE_AMD64 0x8664 x64

Characteristics 文件属性



#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM 0x1000 // System File.
#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.

IMAGE_OPTIONAL_HEADER32


typedef struct_IMAGE_OPTIONAL_HEADER
{
//Standard fields +18h WORD Magic; //标志字 ROM映像文件(0107h) 32位PE是0x10b 64位PE是0x20b
+1Ah BYTE MajorLinkerVersion; //主版本号 连接器的版本号
+1Bh BYTE MinorLinkerVersion; //次版本号 连接器的版本号
+1Ch DWORD SizeOfCode; //所有含代码的节的总大小 (代码段的长度,如果有多个代码段,则是代码段长度的总和。)
+20h DWORD SizeOfInitializedData; //所有含已初始化数据的节的总大小 eg.0e00(3584)
+24h DWORD SizeOfUninitializedData; //所有含未初始化数据的节的大小 eg. 1400(5120)
+28h DWORD AddressOfEntryPoint; //程序执行入口RVA (为啥我的程序是00 00 00 00呢)
+2Ch DWORD BaseOfCode; //代码段起始地址的RVA
+30h DWORD BaseOfData; //数据段起始地址的RVA //NT additional fields //以下是属于NT结构增加的领域
+ DWORD ImageBase; //可执行文件默认装入的基地址 可以由编译器指定 默认40 00 00
DWORD SectionAlignment; //内存中块的对齐值(默认的块对齐值为1000H,4KB个字节)
DWORD FileAlignment;//文件中块的对齐值(默认值为200H字节,为了保证块总是从磁盘的扇区开始的)
WORD MajorOperatingSystemVersion;//要求操作系统的最低版本号的主版本号
WORD MinorOperatingSystemVersion;//要求操作系统的最低版本号的次版本号
WORD MajorImageVersion;//该可执行文件的主版本号
WORD MinorImageVersion;//该可执行文件的次版本号
WORD MajorSubsystemVersion;//要求最低之子系统版本的主版本号 默认 0004
WORD MinorSubsystemVersion;//要求最低之子系统版本的次版本号 默认 0000
DWORD Win32VersionValue;//保留字 默认00000000
DWORD SizeOfImage;//映象的大小,PE文件加载到内存中空间是连续的,这个值指定占用虚拟空间的大小。
DWORD SizeOfHeaders;//所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的。
DWORD CheckSum;//CRC检验和 一般为00000000
WORD Subsystem;//程序使用的用户接口子系统
WORD DllCharacteristics;//DLLmain函数何时被调用,默认为0
DWORD SizeOfStackReserve;//初始化时堆栈大小
DWORD SizeOfStackCommit;//初始化时实际提交的堆栈大小
DWORD SizeOfHeapReserve;//初始化时保留的堆大小
DWORD SizeOfHeapCommit;//初始化时实际提交的对大小
+72 DWORD LoaderFlags;//与调试有关,默认为0 92h
+74h DWORD NumberOfRvaAndSizes;//数据目录结构的数目 16 96h
+78h IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//数据目录表
};

DllCharacteristics (仅仅相对于DLL文件来说)



#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.
// 0x1000 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 // Driver uses WDM model
// 0x4000 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000

运行方式 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_DRIVER 11 //
#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
DllCharacteristics:DLL的文件属性,只对DLL文件有效,可以是下面定义中某些的组合:
#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.
// 0x1000 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 // Driver uses WDM model
// 0x4000 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000

数据目录列表含义


typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; //内存偏移
DWORD Size; //大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16

分别是:导出表、导入表、资源表、异常信息表、安全证书表、重定位表、调试信息表、版权所以表、全局指针表 TLS表、加载配置表、绑定导入表、IAT表、延迟导入表、COM信息表 最后一个保留未使用。

数据目录 共16个条目

节属性



IMAGE_SECTION_HEADER STRUCT
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 8个字节的节区名称
union Misc
DWORD PhysicalAddress;
DWORD VirtualSize; //节区的尺寸
ends
DWORD VirtualAddress; // 节区的 RVA 地址
DWORD SizeOfRawData; // 在文件中对齐后的尺寸 (节处理后大小)
DWORD PointerToRawData; // 在文件中的偏移量 (节数据地址)
DWORD PointerToRelocations; // 在OBJ文件中使用,重定位的偏移
DWORD PointerToLinenumbers; // 行号表的偏移(供调试使用地)
WORD NumberOfRelocations; // 在OBJ文件中使用,重定位项数目
WORD NumberOfLinenumbers; // 行号表中行号的数目
DWORD Characteristics; // 节属性如可读,可写,可执行等
IMAGE_SECTION_HEADER ENDS

在找数据在文件中的位置的时候 需要先减去VirtualAddress 然后再加上 PointerToRawData

节属性 Characteristics


IMAGE_SCN_CNT_CODE 0x00000020
The section contains executable code.
包含代码,常与 0x10000000一起设置。 IMAGE_SCN_CNT_INITIALIZED_DATA
0x00000040
The section contains initialized data.
该区块包含以初始化的数据。 IMAGE_SCN_CNT_UNINITIALIZED_DATA
0x00000080
The section contains uninitialized data.
该区块包含未初始化的数据。 IMAGE_SCN_MEM_DISCARDABLE
0x02000000
The section can be discarded as needed.
该区块可被丢弃,因为当它一旦被装入后,
进程就不在需要它了,典型的如重定位区块。 IMAGE_SCN_MEM_SHARED
0x10000000
The section can be shared in memory.
该区块为共享区块。 IMAGE_SCN_MEM_EXECUTE
0x20000000
The section can be executed as code.
该区块可以执行。通常当0x00000020被设置
时候,该标志也被设置。 IMAGE_SCN_MEM_READ
0x40000000
The section can be read.
该区块可读,可执行文件中的区块总是设置该
标志。 IMAGE_SCN_MEM_WRITE
0x80000000
The section can be written to.
该区块可写。

参考https://www.cnblogs.com/qintangtao/archive/2013/01/11/2857180.html

PE文件到内存的映射


windows装载器装载时只是建立好虚拟地址和PE文件之间的映射关系

需要时才加载到内存,只有真正执行到某个内存也中的指令或者数据的时候,这个页面才会从磁盘提交到物理内存中。

系统装载可执行文件的方式和内存映射还不一样

导入表 IMAGE_IMPORT_DESCRIPTOR (IID)


由可选头结构的数据目录的[1]指向

每一个动态链接库对应一个这样的结构,为0的结构表示结束

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; //指向上面的INT,是一个结构数组,里面保存了导入函数的信息(例如Msg的实际地址)
};                   //最后会以全0的结构为结束,其中每一项是一个结构,一项8个字节,是指向
                       //IMAGE_THUNK_DATA 看下面详解  
DWORD TimeDateStamp; //时间,一般不用 DWORD ForwarderChain; //链表前一个结构,一般不用
DWORD Name;            //上面说的DLL名称的RVA偏移通过偏移可以找到DLL名称
DWORD FirstThunk; // IAT 的RVA偏移.和originalFirstThunk不同
} IMAGE_IMPORT_DESCRIPTOR;

OriginalFirstThunk -> 指向 INT

FirstThunk -> 指向IAT

IMAGE_THUNK_DATA


最高位为1时 表示函数以序号方式输入,低31位看作一个函数序号

最高位为0时 表示函数以字符串类型的函数名方式输入,这时双字的值是一个RVA,指向一个IMAGE_IMPORT_BY_NAME结构

typedef struct _IMAGE_THUNK_DATA32 {
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal;
PIMAGE_IMPORT_BY_NAME AddressOfData;
} u1;
} IMAGE_THUNK_DATA32;

IMAGE_IMPORT_BY_NAME


IMAGE_IMPORT_BY_NAME struct
{
Hint WORD; //也表示函数的序号 不过这个字段是可选的 有些编译器总是将他设置为0
Name BYTE; //定义了导入函数的名字字符串 这是一个以0为结尾的字符串
}

IAT


INT 是死的,但是IAT可以被装载器重写

装载器从 IMAGE_IMPORT_DESCRIPTOROriginalFirstThunk 指向 INT 通过里面的 IMAGE_THUNK_DATA 结构指向 IMAGE_IMPORT_BY_NAME 结构找到指定的函数,然后把函数地址放到IAT中

加载器操作之后

真正的找导入表的方法

先根据 PE之后紧跟的节区表 解析好RVA地址 和 节区地址

根据 数据目录 找到导入表的地址

然后减去 节区的 RVA 地址 得到相对地址

相对地址加上在文件中的偏移量 得到 IID的位置(也即是保存导入的DLL的位置)

然后取得其中一个的OriginalFirstThunk 找到其中的地址

地址减去 节区的 RVA 地址 加上在文件中的偏移量 得到 INT的地址

通过INT地址找到INT, 判断值的最高位 为0则为指向IMAGE_IMPORT_BY_NAME结构的RVA指针

这个指针减去 节区的 RVA 地址 就是函数所在位置 前两个字节是序号 和软件对比 没有问题就找对了

今天最美的图片 (哭死)

根据地址判断导入表在那个区 2554在2000 - 3000 所以是 .rdata

导出表 IMAGE_EXPORT_DIRECTORY (IED)


typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 未使用,总为0
DWORD TimeDateStamp; // 文件创建时间戳
WORD MajorVersion; // 未使用,总为0
WORD MinorVersion; // 未使用,总为0
DWORD Name; // 指向一个代表此 DLL名字的 ASCII字符串的 RVA
DWORD Base; // 函数的起始序号
DWORD NumberOfFunctions; // 导出函数的总数
DWORD NumberOfNames; // 以名称方式导出的函数的总数
DWORD AddressOfFunctions; // 指向输出函数地址的RVA
DWORD AddressOfNames; // 指向输出函数名字的RVA
DWORD AddressOfNameOrdinals; // 指向输出函数序号的RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
Characteristics:现在没有用到,一般为0。
TimeDateStamp:导出表生成的时间戳,由连接器生成。
MajorVersion,MinorVersion:看名字是版本,实际貌似没有用,都是0。
Name:模块的名字。
Base:序号的基数,按序号导出函数的序号值从Base开始递增。
NumberOfFunctions:所有导出函数的数量。
NumberOfNames:按名字导出函数的数量。
AddressOfFunctions:一个RVA,指向一个DWORD数组,数组中的每一项是一个导出函数的RVA,顺序与导出序号相同。
AddressOfNames:一个RVA,依然指向一个DWORD数组,数组中的每一项仍然是一个RVA,指向一个表示函数名字。
AddressOfNameOrdinals:一个RVA,还是指向一个WORD数组,数组中的每一项与AddressOfNames中的每一项对应,表示该名字的函数在AddressOfFunctions中的序号。

导出函数结构图


来自: https://blog.csdn.net/adam001521/article/details/84658708**


在上图中,AddressOfNames指向一个数组,数组里保存着一组RVA,每个RVA指向一个字符串,这个字符串即导出的函数名,与这个函数名对应的是AddressOfNameOrdinals中的对应项。获取导出函数地址时,先在AddressOfNames中找到对应的名字,比如Func2,他在AddressOfNames中是第二项,然后从AddressOfNameOrdinals中取出第二项的值,这里是2,表示函数入口保存在AddressOfFunctions这个数组中下标为2的项里,即第三项,取出其中的值,加上模块基地址便是导出函数的地址。如果函数是以序号导出的,那么查找的时候直接用序号减去Base,得到的值就是函数在AddressOfFunctions中的下标。

找导出函数的练习

随便找了一个DLL Binglib.dll 手动解析结果如下(不熟练 花费了足足一个半小时 不过已经会找了)

运行平台: 01 4C -> x86
文件区块数目: 0x 00 05 -> 5个区块
文件属性: 21 02 -> ‭0010000100000010‬ ->
1. #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable
2. #define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
3. #define IMAGE_FILE_DLL 0x2000 // File is a DLL. 标志字: 01 0B -> 表示32位PE
连接器版本: 0A 00 -> 10.0
程序执行入口: 27 4D
代码段起始RVA地址: 10 00
数据段起始RVA地址: 40 00
默认装入地址: 10 00 00 00
内存中块的对齐值: 10 00
文件对齐值: 02 00
映像大小: 90 00
所有文件头大小: 04 00
程序使用的用户接口子系统: 00 02 ->
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI 节信息:
.text
节区尺寸: 20 75
节区RVA: 10 00
对齐之后大小: 22 00
文件中偏移量: 04 00
节的属性: 60 00 00 20 -> ‭01100000000000000000000000100000‬
1. IMAGE_SCN_CNT_CODE 0x00000020 包含代码,常与 0x10000000一起设置。
2. IMAGE_SCN_MEM_EXECUTE 0x20000000 该区块可以执行。通常当0x00000020被设置时候,该标志也被设置。
3. IMAGE_SCN_MEM_READ 0x40000000 该区块可读,可执行文件中的区块总是设置该标志。 .rdata
节区尺寸: 17 7C
节区RVA: 40 00
对齐之后大小: 18 00
文件中偏移: 26 00
节的属性: 40 00 00 40 -> ‭01000000000000000000000001000000‬
1. IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 该区块包含以初始化的数据。
2. IMAGE_SCN_MEM_READ 0x40000000 该区块可读,可执行文件中的区块总是设置该标志。 .data
节区尺寸: 06 94
节区RVA: 60 00
对齐之后大小: 02 00
文件中偏移: 3e 00
... rsrc
节区尺寸: 05 c0
节区RVA: 70 00
对齐之后大小: 06 00
文件偏移: 40 00
... .reloc
节区尺寸: 07 80
节区RVA: 80 00
对齐之后大小: 08 00
文件偏移: 46 00
... 导出表信息: 55 30 (由上面节区信息可得在 .rdata 节区)
文件中位置: 55 30 - 40 00 + 26 00 = 3B 30
DLL实际名称: 55 E4 - 40 00 + 26 00 = 3B E4 (42 69 6e 67 6c 69 62 2e 64 6c 6c 00) Binglib.dll
起始序号: 01
导出函数总数: 0e -> 14个
以名称方式导出: 0e -> 14个
函数地址数组RVA: 55 58 - 40 00 + 26 00 = 3B 58(既然是RVA 则应该是DWORD) 例如此时值是19 a0 根据节区范围得知是在 .text 节区计算得 DA0就是函数真实位置
函数名称数组RVA: 55 90 - 40 00 + 26 00 = 3B 90(值仍然是RVA)55 f0 -> 3B F0 (??0MyFileW@@QAE@XZ) 第一项
函数序号数组RVA: 55 C8 - 40 00 + 26 00 = 3B C8(WORD类型的值)

软件结果

完美 非常谨慎的结果就是 没有差错 找到了函数所在位置 哈哈

重定位

Windows使用重定位机制保证以上代码无论模块加载到哪个基址都能正确被调用

为什么需要重定位?我的理解是,假设说一个dll文件被加载,里面的函数或者常数地址是不确定的,不顺利的情况下,模块的基址会变,那么以找基址和模块内偏移寻址的方式就是出错,就会出现找不到函数地址的情况。

过程

  1. 编译的时候由编译器识别出哪些项使用了模块内的直接VA,比如push一个全局变量、函数地址,这些指令的操作数在模块加载的时候就需要被重定位。

  2. 链接器生成PE文件的时候将编译器识别的重定位的项纪录在一张表里,这张表就是重定位表,保存在DataDirectory中,序号是 IMAGE_DIRECTORY_ENTRY_BASERELOC。

  3. PE文件加载时,PE 加载器分析重定位表,将其中每一项按照现在的模块基址进行重定位。

_IMAGE_BASE_RELOCATION


typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress; //页起始地址RVA。
DWORD SizeOfBlock; //表示该分组保存了几项重定位项。
// WORD TypeOffset[1]; //这个域有两个含义,大家都知道,页内偏移用12位就可以表示,剩下的高4位用来表示重定位的类型。而事实上,Windows只用了一种类型IMAGE_REL_BASED_HIGHLOW 数值是 3。
} IMAGE_BASE_RELOCATION;

总结


需要重定位的

  1. 代码中使用全局变量的指令,因为全局变量一定是模块内的地址,而且使用全局变量的语句在编译后会产生一条引用全局变量基地址的指令。

  2. 将模块函数指针赋值给变量或作为参数传递,因为赋值或传递参数是会产生mov和push指令,这些指令需要直接地址。

  3. C++中的构造函数和析构函数赋值虚函数表指针,虚函数表中的每一项本身就是重定位项

完结撒花 ★,°:.☆( ̄▽ ̄)/$:.°★

[逆向] PE文件学习的更多相关文章

  1. PE文件学习系列笔记四-C++实现PE文件的分析

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 综述: 首 ...

  2. PE文件学习系列三-PE头详解

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 最近比较忙 ...

  3. PE文件学习系列一为什么是PE

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入)Q  Q:408365330     E-Mail:egojit@qq.com PE概述: ...

  4. 【学习】Windows PE文件学习(一:导出表)

    今天做了一个读取PE文件导出表的小程序,用来学习. 参考了<Windows PE权威指南>一书. 首先, PE文件的全称是Portable Executable,可移植的可执行的文件,常见 ...

  5. PE文件学习系列二 DOS头分析

    合肥程序员群:49313181.    合肥实名程序员群 :128131462 (不愿透露姓名和信息者勿加入)Q  Q:408365330     E-Mail:egojit@qq.com PE文件结 ...

  6. PE文件学习(1)DOS和NT

    大致结构 DOS头和NT头之间通常还有个DOS Stub DOS头 DOS头的作用是兼容MS-DOS操作系统中的可执行文件 一般没啥用 记录着PE头的位置 DOS头定义部分 typedef struc ...

  7. PE文件学习(2)导入表导出表

    转自:evil.eagle https://blog.csdn.net/evileagle/article/details/12176797 导出表是用来描述模块中的导出函数的结构,如果一个模块导出了 ...

  8. 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)

    0 前言 此篇文章想写如何通过工具手查导出表.PE文件代码编程过程中的原理.文笔不是很好,内容也是查阅了很多的资料后整合出来的.希望借此加深对PE文件格式的理解,也希望可以对看雪论坛有所贡献.因为了解 ...

  9. [系统安全] 十六.PE文件逆向基础知识(PE解析、PE编辑工具和PE修改)

    [系统安全] 十六.PE文件逆向基础知识(PE解析.PE编辑工具和PE修改) 文章来源:https://masterxsec.github.io/2017/05/02/PE%E6%96%87%E4%B ...

  10. 深入学习PE文件(转)

    PE文件是Win32的原生文件格式.每一个Win32可执行文件都遵循PE文件格式.对PE文件格式的了解可以加深你对Win32系统的深入理解. 一. 基本结构. 上图便是PE文件的基本结构.(注意:DO ...

随机推荐

  1. vscode推荐插件

    js相关的插件 JavaScript (ES6) code snippets Babel ES6/ES7 html css 汉化 Chinese (Simplified) (简体中文) Languag ...

  2. 对象数组,如果key中的value相同,不要该项

    <script type="text/javascript"> let arr=[ { gradeId: "498094709437239572", ...

  3. 【转载】基于Tablestore Timeline的IM(即时通讯)消息系统架构 - 架构篇

    本文原作者:木洛,阿里云高级技术专家,内容有优化和修订,感谢原作者.原文链接:https://developer.aliyun.com/article/698301 IM全称是『Instant Mes ...

  4. TienChin 活动管理-活动导出

    ActivityController /** * 导出活动列表 */ @PreAuthorize("hasPermission('tienchin:activity:export')&quo ...

  5. 知识蒸馏相关技术【模型蒸馏、数据蒸馏】以ERNIE-Tiny为例

    1.任务简介 基于ERNIE预训练模型效果上达到业界领先,但是由于模型比较大,预测性能可能无法满足上线需求. 直接使用ERNIE-Tiny系列轻量模型fine-tune,效果可能不够理想.如果采用数据 ...

  6. Python自动化办公--Pandas玩转Excel数据分析【二】

    相关文章: Python自动化办公--Pandas玩转Excel[一] Python自动化办公--Pandas玩转Excel数据分析[三] python处理Excel实现自动化办公教学(含实战)[一] ...

  7. 7.5 通过API判断进程状态

    进程状态的判断包括验证进程是否存在,实现方法是通过枚举系统内的所有进程信息,并将该进程名通过CharLowerBuff转换为小写,当转换为小写模式后则就可以通过使用strcmp函数对比,如果发现继承存 ...

  8. Pdfium.Net.Free 一个免费的Pdfium的 .net包装器--添加文本

    项目地址: Pdfium.Net:https://github.com/1000374/Pdfium.Net PdfiumViewer:https://github.com/1000374/Pdfiu ...

  9. 从嘉手札<09-06-2023>

    时常会想 这个世界什么是长久的 我们走在时代的映照下,行色匆匆. 因为别人的悲欢而悲欢,因为自己的局限而挣扎. 晨而得志,暮而踌躇. 青楼梦好,难赋深情. 这个世界有很多的选择. 金钱,酒色,健康,相 ...

  10. 进程锁(互斥锁)(Python)

    3:# 抢票示例 import json import time from multiprocessing import Process,Lock def search(i): with open(' ...