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
- 首先计算出RVA,RVA = 虚拟地址 - IMAGE_OPTIONAL_HEADER32.ImageBase
- 判断RVA是否在头部或者文件对齐和内存对齐一样,符合其中一项直接返回,因为头部在文件和内存中对齐方式是一样的,此时FOA == RVA,注意拓展PE头中的SizeOfHeaders已经包含了节表。
- 判断RVA处于哪一个节中,通过RVA和节在内存中的偏移得到RVA和所在节区的差值
- 差值加上节在文件中的偏移得到FOA,FOA = IMAGE_SECTION_HEADER.PointerToRawData + 差值
FOA转RVA同理,需要先找到FOA在文件中处于什么位置,如果在头中或者文件对齐与内存对齐一样,则可以直接得出结论(FOA == RVA),否则需通过FOA与节偏移的差值计算出RVA。
具体步骤:
- FOA - 指定节.PointerToRawData = 差值
- 差值 + 指定节.VirtuallAddress(节数据在内存中展开的位置) = RVA
- 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;
if (RVA < pOption->SizeOfHeaders || pOption->FileAlignment == pOption->SectionAlignment) { return RVA; }
PIMAGE_SECTION_HEADER pSectionHeader = pFirstSection;
for (int i = 0; i < pFile->NumberOfSections; i++) { if (RVA >= pSectionHeader->VirtualAddress && RVA < pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize) { DWORD relSectionImageAdd = RVA - pSectionHeader->VirtualAddress; 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;
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) { DWORD relSectionFileAdd = FOA - pSectionHeader->PointerToRawData; 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; }
DWORD RVA = 0x41a014 - pOption->ImageBase;
DWORD FOA = RVA2FOA(lpFileBuffer, RVA);
printf("FOA为:%d\n", *((DWORD *)((DWORD)lpFileBuffer + FOA)));
RVA = FOA2RVA(lpFileBuffer, FOA);
printf("RVA为:%X\nVA为:%X",RVA, RVA+pOption->ImageBase);
free(lpFileBuffer);
getchar();
}
|
运行结果: