继续PE系列笔记的更新
PE别的笔记索引可前往:
PE文件笔记一 PE介绍
前面学习了在块表中提到了VA和FOA,这次来学习它们之间的转换
VA转FOA
在先前的笔记PE文件笔记六 节表和节中,已经有提到关于VA、RVA和FOA的概念
对应结构体成员英文全称含义VA_IMAGE_SECTION_HEADER.VirtualAddressVirtual Address在内存中的虚拟地址RVA_IMAGE_SECTION_HEADER.VirtualAddressRelative Virtual Address相对虚拟地址FOA_IMAGE_SECTION_HEADER.PointerToRawDataFile Offset Address文件偏移地址接下来继续学习VA和FOA的转换
在学习转换之前,先探寻一下为什么要学习它们之间的转换?
为什么学习VA与FOA转换
在这里要引入一个问题:如何改变一个全局变量的初始值
在逆向基础笔记十二 汇编 全局和局部 变量中已经说过了全局变量和局部变量的区别
- 如果一个全局变量有初始值,那么它的初始值一定是存储在PE文件中的
- 如果一个全局变量没有初始值,那么在PE文件中就没有存储它的位置,只有当PE文件加载到内存中时,才会给它分配空间
学习RVA与FOA的转换后,就可以修改步调中的数据后一劳永逸,无需每次都用CE等内存工具搜刮修改等等
全局变量初始值demo
为了学习,写一个小的步调,该步调输出全局变量的值和全局变量地址
代码
#include int global = 0x610;int main(int argc, char* argv[]){ //输出全局变量地址 printf("address:%X\n", &global); //输出全局变量的值 printf("value:0x%X\n", global); //暂停一下,防止窗口运行完自动关闭 getchar(); return 0;}运行结果
修改全局变量初始值
可以看到全局变量对应的地址为4198B0
那么是不是直接去PE文件中找到这个地址就行呢?当然不是
首先要明确,此时得到的全局变量地址是运行态时的地址,也就是VA(在内存中的虚拟地址)
VA = ImageBase + RVA
即:在内存中的虚拟地址 = 镜像基地址 + 相对虚拟地址
而 镜像基地址为扩展PE头中的ImageBase成员,是已知的
于是可以得到RVA = VA - ImageBase
而其在PE文件中的地址为FOA(文件偏移地址)
最终问题就也就变成了 RVA与FOA的转换
VA到FOA转换流程
1.得到RVA的值:RVA = VA - ImageBase
2.判断RVA是否位于PE文件头中
2.1如果是:FOA=RVA
2.2如果不是:判断RVA位于哪个节,差值 = RVA - 节.VirtualAddress(RVA),FOA = 节.PointerToRawData + 差值
按照流程转换
1.得到RVA的值:RVA = VA - ImageBase
首先用查察该PE文件的ImageBase
这里采用的PE工具为Detect It Easy,简称DIE
得到ImageBase为0x400000
于是可以得到RVA = VA - ImageBase = 0x4198B0 - 0x400000 = 0x198B0
2.判断RVA是否位于PE文件头中
可以用WinHex 找到PE文件头的部分
可以看到PE文件头的最后一位地址为:1F7
RVA = 0x198B0 显然超出了PE文件头的巨细
3.判断RVA属于哪个节
RVA>=节.VirtualAddress
RVA=0x19000
RVAOptionalHeader.ImageBase; //输出rva printf("rva:%X\n", rva); //找到PE文件头后的地址 = PE文件头首地址+PE文件头巨细 UINT PeEnd = (UINT)dos->e_lfanew+sizeof(_IMAGE_NT_HEADERS); //输出PeEnd printf("PeEnd:%X\n", PeEnd); //判断rva是否位于PE文件头中 if (rva < PeEnd) { //如果rva位于PE文件头中,则foa==rva,直接返回rva即可 printf("foa:%X\n", rva); return rva; } else { //如果rva在PE文件头外 //判断rva属于哪个节 int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { //计算内存对齐后节的巨细 UINT SizeInMemory = ceil((double)max((UINT)sectionArr->Misc.VirtualSize ,(UINT)sectionArr->SizeOfRawData ) / (double)nt->OptionalHeader.SectionAlignment)* nt->OptionalHeader.SectionAlignment; if (rva >= sectionArr->VirtualAddress && rva < (sectionArr->VirtualAddress + SizeInMemory)) { //找到所属的节 //输出内存对齐后的节的巨细 printf("SizeInMemory:%X\n", SizeInMemory); break; } } if (i >= nt->FileHeader.NumberOfSections) { //未找到 printf("没有找到匹配的节\n"); return -1; } else { //计算差值= RVA - 节.VirtualAddress int offset = rva - sectionArr->VirtualAddress; //FOA = 节.PointerToRawData + 差值 int foa = sectionArr->PointerToRawData + offset; printf("foa:%X\n", foa); return foa; } }}//VA转FOA 64位//第一个参数为要转换的在内存中的地址:VA//第二个参数为指向dos头的指针//第三个参数为指向nt头的指针//第四个参数为存储指向节指针的数组UINT VaToFoa64(UINT va, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS64* nt, _IMAGE_SECTION_HEADER** sectionArr) { //得到RVA的值:RVA = VA - ImageBase UINT rva = va - nt->OptionalHeader.ImageBase; //输出rva printf("rva:%X\n", rva); //找到PE文件头后的地址 = PE文件头首地址+PE文件头巨细 UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS64); //输出PeEnd printf("PeEnd:%X\n", PeEnd); //判断rva是否位于PE文件头中 if (rva < PeEnd) { //如果rva位于PE文件头中,则foa==rva,直接返回rva即可 printf("foa:%X\n", rva); return rva; } else { //如果rva在PE文件头外 //判断rva属于哪个节 int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { //计算内存对齐后节的巨细 UINT SizeInMemory = ceil((double)max((UINT)sectionArr->Misc.VirtualSize ,(UINT)sectionArr->SizeOfRawData ) / (double)nt->OptionalHeader.SectionAlignment)* nt->OptionalHeader.SectionAlignment; if (rva >= sectionArr->VirtualAddress && rva < (sectionArr->VirtualAddress + SizeInMemory)) { //找到所属的节 //输出内存对齐后的节的巨细 printf("SizeInMemory:%X\n", SizeInMemory); break; } } if (i >= nt->FileHeader.NumberOfSections) { //未找到 printf("没有找到匹配的节\n"); return -1; } else { //计算差值= RVA - 节.VirtualAddress int offset = rva - sectionArr->VirtualAddress; //FOA = 节.PointerToRawData + 差值 int foa = sectionArr->PointerToRawData + offset; printf("foa:%X\n", foa); return foa; } }}int main(int argc, char* argv[]){ //创建DOS对应的结构体指针 _IMAGE_DOS_HEADER* dos; //读取文件,返回文件句柄 HANDLE hFile = CreateFileA("C:\\Users\\lyl610abc\\Desktop\\GlobalVariety.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; //输出dos->e_magic,以十六进制输出 printf("dos->e_magic:%X\n", dos->e_magic); //创建指向PE文件头标记的指针 DWORD* peId; //让PE文件头标记指针指向其对应的地址=DOS首地址+偏移 peId = (DWORD*)((UINT)dos + dos->e_lfanew); //输出PE文件头标记,其值应为4550,否则不是PE文件 printf("peId:%X\n", *peId); //创建指向可选PE头的第一个成员magic的指针 WORD* magic; //让magic指针指向其对应的地址=PE文件头标记地址+PE文件头标记巨细+标准PE头巨细 magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER)); //输出magic,其值为0x10b代表32位步调,其值为0x20b代表64位步调 printf("magic:%X\n", *magic); //根据magic判断为32位步调还是64位步调 switch (*magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: { printf("32位步调\n"); //确定为32位步调后,就可以使用_IMAGE_NT_HEADERS来接收数据了 //创建指向PE文件头的指针 _IMAGE_NT_HEADERS* nt; //让PE文件头指针指向其对应的地址 nt = (_IMAGE_NT_HEADERS*)peId; printf("Machine:%X\n", nt->FileHeader.Machine); printf("Magic:%X\n", nt->OptionalHeader.Magic); //创建一个指针数组,该指针数组用来存储所有的节表指针 //这里相当于_IMAGE_SECTION_HEADER* sectionArr[nt->FileHeader.NumberOfSections],声明了一个动态数组 _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"); //确定为64位步调后,就可以使用_IMAGE_NT_HEADERS64来接收数据了 //创建指向PE文件头的指针 _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[nt->FileHeader.NumberOfSections],声明了一个动态数组 _IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections); //创建指向块表的指针 _IMAGE_SECTION_HEADER* sectionHeader; //让块表的指针指向其对应的地址,区别在于这里加上的偏移为_IMAGE_NT_HEADERS64 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; } } return 0;}运行结果
可以看到计算出来的结果和前面手动计算的同等
FOA转VA
理解了前面的VA转FOA后,再来学习一下它的逆过程:FOA转VA
如今将前面转换的结果:0x000176B0拿来:
实验逆推出地址
FOA转VA转换流程
1.判断FOA是否位于PE文件头中
1.1如果是:FOA=RVA
1.2如果不是:判断FOA位于哪个节,差值 = FOA - 节.PointerToRawData ,RVA = 差值 + 节.VirtualAddress(RVA)
2.VA = ImageBase + RVA
按照流程转换
1.判断FOA是否位于PE文件头中
显然FOA在PE文件头之外
1.2.判断FOA位于哪个节
FOA>=节.PointerToRawData
FOA=0x16e00
FOAOptionalHeader.ImageBase; //输出rva printf("rva:%X\n", rva); //找到PE文件头后的地址 = PE文件头首地址+PE文件头巨细 UINT PeEnd = (UINT)dos->e_lfanew+sizeof(_IMAGE_NT_HEADERS); //输出PeEnd printf("PeEnd:%X\n", PeEnd); //判断rva是否位于PE文件头中 if (rva < PeEnd) { //如果rva位于PE文件头中,则foa==rva,直接返回rva即可 printf("foa:%X\n", rva); return rva; } else { //如果rva在PE文件头外 //判断rva属于哪个节 int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { //计算内存对齐后节的巨细 UINT SizeInMemory = ceil((double)max((UINT)sectionArr->Misc.VirtualSize ,(UINT)sectionArr->SizeOfRawData ) / (double)nt->OptionalHeader.SectionAlignment)* nt->OptionalHeader.SectionAlignment; if (rva >= sectionArr->VirtualAddress && rva < (sectionArr->VirtualAddress + SizeInMemory)) { //找到所属的节 //输出内存对齐后的节的巨细 printf("SizeInMemory:%X\n", SizeInMemory); break; } } if (i >= nt->FileHeader.NumberOfSections) { //未找到 printf("没有找到匹配的节\n"); return -1; } else { //计算差值= RVA - 节.VirtualAddress UINT offset = rva - sectionArr->VirtualAddress; //FOA = 节.PointerToRawData + 差值 UINT foa = sectionArr->PointerToRawData + offset; printf("foa:%X\n", foa); return foa; } }}//VA转FOA 64位//第一个参数为要转换的在内存中的地址:VA//第二个参数为指向dos头的指针//第三个参数为指向nt头的指针//第四个参数为存储指向节指针的数组UINT VaToFoa64(UINT va, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS64* nt, _IMAGE_SECTION_HEADER** sectionArr) { //得到RVA的值:RVA = VA - ImageBase UINT rva = va - nt->OptionalHeader.ImageBase; //输出rva printf("rva:%X\n", rva); //找到PE文件头后的地址 = PE文件头首地址+PE文件头巨细 UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS64); //输出PeEnd printf("PeEnd:%X\n", PeEnd); //判断rva是否位于PE文件头中 if (rva < PeEnd) { //如果rva位于PE文件头中,则foa==rva,直接返回rva即可 printf("foa:%X\n", rva); return rva; } else { //如果rva在PE文件头外 //判断rva属于哪个节 int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { //计算内存对齐后节的巨细 UINT SizeInMemory = ceil((double)max((UINT)sectionArr->Misc.VirtualSize ,(UINT)sectionArr->SizeOfRawData ) / (double)nt->OptionalHeader.SectionAlignment)* nt->OptionalHeader.SectionAlignment; if (rva >= sectionArr->VirtualAddress && rva < (sectionArr->VirtualAddress + SizeInMemory)) { //找到所属的节 //输出内存对齐后的节的巨细 printf("SizeInMemory:%X\n", SizeInMemory); break; } } if (i >= nt->FileHeader.NumberOfSections) { //未找到 printf("没有找到匹配的节\n"); return -1; } else { //计算差值= RVA - 节.VirtualAddress UINT offset = rva - sectionArr->VirtualAddress; //FOA = 节.PointerToRawData + 差值 UINT foa = sectionArr->PointerToRawData + offset; printf("foa:%X\n", foa); return foa; } }}//FOA转VA 32位//第一个参数为要转换的在文件中的地址:VA//第二个参数为指向dos头的指针//第三个参数为指向nt头的指针//第四个参数为存储指向节指针的数组UINT FoaToVa32(UINT foa, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr) { //找到PE文件头后的地址 = PE文件头首地址+PE文件头巨细 UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS); //判断FOA是否位于PE文件头中 if (foa < PeEnd) { //如果foa位于PE文件头中,则foa==rva,直接返回foa+ImageBase即可 printf("va:%X\n", foa+nt->OptionalHeader.ImageBase); return foa + nt->OptionalHeader.ImageBase; } else { //如果foa在PE文件头外 //判断foa属于哪个节 int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { if (foa >= sectionArr->PointerToRawData && foa < (sectionArr->PointerToRawData + sectionArr->SizeOfRawData)) { //找到所属的节 break; } } if (i >= nt->FileHeader.NumberOfSections) { //未找到 printf("没有找到匹配的节\n"); return -1; } else { //计算差值= FOA - 节.PointerToRawData UINT offset = foa - sectionArr->PointerToRawData; //RVA = 差值 + 节.VirtualAddress(RVA) UINT rva = offset+ sectionArr->VirtualAddress; printf("va:%X\n", rva + nt->OptionalHeader.ImageBase); return rva + nt->OptionalHeader.ImageBase; } } return 0;}//FOA转VA 64位//第一个参数为要转换的在文件中的地址:VA//第二个参数为指向dos头的指针//第三个参数为指向nt头的指针//第四个参数为存储指向节指针的数组UINT FoaToVa64(UINT foa, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS64* nt, _IMAGE_SECTION_HEADER** sectionArr) { //找到PE文件头后的地址 = PE文件头首地址+PE文件头巨细 UINT PeEnd = (UINT)dos->e_lfanew + sizeof(_IMAGE_NT_HEADERS64); //判断FOA是否位于PE文件头中 if (foa < PeEnd) { //如果foa位于PE文件头中,则foa==rva,直接返回foa+ImageBase即可 printf("va:%X\n", foa + nt->OptionalHeader.ImageBase); return foa + nt->OptionalHeader.ImageBase; } else { //如果foa在PE文件头外 //判断foa属于哪个节 int i; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { if (foa >= sectionArr->PointerToRawData && foa < (sectionArr->PointerToRawData + sectionArr->SizeOfRawData)) { //找到所属的节 break; } } if (i >= nt->FileHeader.NumberOfSections) { //未找到 printf("没有找到匹配的节\n"); return -1; } else { //计算差值= FOA - 节.PointerToRawData UINT offset = foa - sectionArr->PointerToRawData; //RVA = 差值 + 节.VirtualAddress(RVA) UINT rva = offset + sectionArr->VirtualAddress; printf("va:%X\n", rva + nt->OptionalHeader.ImageBase); return rva + nt->OptionalHeader.ImageBase; } } return 0;}int main(int argc, char* argv[]){ //创建DOS对应的结构体指针 _IMAGE_DOS_HEADER* dos; //读取文件,返回文件句柄 HANDLE hFile = CreateFileA("C:\\Users\\sixonezero\\Desktop\\GlobalVariety.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; //输出dos->e_magic,以十六进制输出 printf("dos->e_magic:%X\n", dos->e_magic); //创建指向PE文件头标记的指针 DWORD* peId; //让PE文件头标记指针指向其对应的地址=DOS首地址+偏移 peId = (DWORD*)((UINT)dos + dos->e_lfanew); //输出PE文件头标记,其值应为4550,否则不是PE文件 printf("peId:%X\n", *peId); //创建指向可选PE头的第一个成员magic的指针 WORD* magic; //让magic指针指向其对应的地址=PE文件头标记地址+PE文件头标记巨细+标准PE头巨细 magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER)); //输出magic,其值为0x10b代表32位步调,其值为0x20b代表64位步调 printf("magic:%X\n", *magic); //根据magic判断为32位步调还是64位步调 switch (*magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: { printf("32位步调\n"); //确定为32位步调后,就可以使用_IMAGE_NT_HEADERS来接收数据了 //创建指向PE文件头的指针 _IMAGE_NT_HEADERS* nt; //让PE文件头指针指向其对应的地址 nt = (_IMAGE_NT_HEADERS*)peId; printf("Machine:%X\n", nt->FileHeader.Machine); printf("Magic:%X\n", nt->OptionalHeader.Magic); //创建一个指针数组,该指针数组用来存储所有的节表指针 //这里相当于_IMAGE_SECTION_HEADER* sectionArr[nt->FileHeader.NumberOfSections],声明了一个动态数组 _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"); //确定为64位步调后,就可以使用_IMAGE_NT_HEADERS64来接收数据了 //创建指向PE文件头的指针 _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[nt->FileHeader.NumberOfSections],声明了一个动态数组 _IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections); //创建指向块表的指针 _IMAGE_SECTION_HEADER* sectionHeader; //让块表的指针指向其对应的地址,区别在于这里加上的偏移为_IMAGE_NT_HEADERS64 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, nt, sectionArr); FoaToVa32(0x176B0, dos, nt, sectionArr); break; } default: { printf("error!\n"); break; } } return 0;}运行结果
可以看到计算出来的结果和前面手动计算的同等
说明
学会了VA与FOA的转换后,后面就可以开始对步调做一些坏坏的事了(❁′◡`❁)
刚开始使用VC6环境编译,发现编译出来的步调,文件对齐和内存对齐数值是同等的,没法起到较好的学习作用,于是改用VS2015编译出兼容XP的且文件对齐和内存对齐差别的步调,方便学习
PS:在文件对齐即是内存对齐的情况下,RVA就直接即是FOA了
RVA和FOA之间的差异归根结底就是在于文件对齐和内存对齐的差异上
下面附上编译出来的步调和修改过的步调,对应为GlobalVariety.exe和GlobalVariety2.exe,有爱好的可以去修改试试(发起在虚拟机XP环境下测试,否则printf出来的地址可能不正确)
GlobalVariety.zip
110.7 KB, 下载次数: 30, 下载积分: 吾爱币 -1 CB
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |