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

打印 上一主题 下一主题

主题 911|帖子 911|积分 2733

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

  • 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 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 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;}[/code]上述代码运行后即可获取到内存地址0x401000对应的文件地址为0x1000,读者可自行打开WinHex验证是否相等,如下图所示;

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

所谓的相对地址则是内存地址减去基址所获得的地址,该地址的计算同样可以使用代码实现,如下RVA_To_FOA函数可用于将一个相对地址转换为文件偏移,如果内存VA地址是0x401000而基址是0x400000那么相对地址就是0x1000,将相对地址转换为FOA文件偏移,首相要将相对地址加上基址,我们通过相对地址减去PointerToRawData数据指针即可获取到文件偏移。
  1. #include <iostream>
  2. #include <Windows.h>
  3. #include <ImageHlp.h>
  4. #pragma comment(lib,"Imagehlp.lib")
  5. // 读取NT头
  6. PIMAGE_NT_HEADERS GetNtHeader(PVOID ImageBase)
  7. {
  8.   PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
  9.   if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
  10.   {
  11.     return NULL;
  12.   }
  13.   PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)ImageBase + pDosHeader->e_lfanew);
  14.   if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
  15.   {
  16.     return NULL;
  17.   }
  18.   return pNtHeaders;
  19. }
  20. // 读取PE结构的封装
  21. HANDLE OpenPeFile(LPTSTR FileName)
  22. {
  23.   HANDLE hFile, hMapFile, lpMapAddress = NULL;
  24.   DWORD dwFileSize = 0;
  25.   // CreateFile 既可以创建文件,也可以打开文件,这里则是打开文件的含义
  26.   hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  27.   if (hFile == INVALID_HANDLE_VALUE)
  28.   {
  29.     return 0;
  30.   }
  31.   // 获取到文件大小
  32.   dwFileSize = GetFileSize(hFile, NULL);
  33.   // 创建文件的内存映像
  34.   hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
  35.   if (hMapFile == NULL)
  36.   {
  37.     return 0;
  38.   }
  39.   // 读取映射中的内存并返回一个句柄
  40.   lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
  41.   if (lpMapAddress != NULL)
  42.   {
  43.     return lpMapAddress;
  44.   }
  45.   return 0;
  46. }
  47. // 将 VA(虚拟地址) --> 转换为 FOA(文件偏移)
  48. DWORD VA_To_FOA(HANDLE ImageBase, DWORD dwVA)
  49. {
  50.   PIMAGE_NT_HEADERS pNtHead = NULL;
  51.   PIMAGE_FILE_HEADER pFileHead = NULL;
  52.   PIMAGE_SECTION_HEADER pSection = NULL;
  53.   DWORD NumberOfSectinsCount = 0;
  54.   DWORD dwImageBase = 0;
  55.   pNtHead = GetNtHeader(ImageBase);
  56.   pSection = IMAGE_FIRST_SECTION(pNtHead);
  57.   dwImageBase = pNtHead->OptionalHeader.ImageBase;
  58.   NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
  59.   for (int each = 0; each < NumberOfSectinsCount; each++)
  60.   {
  61.     // 获取节的开始地址与结束地址
  62.     DWORD Section_Start = dwImageBase + pSection[each].VirtualAddress;
  63.     DWORD Section_Ends = dwImageBase + pSection[each].VirtualAddress + pSection[each].Misc.VirtualSize;
  64.     // 判断当前的VA地址落在了那个节上
  65.     if (dwVA >= Section_Start && dwVA <= Section_Ends)
  66.     {
  67.       DWORD RVA = dwVA - pNtHead->OptionalHeader.ImageBase;                                    // 计算RVA
  68.       DWORD FOA = pSection[each].PointerToRawData + (RVA - pSection[each].VirtualAddress);     // 计算FOA
  69.       return FOA;
  70.     }
  71.   }
  72.   return -1;
  73. }
  74. int main(int argc, char * argv[])
  75. {
  76.   HANDLE lpMapAddress = NULL;
  77.   // 打开PE文件
  78.   lpMapAddress = OpenPeFile(L"d://lyshark.exe");
  79.   // 转换
  80.   DWORD FOA = VA_To_FOA(lpMapAddress, 0x401000);
  81.   printf("VA --> FOA 结果为: %x \n", FOA);
  82.   system("pause");
  83.   return 0;
  84. }
复制代码
我们还是以上述功能为例,计算相对地址0x1000的文件偏移,则可以得到0x1000的文件偏移值,如下图所示;

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

将文件内的偏移地址FOA转换为内存虚拟地址,在转换时首先通过VirtualAddress节虚拟地址加上,文件偏移地址减去PointerToRawData数据域指针,得到相对地址,再次加上ImageBase基地址即可获取到实际虚拟地址。
  1. #include <iostream>
  2. #include <Windows.h>
  3. #include <ImageHlp.h>
  4. #pragma comment(lib,"Imagehlp.lib")
  5. // 读取NT头
  6. PIMAGE_NT_HEADERS GetNtHeader(PVOID ImageBase)
  7. {
  8.   PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
  9.   if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
  10.   {
  11.     return NULL;
  12.   }
  13.   PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)ImageBase + pDosHeader->e_lfanew);
  14.   if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
  15.   {
  16.     return NULL;
  17.   }
  18.   return pNtHeaders;
  19. }
  20. // 读取PE结构的封装
  21. HANDLE OpenPeFile(LPTSTR FileName)
  22. {
  23.   HANDLE hFile, hMapFile, lpMapAddress = NULL;
  24.   DWORD dwFileSize = 0;
  25.   // CreateFile 既可以创建文件,也可以打开文件,这里则是打开文件的含义
  26.   hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  27.   if (hFile == INVALID_HANDLE_VALUE)
  28.   {
  29.     return 0;
  30.   }
  31.   // 获取到文件大小
  32.   dwFileSize = GetFileSize(hFile, NULL);
  33.   // 创建文件的内存映像
  34.   hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
  35.   if (hMapFile == NULL)
  36.   {
  37.     return 0;
  38.   }
  39.   // 读取映射中的内存并返回一个句柄
  40.   lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
  41.   if (lpMapAddress != NULL)
  42.   {
  43.     return lpMapAddress;
  44.   }
  45.   return 0;
  46. }
  47. // 将 RVA(虚拟地址) --> 转换为 FOA(文件偏移)
  48. DWORD RVA_To_FOA(HANDLE ImageBase, DWORD dwRVA)
  49. {
  50.   PIMAGE_NT_HEADERS pNtHead = NULL;
  51.   PIMAGE_FILE_HEADER pFileHead = NULL;
  52.   PIMAGE_SECTION_HEADER pSection = NULL;
  53.   DWORD NumberOfSectinsCount = 0;
  54.   DWORD dwImageBase = 0;
  55.   pNtHead = GetNtHeader(ImageBase);
  56.   pSection = IMAGE_FIRST_SECTION(pNtHead);
  57.   dwImageBase = pNtHead->OptionalHeader.ImageBase;
  58.   NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
  59.   for (int each = 0; each < NumberOfSectinsCount; each++)
  60.   {
  61.     DWORD Section_Start = pSection[each].VirtualAddress;                                  // 计算RVA开始位置
  62.     DWORD Section_Ends = pSection[each].VirtualAddress + pSection[each].Misc.VirtualSize; // 计算RVA结束位置
  63.     if (dwRVA >= Section_Start && dwRVA <= Section_Ends)
  64.     {
  65.       DWORD VA = pNtHead->OptionalHeader.ImageBase + dwRVA;                                  // 得到VA地址
  66.       DWORD FOA = pSection[each].PointerToRawData + (dwRVA - pSection[each].VirtualAddress); // 得到FOA
  67.       return FOA;
  68.     }
  69.   }
  70.   return -1;
  71. }
  72. int main(int argc, char * argv[])
  73. {
  74.   // 打开文件
  75.   HANDLE lpMapAddress = NULL;
  76.   lpMapAddress = OpenPeFile(L"d://lyshark.exe");
  77.   // 计算地址
  78.   DWORD FOA = RVA_To_FOA(lpMapAddress, 0x1000);
  79.   printf("RVA --> FOA 结果为: %x \n", FOA);
  80.   system("pause");
  81.   return 0;
  82. }
复制代码
运行后即可将文件偏移0x1000转换为内存虚拟地址0x401000如下图所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/ccb722fb.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

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

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

愛在花開的季節

金牌会员
这个人很懒什么都没写!

标签云

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