首页 > 其他分享 >PE结构之RVA转换成FOA(3)

PE结构之RVA转换成FOA(3)

时间:2022-08-25 09:46:45浏览次数:111  
标签:RVA IMAGE printf rva HEADER FOA PE nt

RVA转FOA

简介

PE文件有两种状态, 一种是在文件中的状态,另外一种是在内存中展开

若我们运行了一个PE文件且知道了某个全局变量的地址, 那么该如何通过这个全局变量地址来获得此变量在文件状态下的地址是什么呢?

RVA(relative Virtual Address), 又称为相对虚拟偏移,简单来说就是在内存状态下的偏移地址

FOA(File Ofseet Address), 又称为文件偏移地址, 就是在文件状态下的偏移地址

下图是PE文件在文件对齐和内存对齐状态下的映像结构图

这里文件对齐值是200,内存对齐的值是1000

内存对齐后的映像分布有个明显的拉伸

请添加图片描述


计算方法

1.求RVA的值

RVA = 全局变量在内存中的地址 - ImageBase(基址)

2.判断RVA是否位于PE头中或者内存对齐=文件对齐

如果RVA位于PE头中,则RVA = FOA

如果文件对齐 = 内存对齐,则 RVA = FOA

如果不在则进行下述操作

3.判断RVA位于哪个节

假设rva位于X节中,也就是说X节.VirtualAddress <= RVA <= X节.VirtualAddress+X节内存对齐后的大小

差值 = RVA - X节.VirtualAddress

4.得出FOA

FOA = X节.PointerToRawData(X节在文件中的地址) + 差值


C++代码

代码出处:https://www.52pojie.cn/thread-1408576-1-1.html

// PE.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <malloc.h>
#include <windows.h>
#include <winnt.h>
#include <math.h>
//在VC6这个比较旧的环境里,没有定义64位的这个宏,需要自己定义,在VS2019中无需自己定义
#define IMAGE_FILE_MACHINE_AMD64  0x8664

//VA转FOA 32位
//第一个参数为要转换的在内存中的地址:VA
//第二个参数为指向dos头的指针
//第三个参数为指向nt头的指针
//第四个参数为存储指向节指针的数组
UINT VaToFoa32(UINT va, _IMAGE_DOS_HEADER *dos,_IMAGE_NT_HEADERS* 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_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[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 {
            //计算差值= RVA - 节.VirtualAddress
            int offset = rva - sectionArr[i]->VirtualAddress;
            //FOA = 节.PointerToRawData + 差值
            int foa = sectionArr[i]->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[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 {
            //计算差值= RVA - 节.VirtualAddress
            int offset = rva - sectionArr[i]->VirtualAddress;
            //FOA = 节.PointerToRawData + 差值
            int foa = sectionArr[i]->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;
}

标签:RVA,IMAGE,printf,rva,HEADER,FOA,PE,nt
From: https://www.cnblogs.com/henry666/p/16623182.html

相关文章

  • PE结构之节的操作(4)
    节的操作若要对PE文件填充自己的代码,然后发现字节空间不足,这时候就需要对节表进行操作此处主要讲节的三种操作,分别为扩大节、新增节、合并节扩大节简介对文件的节表进......
  • PE结构之导入表与导出表(5)
    导出表与导入表通常来讲exe文件只有导入表而没有导出表,而dll文件既有导入表也有导出表导出表什么是导出表代码重用机制提供了重用代码的动态链接库,它会向调用者说明......
  • PE结构之重定位表(6)
    重定位表什么是重定位表当一个PE文件执行时会加载多个PE文件,而每个被加载的PE文件都有它在内存展开后的起始地址,这个地址是由此PE文件的imagebase(基址)所决定的若前......
  • java.lang.ExceptionInInitializerError Caused by: org.apache.ibatis.exceptions.Pe
      在build中配置resources,来防止我们的资源导出失败的问题,去pom.xml配置,防止资源导出问题<!--在build中配置resources,来防止我们的资源导出失败的问题--><build>......
  • typescript tsc : 无法加载文件 E:\nodejs\node_global\tsc.ps1
    1.管理员身份运行vscode2.在终端执行:get-ExecutionPolicy,显示Restricted3.在终端执行:set-ExecutionPolicyRemoteSigned4.在终端执行:get-ExecutionPolicy,显示RemoteSig......
  • Dubbo/Zookeeper笔记
    分布式基础:Doubbo/Zookeeper分布式理论一、什么是分布式系统?分布式系统是若干个独立计算机的集合,这些计算机对于用户来说就像单个相关系统分布式系统是一组通过......
  • [Oracle] LeetCode 1326 Minimum Number of Taps to Open to Water a Garden
    Thereisaone-dimensionalgardenonthex-axis.Thegardenstartsatthepoint0andendsatthepointn.(i.eThelengthofthegardenisn).Therearen+1......
  • OpenGL 波浪特效
    目录一.OpenGL波浪特效效果演示1.IOS演示效果2.WindowsOpenGLES演示效果3.WindowsOpenGL演示效果二.OpenGL波浪特效源码下载1.IOSObject-C版本2.Win......
  • Object.defineProperty方法详解(全面)
     Object.defineProperty方法详解(全面) 一、Object.defineProperty的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性二、Object.defineproperty方......
  • ERROR--The dependencies of some of the beans in the application context form a c
    Thedependenciesofsomeofthebeansintheapplicationcontextformacycle场景:启动SpringBoot项目时报循环依赖错误:Thedependenciesofsomeofthebeansin......