RVA与FOA

RVA(Relative Virtual Address)是表相对虚拟地址

FOA(File Offset Address)文件偏移地址

在计算FOA之前需要先取消PE文件的动态基址,VS编译的程序默认都是动态基址,在Configuration Properties>Linker>Advanced下,找到Randomized Base Address。

或者直接修改PE文件结构:

定位到PE标识,往后偏移4个字节找到标准PE头,再往后偏移20个字节找到拓展PE头。找到IMAGE_OPTIONAL_HEADER.DllCharacteristics,如下

得到值为0x8140,将0x40标识的IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE置0


计算过程

因为内存和文件对齐方式虽然不一样,但结构体和成员之间的偏移是不会发生变化的。PE文件在内存中拉伸只是填充了对齐部分,所以可以通过RVA在内存中与其他结构体的差值计算出FOA

  1. 首先计算出RVA,RVA = 虚拟地址 - IMAGE_OPTIONAL_HEADER32.ImageBase
  2. 判断RVA是否在头部或者文件对齐和内存对齐一样,符合其中一项直接返回,因为头部在文件和内存中对齐方式是一样的,此时FOA == RVA,注意拓展PE头中的SizeOfHeaders已经包含了节表。
  3. 判断RVA处于哪一个节中,通过RVA和节在内存中的偏移得到RVA和所在节区的差值
  4. 差值加上节在文件中的偏移得到FOA,FOA = IMAGE_SECTION_HEADER.PointerToRawData + 差值

FOA转RVA同理,需要先找到FOA在文件中处于什么位置,如果在头中或者文件对齐与内存对齐一样,则可以直接得出结论(FOA == RVA),否则需通过FOA与节偏移的差值计算出RVA。

具体步骤:

  1. FOA - 指定节.PointerToRawData = 差值
  2. 差值 + 指定节.VirtuallAddress(节数据在内存中展开的位置) = RVA
  3. VA = RVA + ImageBase

实现

通过FOA修改目标程序的全局变量

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include<Windows.h>
#include<stdio.h>
#include<stdlib.h>
#define FILEPATH TEXT("D:\\absolution\\temporary\\test.exe")
#define OUTFILEPATH TEXT("D:\\absolution\\temporary\\output.exe")
#include<PETool.h>
#pragma comment(lib,"PETool.lib")

DWORD RVA2FOA(LPVOID lpBuffer,DWORD RVA) {
PIMAGE_DOS_HEADER pDos =
(PIMAGE_DOS_HEADER)lpBuffer;
PIMAGE_NT_HEADERS pNT =
(PIMAGE_NT_HEADERS)((DWORD)lpBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile =
(PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOption =
(PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pFirstSection =
PIMAGE_SECTION_HEADER((DWORD)pOption + pFile->SizeOfOptionalHeader);

DWORD FOA;

/*判断RVA是否处于头中*/
if (RVA < pOption->SizeOfHeaders || pOption->FileAlignment == pOption->SectionAlignment) {
return RVA;
}

PIMAGE_SECTION_HEADER pSectionHeader = pFirstSection;

/*判断RVA处于哪一个节区*/
for (int i = 0; i < pFile->NumberOfSections; i++) {
if (RVA >= pSectionHeader->VirtualAddress &&
RVA < pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize) {
/*获取RVA和节区VA的偏移*/
DWORD relSectionImageAdd = RVA - pSectionHeader->VirtualAddress;
/*偏移加上节区文件地址得到FOA*/
FOA = relSectionImageAdd + pSectionHeader->PointerToRawData;
return FOA;
}

/*指向下一个节表*/
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
}

return NULL;

}

DWORD FOA2RVA(LPVOID lpBuffer, DWORD FOA) {
PIMAGE_DOS_HEADER pDos =
(PIMAGE_DOS_HEADER)lpBuffer;
PIMAGE_NT_HEADERS pNT =
(PIMAGE_NT_HEADERS)((DWORD)lpBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile =
(PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOption =
(PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pFirstSection =
PIMAGE_SECTION_HEADER((DWORD)pOption + pFile->SizeOfOptionalHeader);

DWORD RVA;

/*判断FOA是否处于头中*/
if (FOA < pOption->SizeOfHeaders || pOption->FileAlignment == pOption->SectionAlignment) {
return FOA;
}

PIMAGE_SECTION_HEADER pSectionHeader = pFirstSection;

for (int i = 0; pFile->NumberOfSections; i++) {
if (FOA >= pSectionHeader->PointerToRawData &&
FOA < pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData) {
/*获取FOA和节区文件地址的偏移*/
DWORD relSectionFileAdd = FOA - pSectionHeader->PointerToRawData;
/*偏移加节区的VA得到RVA*/
RVA = relSectionFileAdd + pSectionHeader->VirtualAddress;
return RVA;
}
/*指向下一个节表*/
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
}

return NULL;
}

int main() {

LPVOID lpFileBuffer;

lpFileBuffer = createFileBuffer(FILEPATH);

PIMAGE_DOS_HEADER pDos =
(PIMAGE_DOS_HEADER)lpFileBuffer;
PIMAGE_NT_HEADERS pNT =
(PIMAGE_NT_HEADERS)((DWORD)lpFileBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile =
(PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
PIMAGE_OPTIONAL_HEADER pOption =
(PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pFirstSection =
PIMAGE_SECTION_HEADER((DWORD)pOption + pFile->SizeOfOptionalHeader);

/*确认指纹,注意小端储存*/
if (*(WORD*)pDos != IMAGE_DOS_SIGNATURE) {
OutputDebugString("Not Portable Execute!\n");
return FALSE;
}

/*获取RVA:VA - IMAGE_OPTIONAL_HEADER.ImageBase
VA为另一程序中某全局变量的地址。*/
DWORD RVA = 0x41a014 - pOption->ImageBase;

DWORD FOA = RVA2FOA(lpFileBuffer, RVA);

/*将FOA加上文件首地址后解引,验证结果。*/
printf("FOA为:%d\n", *((DWORD *)((DWORD)lpFileBuffer + FOA)));

RVA = FOA2RVA(lpFileBuffer, FOA);

/*验证RVA和VA*/
printf("RVA为:%X\nVA为:%X",RVA, RVA+pOption->ImageBase);

free(lpFileBuffer);


getchar();

}

运行结果: