12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 277|回复: 0

PE文件笔记七 VA与FOA转换

[复制链接]

60

主题

60

帖子

130

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
130
发表于 2021-5-7 23:57:27 | 显示全部楼层 |阅读模式
继续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的转换后,后面就可以开始对程序做一些坏坏的事了(&#10049;′&#9697;`&#10049;)
刚开始使用VC6环境编译,发现编译出来的程序,文件对齐和内存对齐数值是一致的,没法起到较好的学习作用,于是改用VS2015编译出兼容XP的且文件对齐和内存对齐差别的程序,方便学习
PS:在文件对齐等于内存对齐的环境下,RVA就直接等于FOA了
RVA和FOA之间的差异归根结底就是在于文件对齐和内存对齐的差异上
下面附上编译出来的程序和修改过的程序,对应为GlobalVariety.exe和GlobalVariety2.exe,有兴趣的可以去修改试试(建议在虚拟机XP环境下测试,否则printf出来的地址可能禁绝确)
GlobalVariety.zip
110.7 KB, 下载次数: 25, 下载积分: 吾爱币 -1 CB


来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-4-29 07:24 , Processed in 0.125000 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表