PE文件格式的RVA概念

我们常说的RVA,很容易让我们理解成这是相对于节的RVA,其实不然。
要理解RVA的概念,首先还必须理解Section Header结构(由Section Header构成节表)。
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;

注意其中的VirtualAddress和PointerToRawData,这两个都是节的起始地址。不同之处在于:
VirtualAddress用在内存中指明该节的起始地址,而PointerToRawData用在文件中指明该
节的起始地址。


由于文件的对齐值和内存的对齐值不一样,所以,如果以直接用RVA(即iRVA)去定位的话,那么,可能是错误的。

这个RVA我把它分成三种情况,在不同情况下计算得出的RVA各给个名字:
为了方便理解,使用例子数据来说明。
typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];  // .text
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;    // 00069f9b
    } Misc;
    DWORD   VirtualAddress;      // 00000540
    DWORD   SizeOfRawData;      // 00069fc0
    DWORD   PointerToRawData;      // 00000540
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;      // 68000020
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
整理成和LordPE的形式如下:
名称Name  VOffset    VSize    ROffset    RSize    标志
.text    0000540    00069f9b  00000540  00069fc0  68000020

虚拟地址VA = 00049586,ImageBase = 00040000

1、该RVA是以ImageBase为参考的,即是通过RVA=VA - ImageBase = 00049586-00040000=9586 我们称其为iRVA(我们常说为RVA)
2、该RVA是以VOffset(如.text节的起始地址)为参考的,即是通过RVA=VA - VOffset,我们称其为mRVA(内存节偏移)
3、真正的文件偏为fRVA

我们很清楚的知道,唯有它们以节的起始地址为参考的相对偏移才是固定的。我们就根据这个来计算。
我们以VA=00049586为例子,为了得到它的文件偏移,我们需要经过以下的步骤:
先把VA转换成iRVA,即iRVA = VA - ImageBase = 00049586-00040000=9586
把iRVA转换成mRVA,即mRVA = iRVA - VOffset = 9586-540=9046
得到了mRVA后,就很容易得到fRVA了,fRVA = mRVA+ROffset = 9046+540=9586
所以,VA=00049586的文件偏移为fRVA=9586
上面由于ROffset和VOffset值一样,所以,你别以为直接计算就行了。
最后总结:
1、虚拟地址转文件偏地址fRVA
fRVA = mRVA + ROffset

由于mRVA = iRVA - VOffset
   再由于iRVA = 虚拟地址 - ImageBase
所以,mRVA = (虚拟地址 - ImageBase) - VOffset

所以,fRVA = (虚拟地址 - ImageBase) - VOffset + ROffset

最终得到:
fRVA = (虚拟地址 - ImageBase) - VOffset + ROffset

一个简单的代入法!

2、RVA转文件偏移地址fRVA
同上

### 汇编语言与PE文件格式的关系 汇编语言是一种低级编程语言,它直接对应于计算机硬件指令集。通过汇编语言可以实现对二进制文件的精确控制和操作。PE(Portable Executable)文件格式是Windows操作系统使用的可执行文件标准,其内部结构复杂且层次分明。 #### 1. PE文件的基本组成 PE文件由多个部分构成,主要包括以下几个方面[^2]: - **DOS头**: 这是一个简单的头部结构,用于兼容旧版本的操作系统。 - **NT头**: 包含文件头和可选头,描述了整个PE文件的关键属性。 - **节表**: 列出了各个节的名字及其特性。 - **节数据**: 实际存储程序代码、资源和其他数据的部分。 这些组成部分可以通过汇编语言逐一访问并解析。 #### 2. 使用汇编语言解析PE文件的方法 要利用汇编语言来解析PE文件,通常需要遵循以下逻辑流程: ```assembly section .data peFile db 'example.exe', 0 ; 文件名字符串 fileHandle dd 0 ; 文件句柄变量 section .bss buffer resb 4096 ; 缓冲区预留空间 section .text global _start _start: mov eax, 5 ; sys_open 系统调用号 (Linux) mov ebx, peFile ; 文件路径地址 mov ecx, 0 ; O_RDONLY 打开模式只读 int 0x80 ; 中断请求内核服务 cmp eax, 0 ; 检查返回值是否小于零表示错误 jl error_exit ; 如果有错跳转到error_exit处理 mov [fileHandle], eax ; 将打开成功后的fd保存起来 read_dos_header: mov eax, 3 ; sys_read 系统调用号 mov ebx, [fileHandle] ; fd 文件描述符 lea ecx, [buffer] ; buf 存储位置指针 mov edx, 64 ; count 需要读取字节数量(假设dos header长度为64byte) int 0x80 ; 请求中断完成读入动作 parse_nt_headers: add ecx, offset_to_NT_Header ; 计算偏移至NT Header处 ... ``` 此代码片段展示了如何使用汇编语言加载一个PE文件,并尝试定位到它的`DOS头`以及进一步寻找`NT头`的位置[^1]。 #### 3. 关键概念解释 - **RVA (Relative Virtual Address)**: 是相对于映像基址的一个相对虚拟地址,在分析PE文件时非常重要,因为它帮助我们找到特定的数据项所在内存中的确切位置。 - **Section Table**: 描述了PE文件中不同区域的信息,比如`.text`, `.rdata`, 和`.idata`等各段的具体情况[^3]。 ### 结论 通过对PE文件的学习和理解,配合汇编语言的强大功能,能够深入探索Windows平台下各种应用程序的工作原理和技术细节。这不仅有助于提高逆向工程能力,也加深了对于现代软件架构的认识水平。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值