2.14 PE结构:地址之间的转换

这篇具有很好参考价值的文章主要介绍了2.14 PE结构:地址之间的转换。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在可执行文件PE文件结构中,通常我们需要用到地址转换相关知识,PE文件针对地址的规范有三种,其中就包括了VARVAFOA三种,这三种该地址之间的灵活转换也是非常有用的,本节将介绍这些地址范围如何通过编程的方式实现转换。

如下是三种格式的异同点:

  • VA(Virtual Address,虚拟地址):它是在进程的虚拟地址空间中的地址,用于在运行时访问内存中的数据和代码。VA是相对于进程基址的偏移量。在不同的进程中,相同的VA可能映射到不同的物理地址。
  • RVA(Relative Virtual Address,相对虚拟地址):它是相对于模块基址(Module Base Address)的偏移量,用于定位模块内部的数据和代码。RVA是相对于模块基址的偏移量,通过将模块基址和RVA相加,可以计算出相应的VA。
  • FOA(File Offset Address,文件偏移地址):它是相对于文件起始位置的偏移量,用于定位可执行文件中的数据和代码在文件中的位置。通过将文件偏移地址和节表中的指定节的起始位置相加,可以计算出相应的FOA。

VA虚拟地址转换为FOA文件偏移

VA地址代指的是程序加载到内存后的内存地址,而FOA地址则代表文件内的物理地址,通过编写VA_To_FOA则可实现将一个虚拟地址转换为文件偏移地址,该函数的实现方式,首先得到ImageBase镜像基地址,并得到NumberOfSections节数量,有了该数量以后直接循环,通过判断语句将节限定在一个区间内该区间dwVA >= Section_Start && dwVA <= Section_Ends,当找到后,首先通过VA-ImageBase得到当前的RVA地址,接着通过该地址减去VirtualAddress并加上PointerToRawData文件指针,即可获取到文件内的偏移。

#include <iostream>
#include <Windows.h>
#include <ImageHlp.h>

#pragma comment(lib,"Imagehlp.lib")

// 读取NT头
PIMAGE_NT_HEADERS GetNtHeader(PVOID ImageBase)
{
  PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;

  if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
  {
    return NULL;
  }

  PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)ImageBase + pDosHeader->e_lfanew);
  if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
  {
    return NULL;
  }

  return pNtHeaders;
}

// 读取PE结构的封装
HANDLE OpenPeFile(LPTSTR FileName)
{
  HANDLE hFile, hMapFile, lpMapAddress = NULL;
  DWORD dwFileSize = 0;

  // CreateFile 既可以创建文件,也可以打开文件,这里则是打开文件的含义
  hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    return 0;
  }

  // 获取到文件大小
  dwFileSize = GetFileSize(hFile, NULL);

  // 创建文件的内存映像
  hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
  if (hMapFile == NULL)
  {
    return 0;
  }

  // 读取映射中的内存并返回一个句柄
  lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
  if (lpMapAddress != NULL)
  {
    return lpMapAddress;
  }

  return 0;
}

// 将 VA(虚拟地址) --> 转换为 FOA(文件偏移)
DWORD VA_To_FOA(HANDLE ImageBase, DWORD dwVA)
{
  PIMAGE_NT_HEADERS pNtHead = NULL;
  PIMAGE_FILE_HEADER pFileHead = NULL;
  PIMAGE_SECTION_HEADER pSection = NULL;
  DWORD NumberOfSectinsCount = 0;
  DWORD dwImageBase = 0;

  pNtHead = GetNtHeader(ImageBase);
  pSection = IMAGE_FIRST_SECTION(pNtHead);

  dwImageBase = pNtHead->OptionalHeader.ImageBase;
  NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
  for (int each = 0; each < NumberOfSectinsCount; each++)
  {
    // 获取节的开始地址与结束地址
    DWORD Section_Start = dwImageBase + pSection[each].VirtualAddress;
    DWORD Section_Ends = dwImageBase + pSection[each].VirtualAddress + pSection[each].Misc.VirtualSize;
    // 判断当前的VA地址落在了那个节上
    if (dwVA >= Section_Start && dwVA <= Section_Ends)
    {
      DWORD RVA = dwVA - pNtHead->OptionalHeader.ImageBase;                                    // 计算RVA
      DWORD FOA = pSection[each].PointerToRawData + (RVA - pSection[each].VirtualAddress);     // 计算FOA
      return FOA;
    }
  }
  return -1;
}

int main(int argc, char * argv[])
{
  HANDLE lpMapAddress = NULL;

  // 打开PE文件
  lpMapAddress = OpenPeFile(L"d://lyshark.exe");

  // 转换
  DWORD FOA = VA_To_FOA(lpMapAddress, 0x401000);
  printf("VA --> FOA 结果为: %x \n", FOA);

  system("pause");
  return 0;
}

上述代码运行后即可获取到内存地址0x401000对应的文件地址为0x1000,读者可自行打开WinHex验证是否相等,如下图所示;

2.14 PE结构:地址之间的转换

RVA相对地址转换为FOA文件偏移

所谓的相对地址则是内存地址减去基址所获得的地址,该地址的计算同样可以使用代码实现,如下RVA_To_FOA函数可用于将一个相对地址转换为文件偏移,如果内存VA地址是0x401000而基址是0x400000那么相对地址就是0x1000,将相对地址转换为FOA文件偏移,首相要将相对地址加上基址,我们通过相对地址减去PointerToRawData数据指针即可获取到文件偏移。

#include <iostream>
#include <Windows.h>
#include <ImageHlp.h>

#pragma comment(lib,"Imagehlp.lib")

// 读取NT头
PIMAGE_NT_HEADERS GetNtHeader(PVOID ImageBase)
{
  PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;

  if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
  {
    return NULL;
  }

  PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)ImageBase + pDosHeader->e_lfanew);
  if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
  {
    return NULL;
  }

  return pNtHeaders;
}

// 读取PE结构的封装
HANDLE OpenPeFile(LPTSTR FileName)
{
  HANDLE hFile, hMapFile, lpMapAddress = NULL;
  DWORD dwFileSize = 0;

  // CreateFile 既可以创建文件,也可以打开文件,这里则是打开文件的含义
  hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    return 0;
  }

  // 获取到文件大小
  dwFileSize = GetFileSize(hFile, NULL);

  // 创建文件的内存映像
  hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
  if (hMapFile == NULL)
  {
    return 0;
  }

  // 读取映射中的内存并返回一个句柄
  lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
  if (lpMapAddress != NULL)
  {
    return lpMapAddress;
  }

  return 0;
}

// 将 RVA(虚拟地址) --> 转换为 FOA(文件偏移)
DWORD RVA_To_FOA(HANDLE ImageBase, DWORD dwRVA)
{
  PIMAGE_NT_HEADERS pNtHead = NULL;
  PIMAGE_FILE_HEADER pFileHead = NULL;
  PIMAGE_SECTION_HEADER pSection = NULL;
  DWORD NumberOfSectinsCount = 0;
  DWORD dwImageBase = 0;

  pNtHead = GetNtHeader(ImageBase);
  pSection = IMAGE_FIRST_SECTION(pNtHead);

  dwImageBase = pNtHead->OptionalHeader.ImageBase;
  NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
  for (int each = 0; each < NumberOfSectinsCount; each++)
  {
    DWORD Section_Start = pSection[each].VirtualAddress;                                  // 计算RVA开始位置
    DWORD Section_Ends = pSection[each].VirtualAddress + pSection[each].Misc.VirtualSize; // 计算RVA结束位置

    if (dwRVA >= Section_Start && dwRVA <= Section_Ends)
    {
      DWORD VA = pNtHead->OptionalHeader.ImageBase + dwRVA;                                  // 得到VA地址
      DWORD FOA = pSection[each].PointerToRawData + (dwRVA - pSection[each].VirtualAddress); // 得到FOA
      return FOA;
    }
  }
  return -1;
}

int main(int argc, char * argv[])
{
  // 打开文件
  HANDLE lpMapAddress = NULL;
  lpMapAddress = OpenPeFile(L"d://lyshark.exe");

  // 计算地址
  DWORD FOA = RVA_To_FOA(lpMapAddress, 0x1000);
  printf("RVA --> FOA 结果为: %x \n", FOA);

  system("pause");
  return 0;
}

我们还是以上述功能为例,计算相对地址0x1000的文件偏移,则可以得到0x1000的文件偏移值,如下图所示;

2.14 PE结构:地址之间的转换

FOA文件偏移转换为VA虚拟地址

将文件内的偏移地址FOA转换为内存虚拟地址,在转换时首先通过VirtualAddress节虚拟地址加上,文件偏移地址减去PointerToRawData数据域指针,得到相对地址,再次加上ImageBase基地址即可获取到实际虚拟地址。

#include <iostream>
#include <Windows.h>
#include <ImageHlp.h>

#pragma comment(lib,"Imagehlp.lib")

// 读取NT头
PIMAGE_NT_HEADERS GetNtHeader(PVOID ImageBase)
{
  PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;

  if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
  {
    return NULL;
  }

  PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)ImageBase + pDosHeader->e_lfanew);
  if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
  {
    return NULL;
  }

  return pNtHeaders;
}

// 读取PE结构的封装
HANDLE OpenPeFile(LPTSTR FileName)
{
  HANDLE hFile, hMapFile, lpMapAddress = NULL;
  DWORD dwFileSize = 0;

  // CreateFile 既可以创建文件,也可以打开文件,这里则是打开文件的含义
  hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    return 0;
  }

  // 获取到文件大小
  dwFileSize = GetFileSize(hFile, NULL);

  // 创建文件的内存映像
  hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
  if (hMapFile == NULL)
  {
    return 0;
  }

  // 读取映射中的内存并返回一个句柄
  lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
  if (lpMapAddress != NULL)
  {
    return lpMapAddress;
  }

  return 0;
}

// 将 FOA(文件偏移) --> 转换为 VA(虚拟地址)
DWORD FOA_To_VA(HANDLE ImageBase, DWORD dwFOA)
{
  PIMAGE_NT_HEADERS pNtHead = NULL;
  PIMAGE_FILE_HEADER pFileHead = NULL;
  PIMAGE_SECTION_HEADER pSection = NULL;
  DWORD NumberOfSectinsCount = 0;
  DWORD dwImageBase = 0;

  pNtHead = GetNtHeader(ImageBase);
  pSection = IMAGE_FIRST_SECTION(pNtHead);

  dwImageBase = pNtHead->OptionalHeader.ImageBase;
  NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
  for (int each = 0; each < NumberOfSectinsCount; each++)
  {
    DWORD PointerRawStart = pSection[each].PointerToRawData;                                // 文件偏移开始位置
    DWORD PointerRawEnds = pSection[each].PointerToRawData + pSection[each].SizeOfRawData;  // 文件偏移结束位置

    if (dwFOA >= PointerRawStart && dwFOA <= PointerRawEnds)
    {
      DWORD RVA = pSection[each].VirtualAddress + (dwFOA - pSection[each].PointerToRawData);  // 计算出RVA
      DWORD VA = RVA + pNtHead->OptionalHeader.ImageBase;                                     // 计算出VA
      return VA;
    }
  }
  return -1;
}

int main(int argc, char * argv[])
{
  // 打开文件
  HANDLE lpMapAddress = NULL;
  lpMapAddress = OpenPeFile(L"d://lyshark.exe");

  // 转换
  DWORD VA = FOA_To_VA(lpMapAddress, 0x1000);
  printf("FOA --> VA 结果为: 0x%X \n", VA);

  system("pause");
  return 0;
}

运行后即可将文件偏移0x1000转换为内存虚拟地址0x401000如下图所示;

2.14 PE结构:地址之间的转换文章来源地址https://www.toymoban.com/news/detail-707970.html

到了这里,关于2.14 PE结构:地址之间的转换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • PE解释器之PE文件结构(二)

    接下来的内容是对IMAGE_OPTIONAL_HEADER32中的最后一个成员DataDirectory,虽然他只是一个结构体数组,每个结构体的大小也不过是个字节,但是它却是PE文件中最重要的成员。PE装载器通过查看它才能准确的找到某个函数或某个资源。 一:IMAGE_DATA_DIRECTORY——数据目录结构 此数据目

    2024年01月20日
    浏览(41)
  • 2.14日学习打卡----初学Zookeeper(一)

    单机架构 一个系统业务量很小的时候所有的代码都放在一个项目中就好了,然后这个项目部署在一台服务器上,整个项目所有的服务都由这台服务器提供。 缺点 : 服务性能存在瓶颈 不可伸缩性 代码量庞大,系统臃肿,牵一发动全身 单点故障问题 集群架构 单机处理到达瓶

    2024年02月22日
    浏览(36)
  • PE 文件结构图

    最近在进行免杀的学习,在《黑客免杀攻防》这本书中找到了非常好的关于PE文件的描述,虽然书比较古老的,但是里面的内容是非常精细和优秀的。它的附页中有非常清晰的PE文件结构图,可是翻看比较麻烦,撕下来又可惜,于是我今天对着附页的图用excel重新画了一个。这

    2024年02月10日
    浏览(34)
  • 使用element 2.14 实现表格虚拟滚动组件

    下述代码为组件实现代码复制即可食用,默认只展示 一屏数据加两条 全选存在些许问题, 使用row-key时,如果行过多滚动时会不会很流畅 特别需要注意的是 行高必须要保持一致 下面为调用示例  

    2024年02月05日
    浏览(29)
  • 2.2 PE结构:文件头详细解析

    PE结构是 Windows 系统下最常用的可执行文件格式,理解PE文件格式不仅可以理解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,DOS头是PE文件开头的一个固定长度的结构体,这个结构体的大小为64字节(0x40)。DOS头包含了很多有用的信息,该信

    2024年02月10日
    浏览(23)
  • Mir 2.14 正式发布,Ubuntu 使用的 Linux 显示服务器

    Canonical 公司最近发布了 Mir 2.14,这是该项目的最新版本。 Mir 2.14 在 Wayland 方面通过 ext-session-lock-v1 协议增加了对屏幕锁定器 (screen lockers) 的支持,并最终支持 Wayland 拖放。此外还整合了渲染平台的实现,放弃了之前在 Raspberry Pi 设备上使用的 DispmanX 平台。 Mir 2.14 还增加了对

    2024年02月14日
    浏览(24)
  • 【TCP/IP】IP地址与域名之间的转换 - gethostbyname 和 gethostbyaddr函数

    目录 域名系统 DNS服务器 IP地址和域名之间的转换 通过域名获取IP地址 通过IP地址获取域名          域名系统(英文:Domain Name System,缩写:DNS)是互联网的一项服务 。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。          所

    2024年02月08日
    浏览(31)
  • go struct结构体之间的转换

    原文链接:https://www.zhoubotong.site/post/94.html 说下背景吧,大家在开发中可能在不同的目录(package)下定义了相同的struct(属性参数完全一样如名字、个数和类型),在方法调用传参数的时候,可能是用到了其中某一个struct的引用。 那么这里就牵扯到相互间的转换:直接上demo: 显然

    2023年04月17日
    浏览(37)
  • 【Go 基础篇】Go语言结构体之间的转换与映射

    在Go语言中,结构体是一种强大的数据类型,用于定义和组织不同类型的数据字段。当我们处理复杂的数据逻辑时,常常需要在不同的结构体之间进行转换和映射,以便实现数据的转移和处理。本文将深入探讨Go语言中结构体之间的转换和映射技巧,包括类型转换、自定义转换

    2024年02月10日
    浏览(30)
  • 无缝数据转换!使用C++ 实现 Excel文件与CSV之间的相互转换

    CSV格式是一种通用的文本文件格式,可在多个应用程序之间共享和使用。相比之下,Excel文件是一种电子表格格式,通常只能在Microsoft Excel中编辑和查看。因此,将Excel文件转换为CSV格式可使数据更方便地在其他应用程序中使用;而将CSV文件转换为Excel格式则有利于在Microsoft

    2024年02月11日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包