首页
社区
课程
招聘
[原创]pe文件结构解读-1
2023-5-6 18:02 6225

[原创]pe文件结构解读-1

2023-5-6 18:02
6225

前言:

pe文件逆向分析、免杀、病毒分析,脱壳加壳都是有着非常重要的技能,掌握学习pe还是比较重要滴

1.PE文件简介

PE(PortableExecutable),即可移植的执行体。所有的Windows(包括Win9x、WinNT、Win CE)下的可执行文件(包括EXE、DLL、SYS、OCX、COM)均使用PE文件结构,这些使用PE文件结构的可执行文件也成为PE文件。
作为一个普通的程序员也许没有必要掌握 PE文件结构,因为普通的程序员大多都是开发服务性、决策性、的软件。但是对于学习黑客知识或者学习安全编程的程序员来说,那么掌握PE文件结构的知识就非常重要了。通过pe文件可以大致猜出此文件的功能

 

现在对PE结构有一个整体上的认识,要知道PE结构包含的结构体有DOS头、PE头(PE标识、文件头、可选头)、节表、节表数据等,如图所示:
图片描述
DOS头部其实是一个DOS头部,也可以叫MZ头,该部分用于在DOS下加载可执行程序,是用IMAGE_DOS_HEADER来定义的
DOS存根是一段简单的程序,主要用于输出"This program cannot be run in DOS mode"类似的字符串。
PE头部是保存Windows系统加载可执行文件的重要信息。
节表:是PE文件对后续节表数据的描述。
节表的数据:每个节实际上是一个容器,这里面可以包含程序的代码、数据等等,每个节可以有独立的内存权限,比如代码节默认有读/执行权限,节的名字和数量可以自己定义。

1.1 DOS头部详解:

IMAGE_DOS_HEADER:
DOS部分主要是为了兼容以前的DOS系统,DOS部分可以分为DOS MZ文件头(IMAGE_DOS_HEADER)和DOS块(DOS Stub)组成,PE文件的第一个字节位于一个传统的MS-DOS头部,称作IMAGE_DOS_HEADER,占用64个字节 ,其结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;        // 一个WORD类型,值是一个常数0x4D5A,用文本编辑器查看该值位‘MZ’,可执行文件必
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // 指向PE文件头的地址
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

DOS部分我们主要的是e_magic成员和e_lfanew成员,前者是标识PE指纹的一部分,后者则是寻找PE文件头的部分,除了这两个成员,其他成员全部用0填充都不会影响程序正常运行,所以我们不需要过多的对其他部分深究,DOS部分在16进制编辑器中看就是下图的部分:

DOS存根:我们可以看到e_lfanew指向PE文件头,我们可以通过它来寻找PE文件头,而DOS块的部分自然就是PE文件头和DOS MZ文件头中间的部分,这部分是由链接器所写入的,可以随意进行修改,并不影响程序的运行:

1.2 PE头部详解:

IMAGE_NT_HEADERS:
PE文件头由PE文件头标志,标准PE头,扩展PE头三部分组成。PE头部是真正用来装载Win32程序的头部,PE文件头标志是50 45 00 00,也就是PE,我们从结构体的角度看一下PE文件头的详细信息:
64位和32位结构体不同是IMAGE_OPTIONAL_HEADER的字节不同,体现在拓展PE头,64位结构体占240字节、32位结构体占224字节。(读取标准pe头的Magic字段为0x010b为32位程序,为0x020b是64位程序)

1
2
3
4
5
typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
1
2
3
4
5
typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;                         //PE文件头标志 => 4字节
    IMAGE_FILE_HEADER FileHeader;             //文件头标准PE头 => 20字节
    IMAGE_OPTIONAL_HEADER32 OptionalHeader; //扩展PE头 => 32位下224字节(0xE0) 64位下240字节(0xF0)
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

PE标识Signature:

PE标识占4个字节,winhex打开如下所示4550,ANSI码为PE

标准PE头IMAGE_FILE_HEADER:

标准PE头结构如下,有20个字节,主要存储运行(平台、节的数量、时间、拓展头大小、文件类型)我们可以从PE文件头标志后20个字节找到它,如下图:

1
2
3
4
5
6
7
8
9
typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;                 //可以运行的目标CPU类型
    WORD    NumberOfSections;         //节的数量
    DWORD   TimeDateStamp;             //表示文件何时被创建的编译器填写的时间戳
    DWORD   PointerToSymbolTable;   //调试相关
    DWORD   NumberOfSymbols;         //调试相关
    WORD    SizeOfOptionalHeader;   //标识扩展PE头(IMAGE_OPTIONAL_HEADER)大小
    WORD    Characteristics;        //文件的属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;


(1)Machine:可以运行的目标CPU类型
(2)NumberOfSection:表示节表的数目,节表紧跟在IMAGE_NT_HEADERS后面
(3)TimeDateStamp:表示文件的创建时间。
(4)PointerToSymbolTable:调试相关
(5)NumberOfSymbols: 调试相关
(6)SizeOfOptionalHeaders:标识pe拓展头的大小
(7)Characteristics: 文件的属性,普通的exe字段一般为010fh,dll字段为2102h

扩展PE头IMAGE_OPTIONAL_HEADER:

IMAGE_OPTIONAL_HEADER被称为"可选头"。虽然被称为可选头,但是必须存在的一个头,之所以称作为"可选头"认为是在该头的数据目录数组中,有的数据目录项(后面会讲到)是可有可无的,这部分内容是可选的。可选头紧挨着文件头。其中较为重要的是文件对齐、内存对齐、数据目录表。

32位结构如下:

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
typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //
 
    WORD    Magic;                        //指定文件状态的类型 PE32: 10B PE64: 20B
    BYTE    MajorLinkerVersion;            //主链接版本号
    BYTE    MinorLinkerVersion;            //次链接版本号
    DWORD   SizeOfCode;                    //所有含有代码的区块的大小 编译器填入 没用(可改)
    DWORD   SizeOfInitializedData;        //所有初始化数据区块的大小 编译器填入 没用(可改)
    DWORD   SizeOfUninitializedData;    //所有含未初始化数据区块的大小 编译器填入 没用(可改)
    DWORD   AddressOfEntryPoint;        //程序入口RVA
    DWORD   BaseOfCode;                    //代码区块起始RVA
    DWORD   BaseOfData;                    //数据区块起始RVA
 
    //
    // NT additional fields.
    //
 
    DWORD   ImageBase;                        //内存镜像基址(程序默认载入基地址)
    DWORD   SectionAlignment;                 //内存中对齐大小
    DWORD   FileAlignment;                     //文件中对齐大小(提高程序运行效率)
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;                    //内存中整个PE文件的映射的尺寸,可比实际值大,必须是SectionAlignment的整数倍
    DWORD   SizeOfHeaders;                     //所有的头加上节表文件对齐之后的值
    DWORD   CheckSum;                        //映像校验和,一些系统.dll文件有要求,判断是否被修改
    WORD    Subsystem;                       
    WORD    DllCharacteristics;                //文件特性,不是针对DLL文件的,16进制转换2进制可以根据属性对应的表格得到相应的属性
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //数据目录表,结构体数组
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

64位结构如下:

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
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;

这里介绍32位的结构,因为32位结构覆盖完了64位所有属性,只是属性的占用字节数不同(加粗的是32位有的,64位没用的):
(1)Magic:这是一个标记字,32位为010bh,64位为020bh
(2)MajorLinkerVersion: 主链接版本号
(3)MinorLinkerVersion: 次链接版本号
(4)SizeOfCode: 代码节的大小,如果有多个代码节的话,就是它们的总和。该处是指所有包含可执行属性的节的大小。
(5)SizeOflnitializedData: 已初始化数据块的大小。
(6)SizeOfUninitializedData:未初始化数据块的大小。
(7)AddressOfEntryPoint:程序执行的入口地址,该地址是一个相对虚拟地址,该地址简称EP。如果加壳后,找到了该地址,就被称作了OEP。该地址指向的不是main(),也不是WinMain()的地址该地址指向了运行库代码的地址。对于DLL这个值的意义不大,因为DLL甚至可以没有DIIMain()函数,没有DIIMain()是无法捕获DLL的4个消息的
(8)BaseOfCode:代码段的起始相对虚拟地址。
(9)BaseOfData:数据段的起始相对虚拟地址。
(10)ImageBase:文件被装入内存后的首选建议装载地址。对于EXE文件来说,通常情况下该地址就是装载地址:对于DLL 来说,可能就不是其装入内存后的地址了。程序加载到内存的起始地址
(11)SectionAlignment:节被装入内存后的对齐值。节被映射到内存中需要对齐的单位。通常情况下0x1000,也就是4KB大小。Windows操作系统的内存分页一般为4KB
(12)FileAlignment:节在文件中的对齐值。节在磁盘上是对齐单位。
(13)MajorOperatingSystemVersion:要求最低操作系统的主版本号。
(14)MinorOperatingSystemVersion:要求最低操作系统的次版本号。
(15)MajorImageVersion:可执行文件的主版本号。
(16)MinorImageVersion:可执行文件的次版本号
(I7)MajorSussystemVersion:要求最低子系统的主版本号。
(18)MinorSubsystemVersion:要求最低子系统的次版本号。
(19)Win32VersionValue:该成员变量是被保留的。
(20)SizeOflmage:可执行文件装入内存后的总大小。该大小按内存对齐方式对齐
(21)SizeOfHeaders:PE头的大小,这个PE 头泛指DOS头、PE头、节表的总和大小。
(22)CheckSum:校验和对于EXE文件通常为0对SYS文件则必须有一个校验和
(23)Subsystem:可执行文件的子系统类型(只对exe重要)。
(24)DIICharacteristics:指定DLL文件的属性。该值大部分时候为0。
(25)SizefStackReserve:为线程保留的栈大小
(26)SizeOfStackCommit:为线程已经提交的找大小
(27)SizeOfHeapReserve:为线程保留的堆大小
(28)SizeOfHeapCommit:为线程已经提交的堆大小
(29)LoaderFlags:被废弃的成员值。MSDN上的原话为“This member is obsolete”。但是该值在某些情况下还是会被用到的,比如针对旧版OD时,修改该值会起到反调试的作用
(30)NumberOfRvaAndSizes:数据目录项的个数。
(31)DataDirectory:数据目录表,由NumberOfRvaAndSize个IMAGE_DATA_DIRECTORY结构体组成。该数组中包含了输入表、输出表、资源等数据的RVA。IMAGE_DATA_DIRECTORY 的定义如下

1
2
3
4
typedef struct _IMAGE_DATA_DIRECTORY (
    DWORD VirtualAddress;
    DWORD Size;
) IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;

该结构体的第一个变量为该目录的相对虚拟地址的起始值,第二个是该目录的长度。(这里先做大概了解,后续文章会对数据目录表进行讲解)

1.3 节表详解:

在pe文件头与原始数据之间存在一个节区表(Section Table)。节区表中包含每个块在映像中的信息,分别指向不同的区块实体。

节表结构:

每个IMAGE_SECTION_HEADER代表一个节表,其中都存放着可执行文件被映射到内存中所在位置的信息节表的位置在IMAGE_OPTIONAL_HEADER的后面,其中节的个数由IMAGE_FILE_HEADER中的NumberOfSections给出。节表的结构如下,40个字节: DWORD 4个字节 WORD占2个字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME]; //ASCII字符串 可自定义 只截取8个字节
    union {                                   //该节在没有对齐之前的真实尺寸,该值可以不准确
            DWORD   PhysicalAddress; 
            DWORD   VirtualSize;   
    } Misc;
    DWORD   VirtualAddress;                //加载到内存中的部分的第一个字节的地址,相对于映像基地址RVA
    DWORD   SizeOfRawData;                   //节在文件中对齐的大小
    DWORD   PointerToRawData;               //节区在文件中的偏移
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;               //节的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

(1)Name: 该成员变量保存节的名称,节的名称用ASCII编码来保存。节名的长度IMAGE_SIZEOF_SHORT_NAME,这是一个宏,该宏的定义如下:

1
#define IMAGE_SIZEOF_SHORT_NAME 8

节名的长度为8个字节,多余的字节会被自动截断。通常情况下节名以“.”为开始,当然这是编译器的习惯。我们看一下图的前8个字节的内容为“2E74657874000000”其对应ASCII字符为“text
(2)VirtualSize: 指出实际被使用的区块大小,是在对齐处理前的区块的大小
(3)VirtualAddress: 该节区载入到内存后的相对虚拟地址。这个地址是按内存进行对齐的,是sectionAlignment的整数倍
(4)SizeOfRawData:该节区在磁盘上所占用的空间大小,该值通常是对齐后的值该字段包括经过FileAlignment调整后块的长度。例如FileAlignment的大小为200h,如果VirtualSize中的块长度为19Ah个字节,这一块保存的长度为200h个字节,用0进行填充。
(5)PointerToRawData:该节区在磁盘文件上的偏移值。
(6)PointerToRelocations:在exe文件中没用意义,在obj文件中,表示本块重定位信息的偏移量。
(7)PointerToLinenumbers:行号表在文件中的偏移量。这是文件的调试信息。
(8)NumberOfRelocations:在exe中无意义。
(9)NumberOfLinenumbers:该块在行号表中的行号数目
(10)Characteristics:节区属性。该属性的值如图所示。


代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//区段解析遍历
         printf("Selection Header Info:\n");
         PIMAGE_SECTION_HEADER ReadSectionHeader = IMAGE_FIRST_SECTION(ReadNTHeaders);
 
         PIMAGE_FILE_HEADER pFileHeader = &ReadNTHeaders->FileHeader;
         for (int i = 0; i < pFileHeader->NumberOfSections; i++)
         {
             printf("Name(区段名称):%s\n", ReadSectionHeader[i].Name);
             printf("VirtualSize(数据实际节区大小VirtualSize):%08X\n", ReadSectionHeader[i].Misc.VirtualSize);
             printf("VOffset(起始的相对虚拟地址VirtualAddress):%08X\n", ReadSectionHeader[i].VirtualAddress);
             printf("VSize(节区在磁盘上的大小SizeOfRawData):%08X\n", ReadSectionHeader[i].SizeOfRawData);
             printf("ROffset(节区在磁盘上的偏移PointerToRawData):%08X\n", ReadSectionHeader[i].PointerToRawData);
             printf("标记(区段的属性Characteristics):%08X\n\n", ReadSectionHeader[i].Characteristics);
         }

地址转换:

为什么要做地址转换,因为内存页和磁盘页对齐的大小不同,存储的数据代码在内存和磁盘的位置也不同,例如:如果你要修改pe文件,那么就需要进行地址转换。

RVA和FOA的转换:

一些pe文件为了减少体积,磁盘对齐值不是一个内存页1000h,而是200h。当这类文件被映射到内存中后,同一数据相对于文件头的偏移量在内存中和磁盘中是不同的,就出现了文件偏移和虚拟地址的偏移问题。

RVA : 相对虚拟地址(Relative Virtual Address),PE 文件中的各种数据结构中涉及地址的字段大部分都是以 RVA 表示的。
VA 是当PE 文件被装载到内存中后,某个数据位置相对于文件头的偏移量。实际的内存地址
FOA (文件偏移地址): PE文件储存在磁盘上时, 某个数据的位置相对于文件头的偏移量,称为文件偏移地址(File Offset)或物理地址(RAW Offset)文件偏移地址从PE文件的第一个字节开始计 数,起始值为0。用十六进制工具 (例如WinHex、C32等)打开文件 所显示的地址就是文件偏移地址。

1
RVA = 内存地址(VA) - ImageBase

RVA 转换为 FOA:
某数据的文件偏移 = 该数据所在节的起始文件偏移 + (某数据的RVA –该数据所在节的起始RVA)

1
2
3
4
5
6
PointerToRawData: 该节区在磁盘文件上的偏移值
VitualAddress: 该节区载入到内存后的相对虚拟地址
Misc.VirtualSize: 表示节的大小,是在对齐处理前的区块的大小
SizeOfHeaders:  PE头的大小,这个PE 头泛指DOS头、PE头、节表的总和大小
SectionAlignment:加载到内存中的节的对齐方式,以字节为单位。此值必须大于或等于 FileAlignment成员。默认值是系统的页面大小。
FileAlignment:图像文件中各个部分的原始数据的对齐方式,以字节为单位。该值应该是 2 的幂,介于 512 64K(含)之间。默认值 512。如果SectionAlignment成员小于系统页面大小,则该成员必须与 SectionAlignment相同。
1
2
3
4
5
6
7
8
9
10
11
12
13
第一种情况:文件对齐跟内存对齐一样的情况,那么这样就可以直接去找 RVA的地址了 这个地址也就是FOA
第二种情况:文件对齐和内存对齐不一样的情况
 
1.如果RVA属于文件头部(DOS头 + PE头 + 节表),头部大小是文件对齐大小的整数倍!
那么不需要进行计算了,因为DOS头和PE头和节表在文件中和在内存中展开都是一样的,直接从开始位置寻
找到RVA个字节即可,就是找RVA,也就是FOA(文件偏移地址)pOptionalHeader->SizeOfHeaders
 
2.判断 RVA 位于哪个节
RVA >= 节.VirtualAddress
RVA <= 节.VirtualAddress + 当前内存对齐后的大小(节.Misc.VirtualSize)
差值 = RVA - 节.VirtualAddress
 
3.FOA = 节.PointerToRawData + 差值

RVA转换FOA例子:

创建一个a64.exe变量:

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <stdlib.h>
int Test = 0x12345678;
int main(int argc, char* argv[])
{
    printf("全局变量地址 = %X \r\n", &Test);
    printf("全局变量值 = %X \r\n", Test);
 
    getchar();
}

输出的值是VA(内存地址)

VA 4001C000
Imagebase 40000000
RVA = VA -ImageBase = 1C000
1.判断RVA是否在PE文件头中
001FF <1C000 不在PE文件头中

 

2.判断属于哪个节:
.data.VirtualAddress >=1C000 且 1C000<1C200(.data.VirtualAddress+.data.SizeOfRawData) 所以属于这个data节区中
3.FOA = 节.PointerToRawData + 差值(RVA - 节.VirtualAddress)
=B200+1C000-1C000=B200
下图就找到了对应的值:

在winhex中修改为78 56 44 12运行a64.exe成功

代码:

VA->Offset

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
void CPEmfcDlg::OnBnClickedButtoncalc()
{
 
 
    //获取输入的VA
    DWORD dwRva;
    CString strRVA;
    GetDlgItem(IDC_EDIT_VAshow)->GetWindowText(strRVA);
    //字符串转换16进制
    _stscanf_s(strRVA.GetBuffer(), _T("%x"), &dwRva);
    CString str;
    DWORD offset =VA2Offset(dwRva, m_pNtHdr, m_pSecHdr);
    str.Format("%08X", offset);
    SetDlgItemText(IDC_EDIT_offsetshow, str);
    //SetDlgItemText(IDC_EDIT_quduan, (LPCTSTR)m_pSecHdr[nInNum].Name);
}
DWORD  VA2Offset(DWORD dwVa, PIMAGE_NT_HEADERS m_pNtHdr, PIMAGE_SECTION_HEADER m_pSecHdr)
    {
        //imagebase文件基地址
        DWORD dwImageBase = m_pNtHdr->OptionalHeader.ImageBase;
        //文件对齐和内存对齐相等且不在文件头中
        if (m_pNtHdr->OptionalHeader.FileAlignment == m_pNtHdr->OptionalHeader.SectionAlignment || dwVa <= m_pNtHdr->OptionalHeader.SizeOfHeaders)
        {
            return dwVa;
        }
        else
        {
            for (int nInNum = 0; nInNum < m_pNtHdr->FileHeader.NumberOfSections; nInNum++)
            {
                if (dwVa >= dwImageBase + m_pSecHdr[nInNum].VirtualAddress && dwVa <= dwImageBase + m_pSecHdr[nInNum].VirtualAddress + m_pSecHdr[nInNum].Misc.VirtualSize)
                {
 
                    return m_pSecHdr[nInNum].PointerToRawData + dwVa - m_pSecHdr[nInNum].VirtualAddress - m_pNtHdr->OptionalHeader.ImageBase;
                }
            }
        }
    }

FOA转换RVA:

文件地址为:FOA=15E00 ,根据区块表查看到了此在.rsrc区块数据中 。
15E00-9C00=C200 (这个为偏移量)
此时的区块相对虚拟地址为:D000,
RVA: C200+D000=19200
VA= imagebase +RVA =140000000+19200=140019200

最后:

感谢互联网知识分享~
可以关注微信公众号酷酷信安


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-5-8 13:52 被酷酷信安编辑 ,原因: 编辑排版
收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回