VA转FOA
相应名词概念
|
对应结构体成员 |
英文全称 |
含义 |
VA |
_IMAGE_SECTION_HEADER.VirtualAddress |
Virtual Address |
在内存中的虚拟地址 |
RVA |
_IMAGE_SECTION_HEADER.VirtualAddress |
Relative Virtual Address |
相对虚拟地址 |
FOA |
_IMAGE_SECTION_HEADER.PointerToRawData |
File Offset Address |
文件偏移地址 |
为什么需要学习VA与FOA之间的转换
此处需要引入一个问题:如何改变一个全局变量的初始值
- 如果一个全局变量有初始值,那么他的初试值一定是存储于PE文件中的。
- 如果一个全局变量没有初始值,那么在PE文件中就没有存储它的位置,只有当PE文件加载到内存中时,才会给它分配空间
修改程序的数据时,如果不懂的如何转换VA与FOA,我们在修改内存全局变量的时候就只能通过CE等辅助工具进行搜索修改了。
全局变量初始值Demo
利用c语言输出全局变量的值与地址。
1 2 3 4 5 6 7 8 9 10 11 12
| #include <stdio.h> int global = 0x610; int main(int argc, char* argv[]) { printf("address:%X\n", &global); printf("value:0x%X\n", global); getchar(); return 0; }
|
可以看到程序将全局变量地址与值打印到了dos窗口之上
修改全局变量的初始值
注意,在调试的时候,要选择低版本windows 如windows 2008,高版本windows启用随机内存地址保护,不利于我们调试,一开始踩了这个大坑,又空耗了1个多小时….
此时我们可以看到在运行时,全局变量的地址为0A425A30
此时我们如果使用16进制编辑器去寻找这个地址,我们是无法发现全局变量的。
这是由于PE文件在载入内存中时的地址是虚拟地址(VA)
VA = ImageBase + RVA
也就是说虚拟地址 = PE文件载入内存中后的基地址 + 相对虚拟地址(相对于基地址看齐)
而基地址是ImageBase字段的值,这点我们可以从PE头中获知
于是我们可以计算得出 RVA = VA - ImageBase
而其在PE文件中的地址为FOA(文件偏移地址)
最终问题就也就变成了 RVA与FOA的转换
通俗点来讲,就是说假设我们通过运行程序拿到RVA,想要获知FOA,就需要通过一定手段利用RVA来获知FOA,从而定位到我们想要的一个函数或变量的地址
VA到FOA转换流程
1.得到RVA的值: RVA = VA - ImageBase
2.判断RVA是否在PE文件头内
- 如果在,则FOA=RVA
- 如果不在,判断RVA在哪个节(区块),差值 = RVA - 区块.virtualAddress(RVA), FOA = 区块.PointerToRawData + 差值
根据流程转换
1.拿到RVA的值: RVA = VA - ImageBase
首先拿到ImageBase值
利用StudyPE来查看
显然,ImageBase的值为00400000
于是可以算出 RVA = VA - ImageBase = 425a30h - 400000h = 25A30h
2.判断RVA是否位于PE文件头之内
可以看到PE文件头的地址在F7h处结尾,而25A30h远远大于,因此不在pe文件头处
很显然,1F600已经超出了PE文件的最大范围,这证明我们定位的RVA ≠FOA
3.判断RVA属于那一节
根据RVA > = 区块.VirtualAddress
RVA < 区块.VirtualAddress + 当前节内存对齐后
1 2 3
| RVA>=节.VirtualAddress
RVA<节.VirtualAddress + 当前节内存对齐后的大小=节.VirtualAddress +[(Max{节.Misc,节.SizeOfRawData})÷SectionAlignment]向上取整×SectionAlignment
|
- 节.sizeOfRawData 是节文件对齐后的大小
- 节.Misc是节的实际大小
内存对齐后的大小 = [Max{实际的大小,文件对齐后的大小}÷内存对齐]向上取整×内存对齐
向上取整的意思就是 如果除后的结果为整数就直接为结果,如果除后的结果带小数则取整然后加一
例子:[5÷2]向上取整= 2.5取整+1=2+1=3,[4÷2]向上取整=2
根据工具可知,RVA = 0X25A30 处于.idata区块之中
程序在内存中的对齐基数为SectionAlign设置的0x1000
其实际大小 = 区块.Misc 在 StudyPE中显示为V.Size = 0x5604
Max{节.Misc,节.SizeofRawData} = Max{0x5604,0x4000} = 0x5604
内存对齐后的大小 = {0x5604 / 内存对齐} 向上取整*内存对齐 = (0x5604/0x1000)向上取整 * 0x1000 = (5 + 1 ) * 0x1000 = 0x6000
RVA>=0x25000
RVA<0X25000 + 内存对齐后的大小 = 0x25000 + 0x6000 = 0x2A000
差值 = RVA - 节.VirtualAddress = 0x25A30 - 0X25000 = 0XA30
PointerToRawData 在工具中显示为Offset,为0X25000
FOA = 节.PointerToRawData + 差值 = 0x25000 + 0XA30= 0x25A30
由此,找到了数值地址,进行修改为65535,再次运行,发现值已被修改.
利用代码实现VA转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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
#include <stdio.h> #include <malloc.h> #include <windows.h> #include <winnt.h> #include <math.h>
#define IMAGE_FILE_MACHINE_AMD64 0x8664
UINT VaToFoa32(UINT va, _IMAGE_DOS_HEADER *dos,_IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr) { UINT rva = va - nt->OptionalHeader.ImageBase; printf("rva:%X\n", rva); UINT PeEnd = (UINT)dos->e_lfanew+sizeof(_IMAGE_NT_HEADERS); printf("PeEnd:%X\n", PeEnd); if (rva < PeEnd) { printf("foa:%X\n", rva); return rva; } else { int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { UINT SizeInMemory = ceil((double)max((UINT)sectionArr[i]->Misc.VirtualSize ,(UINT)sectionArr[i]->SizeOfRawData ) / (double)nt->OptionalHeader.SectionAlignment)* nt->OptionalHeader.SectionAlignment;
if (rva >= sectionArr[i]->VirtualAddress && rva < (sectionArr[i]->VirtualAddress + SizeInMemory)) { printf("SizeInMemory:%X\n", SizeInMemory); break; } } if (i >= nt->FileHeader.NumberOfSections) { printf("没有找到匹配的节\n"); return -1; } else { int offset = rva - sectionArr[i]->VirtualAddress; int foa = sectionArr[i]->PointerToRawData + offset; printf("foa:%X\n", foa); return foa; }
}
}
UINT VaToFoa64(UINT va, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS64* nt, _IMAGE_SECTION_HEADER** sectionArr) { UINT rva = va - nt->OptionalHeader.ImageBase; printf("rva:%X\n", rva); UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS64); printf("PeEnd:%X\n", PeEnd); if (rva < PeEnd) { printf("foa:%X\n", rva); return rva; } else { int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { UINT SizeInMemory = ceil((double)max((UINT)sectionArr[i]->Misc.VirtualSize ,(UINT)sectionArr[i]->SizeOfRawData ) / (double)nt->OptionalHeader.SectionAlignment)* nt->OptionalHeader.SectionAlignment;
if (rva >= sectionArr[i]->VirtualAddress && rva < (sectionArr[i]->VirtualAddress + SizeInMemory)) { printf("SizeInMemory:%X\n", SizeInMemory); break; } } if (i >= nt->FileHeader.NumberOfSections) { printf("没有找到匹配的节\n"); return -1; } else { int offset = rva - sectionArr[i]->VirtualAddress; int foa = sectionArr[i]->PointerToRawData + offset; printf("foa:%X\n", foa); return foa; }
}
} int main(int argc, char* argv[]) { _IMAGE_DOS_HEADER* dos; HANDLE hFile = CreateFileA("C:\\Users\\86156\\Desktop\\N1.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, 0); LPVOID pFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); dos = (_IMAGE_DOS_HEADER*)pFile; printf("dos->e_magic:%X\n", dos->e_magic);
DWORD* peId; peId = (DWORD*)((UINT)dos + dos->e_lfanew); printf("peId:%X\n", *peId);
WORD* magic; magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER)); printf("magic:%X\n", *magic); switch (*magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: { printf("32位程序\n"); _IMAGE_NT_HEADERS* nt; nt = (_IMAGE_NT_HEADERS*)peId; printf("Machine:%X\n", nt->FileHeader.Machine); printf("Magic:%X\n", nt->OptionalHeader.Magic);
_IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**) malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);
_IMAGE_SECTION_HEADER* sectionHeader; sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS)); int cnt = 0; while(cnt< nt->FileHeader.NumberOfSections){ _IMAGE_SECTION_HEADER* section; section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER)*cnt); sectionArr[cnt++] = section; printf("%s\n", section->Name); }
VaToFoa32(0x4198B0,dos, nt, sectionArr);
break; }
case IMAGE_NT_OPTIONAL_HDR64_MAGIC: { printf("64位程序\n"); _IMAGE_NT_HEADERS64* nt; nt = (_IMAGE_NT_HEADERS64*)peId; printf("Machine:%X\n", nt->FileHeader.Machine); printf("Magic:%X\n", nt->OptionalHeader.Magic);
_IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);
_IMAGE_SECTION_HEADER* sectionHeader; sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS64)); int cnt = 0; while (cnt < nt->FileHeader.NumberOfSections) { _IMAGE_SECTION_HEADER* section; section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt); sectionArr[cnt++] = section; printf("%s\n", section->Name); } break; }
default: { printf("error!\n"); break; }
} getchar(); return 0; }
|
可以看到各个区块以及FOA RVA的值
FOA转VA转换流程
首先,拿到FOA偏移量:0x25A30
1.判断FOA是否在PE文件头之中
- 如果在PE文件头内 FOA =RVA
- 如果不在: 判断FOA位于哪个节,差值 = FOA - 节.PointerToRawData(R Offset) , RVA = 差值 + 节.VirtualAddress(RVA)
2.VA = ImageBase + RVA
根据流程进行转换
1.判断FOA是否在文件头内
根据e_ifanew找到PE头,发现相距甚远
2.判断处于哪一区块
FOA>=节.PointerToRawData
FOA<节.PointerToRawData + 当前节文件对齐后的大小=节.PointerToRawData+节.SizeOfRawData
翻译一下就是在哪份区块之中
另外,利用FOA求VA时,由于是查找FOA在哪个区块中,因此要利用PointerToRawData(R Size)与 PointerToRawSize(R size)进行判断,而不是VirstualAddress(V Addr) 与 VirstualSize(V Size) 利用VA求FOA时则相反
比较可知,根据FOA = 0x25A30可知,FOA在.idata区块之中
差值 = FOR - 节.PointerToRawData = 0x25A30 - 0x25000 = 0xA30
RVA = 节.VirstualAddress + 差值 = 0x25000 + 0xA30 = 0x25A30
VA = ImageBase + RVA = 0x400000 + 0x25A30 = 0x425A30
运行程序检验,发现完全吻合!
代码实现FOA 转 VA
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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
|
#include <stdio.h> #include <malloc.h> #include <windows.h> #include <winnt.h> #include <math.h>
#define IMAGE_FILE_MACHINE_AMD64 0x8664
UINT VaToFoa32(UINT va, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr) { UINT rva = va - nt->OptionalHeader.ImageBase; printf("rva:%X\n", rva); UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS); printf("PeEnd:%X\n", PeEnd); if (rva < PeEnd) { printf("foa:%X\n", rva); return rva; } else { int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { UINT SizeInMemory = ceil((double)max((UINT)sectionArr[i]->Misc.VirtualSize, (UINT)sectionArr[i]->SizeOfRawData) / (double)nt->OptionalHeader.SectionAlignment) * nt->OptionalHeader.SectionAlignment;
if (rva >= sectionArr[i]->VirtualAddress && rva < (sectionArr[i]->VirtualAddress + SizeInMemory)) { printf("SizeInMemory:%X\n", SizeInMemory); break; } } if (i >= nt->FileHeader.NumberOfSections) { printf("没有找到匹配的节\n"); return -1; } else { UINT offset = rva - sectionArr[i]->VirtualAddress; UINT foa = sectionArr[i]->PointerToRawData + offset; printf("foa:%X\n", foa); return foa; }
}
}
UINT VaToFoa64(UINT va, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS64* nt, _IMAGE_SECTION_HEADER** sectionArr) { UINT rva = va - nt->OptionalHeader.ImageBase; printf("rva:%X\n", rva); UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS64); printf("PeEnd:%X\n", PeEnd); if (rva < PeEnd) { printf("foa:%X\n", rva); return rva; } else { int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { UINT SizeInMemory = ceil((double)max((UINT)sectionArr[i]->Misc.VirtualSize, (UINT)sectionArr[i]->SizeOfRawData) / (double)nt->OptionalHeader.SectionAlignment) * nt->OptionalHeader.SectionAlignment; if (rva >= sectionArr[i]->VirtualAddress && rva < (sectionArr[i]->VirtualAddress + SizeInMemory)) { printf("SizeInMemory:%X\n", SizeInMemory); break; } } if (i >= nt->FileHeader.NumberOfSections) { printf("没有找到匹配的节\n"); return -1; } else { UINT offset = rva - sectionArr[i]->VirtualAddress; UINT foa = sectionArr[i]->PointerToRawData + offset; printf("foa:%X\n", foa); return foa; }
}
}
UINT FoaToVa32(UINT foa, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr) { UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS); if (foa < PeEnd) { printf("va:%X\n", foa + nt->OptionalHeader.ImageBase); return foa + nt->OptionalHeader.ImageBase; } else { int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) {
if (foa >= sectionArr[i]->PointerToRawData && foa < (sectionArr[i]->PointerToRawData + sectionArr[i]->SizeOfRawData)) { break; } } if (i >= nt->FileHeader.NumberOfSections) { printf("没有找到匹配的节\n"); return -1; } else { UINT offset = foa - sectionArr[i]->PointerToRawData; UINT rva = offset + sectionArr[i]->VirtualAddress; printf("va:%X\n", rva + nt->OptionalHeader.ImageBase); return rva + nt->OptionalHeader.ImageBase; } } return 0; }
UINT FoaToVa64(UINT foa, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS64* nt, _IMAGE_SECTION_HEADER** sectionArr) { UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS64); if (foa < PeEnd) { printf("va:%X\n", foa + nt->OptionalHeader.ImageBase); return foa + nt->OptionalHeader.ImageBase; } else { int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) {
if (foa >= sectionArr[i]->PointerToRawData && foa < (sectionArr[i]->PointerToRawData + sectionArr[i]->SizeOfRawData)) { break; } } if (i >= nt->FileHeader.NumberOfSections) { printf("没有找到匹配的节\n"); return -1; } else { UINT offset = foa - sectionArr[i]->PointerToRawData; UINT rva = offset + sectionArr[i]->VirtualAddress; printf("va:%X\n", rva + nt->OptionalHeader.ImageBase); return rva + nt->OptionalHeader.ImageBase; } } return 0; }
int main(int argc, char* argv[]) { _IMAGE_DOS_HEADER* dos; HANDLE hFile = CreateFileA("C:\\Users\\86156\\Desktop\\N1.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, 0); LPVOID pFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); dos = (_IMAGE_DOS_HEADER*)pFile; printf("dos->e_magic:%X\n", dos->e_magic);
DWORD* peId; peId = (DWORD*)((UINT)dos + dos->e_lfanew); printf("peId:%X\n", *peId);
WORD* magic; magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER)); printf("magic:%X\n", *magic); switch (*magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: { printf("32位程序\n"); _IMAGE_NT_HEADERS* nt; nt = (_IMAGE_NT_HEADERS*)peId; printf("Machine:%X\n", nt->FileHeader.Machine); printf("Magic:%X\n", nt->OptionalHeader.Magic);
_IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);
_IMAGE_SECTION_HEADER* sectionHeader; sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS)); int cnt = 0; while (cnt < nt->FileHeader.NumberOfSections) { _IMAGE_SECTION_HEADER* section; section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt); sectionArr[cnt++] = section; printf("%s\n", section->Name); }
VaToFoa32(0x4198B0, dos, nt, sectionArr); FoaToVa32(0x176B0, dos, nt, sectionArr);
break; }
case IMAGE_NT_OPTIONAL_HDR64_MAGIC: { printf("64位程序\n"); _IMAGE_NT_HEADERS64* nt; nt = (_IMAGE_NT_HEADERS64*)peId; printf("Machine:%X\n", nt->FileHeader.Machine); printf("Magic:%X\n", nt->OptionalHeader.Magic);
_IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);
_IMAGE_SECTION_HEADER* sectionHeader; sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS64)); int cnt = 0; while (cnt < nt->FileHeader.NumberOfSections) { _IMAGE_SECTION_HEADER* section; section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt); sectionArr[cnt++] = section; printf("%s\n", section->Name); } VaToFoa32(0x4198B0, dos, (_IMAGE_NT_HEADERS *)nt, sectionArr); FoaToVa32(0x176B0, dos, (_IMAGE_NT_HEADERS * )nt, sectionArr); break; }
default: { printf("error!\n"); break; }
} getchar(); return 0; }
|
根据代码,我们可以.data区块的RVA值
本文复现学习自52pojie.cn论坛的lyl610abc师傅PE文件笔记。
原文连接:
1
| https://www.52pojie.cn/thread-1412395-1-1.html
|