2.10 PE结构:重建重定位表结构

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

Relocation(重定位)是一种将程序中的一些地址修正为运行时可用的实际地址的机制。在程序编译过程中,由于程序中使用了各种全局变量和函数,这些变量和函数的地址还没有确定,因此它们的地址只能暂时使用一个相对地址。当程序被加载到内存中运行时,这些相对地址需要被修正为实际的绝对地址,这个过程就是重定位。

在Windows操作系统中,程序被加载到内存中运行时,需要将程序中的各种内存地址进行重定位,以使程序能够正确地运行。Windows系统使用PE(Portable Executable)文件格式来存储可执行程序,其中包括重定位信息。当程序被加载到内存中时,系统会解析这些重定位信息,并将程序中的各种内存地址进行重定位。

重定位表一般出现在DLL中,因为DLL都是动态加载,所以地址不固定,DLL的入口点在整个执行过程中至少要执行2次,一次是在开始时执行初始化工作,一次则是在结束时做最后的收尾工作,重定位表则是解决DLL的地址问题,为了能找到重定位表首先我们需要使用PeView工具查询DataDirectory数据目录表,在其中找到Base relocation字段,里面的0x00001800则是重定位表基地址;

2.10 PE结构:重建重定位表结构

我们通过使用WinHex工具定位到0x00001800即可看到重定位表信息,如下图中的1000代表的是重定位RVA地址,绿色的0104代表的则是重定位块的长度,后面则是每两个字节代表一个重定位块,0A是重定位地址,30则是重定位的类型,以此顺序向下排列。

2.10 PE结构:重建重定位表结构

重定位表也是分页排列的,每一页大小都是1000字节,通过使用FixRelocPage命令即可查询到当前程序中的重定位块信息,并以第一个为例,查询一下起始地址RVA为1000的页上,有哪些重定位结构,如下图所示;

2.10 PE结构:重建重定位表结构

其中的重定位RVA地址0000100A是用标黄色的1000加上标蓝色的0xA得到的。而修正RVA地址00003000加上模块基地址63FF0000+3000得到的则是第一个被修正的内存地址,读者可使用x64dbg跳转到该程序内自行确认。

重定位表的修复原理与IAT修复完全一致,我们需要分别读入脱壳前与脱壳后的两个程序,接着通过循环正确的重定位表信息,并依次覆盖到脱壳后的程序内,以此实现对重定位表的修复功能,实现代码如下所示;

#include <windows.h>
#include <stdio.h>

struct TypeOffset
{
  WORD Offset : 12;       // 低12位代表重定位地址
  WORD Type : 4;          // 高4位代表重定位类型
};

DWORD FileSize = 0;  // 定义文件大小
DWORD FileBase = 0;  // 保存文件的基地址

// 定义全局变量,来存储DOS,NT,Section头
PIMAGE_DOS_HEADER DosHeader = nullptr;
PIMAGE_NT_HEADERS NtHeader = nullptr;
PIMAGE_FILE_HEADER FileHead = nullptr;

// 将RVA转换为FOA的函数
DWORD RVAtoFOA(DWORD rva)
{
  auto SectionTables = IMAGE_FIRST_SECTION(NtHeader);    // 获取区段表
  WORD Count = NtHeader->FileHeader.NumberOfSections;    // 获取区段数量

  for (int i = 0; i < Count; ++i)
  {
    // 判断是否存在于区段中
    DWORD Section_Start = SectionTables[i].VirtualAddress;
    DWORD Section_Ends = SectionTables[i].VirtualAddress + SectionTables[i].SizeOfRawData;
    if (rva >= Section_Start && rva < Section_Ends)
    {
      // 找到之后计算位置并返回值
      return rva - SectionTables[i].VirtualAddress + SectionTables[i].PointerToRawData;
    }
  }
  return -1;
}

// 打开PE文件
bool OpenPeFile(LPCSTR FileName)
{
  // 打开文件
  HANDLE Handle = CreateFileA(FileName, GENERIC_READ, NULL,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (Handle == INVALID_HANDLE_VALUE)
    return false;

  // 获取文件大小
  FileSize = GetFileSize(Handle, NULL);

  // 读取文件数据
  DWORD OperSize = 0;
  FileBase = (DWORD)new BYTE[FileSize];
  ReadFile(Handle, (LPVOID)FileBase, FileSize, &OperSize, NULL);

  // 获取DOS头并判断是不是一个有效的DOS文件
  DosHeader = (PIMAGE_DOS_HEADER)FileBase;
  if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    return false;

  // 获取 NT 头并判断是不是一个有效的PE文件
  NtHeader = (PIMAGE_NT_HEADERS)(FileBase + DosHeader->e_lfanew);
  if (NtHeader->Signature != IMAGE_NT_SIGNATURE)
    return false;

  // 判断是不是一个32位文件
  if (NtHeader->OptionalHeader.Magic != 0x010B)
    return false;
  
  CloseHandle(Handle);
  return true;
}

// 修复重定位表
void RepairFixReloc(char new_file[])
{
  DWORD base = NtHeader->OptionalHeader.ImageBase;
  // 1. 获取重定位表的 rva
  DWORD RelocRVA = NtHeader->OptionalHeader.DataDirectory[5].VirtualAddress;
  // 2. 获取重定位表
  auto Reloc = (PIMAGE_BASE_RELOCATION)(FileBase + RVAtoFOA(RelocRVA));

  // 3. 遍历重定位表中的重定位块,以0结尾
  while (Reloc->SizeOfBlock != 0)
  {
    // 3.1 输出分页基址
    printf("[↓] 分页基址: 0x%08X \n\n", Reloc->VirtualAddress);
    // 3.2 找到重定位项
    auto Offset = (TypeOffset*)(Reloc + 1);

    // 3.3 计算重定位项的个数
    // Reloc->SizeOfBlock 保存的是整个重定位块的大小 结构体 + 重定位项数组
    // Reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) 得到单个数组大小
    // 上面的结果 \ 2 = 重定位项的个数,原因是重定位项的大小为两个字节
    DWORD Size = (Reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;

    // 3.4 遍历所有的重定位项
    for (DWORD i = 0; i < Size; ++i)
    {
      DWORD Type = Offset[i].Type;                  // 获取重定位类型,只关心为3的类型
      DWORD pianyi = Offset[i].Offset;              // 获取重定位的偏移值
      DWORD rva = pianyi + Reloc->VirtualAddress;   // 获取要重定位的地址所在的RVA
      DWORD foa = RVAtoFOA(rva);                    // 获取要重定位的地址所在的FOA
      DWORD fa = foa + FileBase;                    // 获取要重定位的地址所在的fa
      DWORD addr = *(DWORD*)fa;                     // 获取要重定位的地址
      DWORD new_addr = addr - base + 0x1500000;     // 计算重定位后的数据: addr - oldbase + newbase

      // 将重定位后的数据写回缓冲区(文件)
      if (Offset[i].Type == 3)
        *(DWORD*)fa = new_addr;

      printf("\t [->] 重定位RVA: 0x%08X | 重定位FOA: 0x%08X | 重定位地址: 0x%08X | 修正地址: 0x%08X \n", rva, foa, addr, new_addr);
    }
    // 找到下一个重定位块
    Reloc = (PIMAGE_BASE_RELOCATION)((DWORD)Reloc + Reloc->SizeOfBlock);
  }
  // 保存修正后的文件
  NtHeader->OptionalHeader.ImageBase = 0x1500000;

  // 打开一个新文件
  HANDLE new_handle = CreateFileA(new_file, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  if (new_handle == INVALID_HANDLE_VALUE)
    return;

  DWORD OperSize = 0;
  // 保存修正好的程序
  BOOL ret = WriteFile(new_handle, (LPVOID)FileBase, FileSize, &OperSize, NULL);
  if (ret == TRUE)
  {
    printf("\n\n");
    CloseHandle(new_handle);

    printf("[*] 修复 %s 文件 \t 写入基址: %08X \t 总长度: %d \t 写入长度: %d \n", new_file, FileBase, FileSize, OperSize);
  }
}

void Banner()
{
  printf(" ____        _ _     _   ____      _            \n");
  printf("| __ ) _   _(_) | __| | |  _ \\ ___| | ___   ___ \n");
  printf("|  _ \\| | | | | |/ _` | | |_) / _ \\ |/ _ \\ / __|\n");
  printf("| |_) | |_| | | | (_| | |  _ <  __/ | (_) | (__ \n");
  printf("|____/ \\__,_|_|_|\\__,_| |_| \\_\\___|_|\\___/ \\___|\n");
  printf("                                                \n");
  printf("Reloc 重定位表快速修复工具 \t By: LyShark \n");
  printf("Usage: BuildFix [原文件位置] [修复后文件位置] \n\n\n");
}

int main(int argc, char* argv[])
{
  Banner();
  if (argc == 3)
  {
    bool flag = OpenPeFile(argv[1]);
    if (true == flag)
    {
      RepairFixReloc(argv[2]);
    }
  }
  return 0;
}

运行上述程序,读者可自行传入脱壳前的程序与脱壳后的程序,此时则会实现自动替换,如下图所示;

2.10 PE结构:重建重定位表结构文章来源地址https://www.toymoban.com/news/detail-702338.html

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

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

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

相关文章

  • ZStack Cloud4.2.10部署手册

    1、环境准备 1.1 准备软件工具 • 系统镜像 ZStack-Cloud-x86_64-DVD-4.2.10-c76.iso • Zstack安装包 ZStack-installer-4.2.10.bin • http://www.zstack.io/product_downloads/ 请准备以上的软件,并通过 MD5 校验工具核对校验码,以确保软件完整无损。 1.2 核对硬件设备 • 以单节点服务器作为部署案例,对

    2023年04月15日
    浏览(22)
  • Android 基础知识4-2.10 GridLayout(网格布局)详解

    一、GridLayout(网格布局)概述         GridLayout 布局是 Android 4.0 以后引入的新布局,和 TableLayout(表格布局) 有点类似,不过它功能更多,也更加好用,最大的特点是放置的组件自动占据网格的整个区域,每个组件的 大小相同 , 不能改变组件大小 ,只能改变组件之间的水平

    2024年02月16日
    浏览(27)
  • OceanPen Art AI绘画系统 运营教程(三)2.10绘画全新界面升级

    在一个崇高的目标支持下,不停地工作,即使慢,也一定会获得成功。 —— 爱因斯坦 演示站点 :     ai.oceanpen.art 官方论坛 :     www.jingyuai.com       提示词自取: https://ai.oceanpen.art/   艺术字体验:https://ai.oceanpen.art/ oceanpen Art绘画源码限量版V我获取 OceanPen Art AI艺术系

    2024年03月28日
    浏览(35)
  • PE解释器之PE文件结构(二)

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

    2024年01月20日
    浏览(42)
  • 2.12 PE结构:实现PE字节注入

    本章笔者将介绍一种通过Metasploit生成ShellCode并将其注入到特定PE文件内的Shell注入技术。该技术能够劫持原始PE文件的入口地址,在PE程序运行之前执行ShellCode反弹,执行后挂入后台并继续运行原始程序,实现了一种隐蔽的Shell访问。而我把这种技术叫做字节注入反弹。 字节注

    2024年02月09日
    浏览(25)
  • 微PE安装Win10、Win11

    准备一个U盘、一台需要换系统的电脑,按照如下步骤即可安装系统 1、下载微PE工具箱 2、安装PE到U盘 3、安装完成 1、ITellYou下载镜像 按需下载Win10与Win11的镜像 consumer_editions 版本(零售版)包含:Home(家庭版); Education(教育版) ; Professional(专业版); business_editions 版本(VL版)包

    2024年02月06日
    浏览(47)
  • Win10PE纯净版制作过程

    首先在win10系统中查询自己的系统是个什么版本, 打开“运行”输入“winver\\\" 查看windows版本, 去下载对应版本的windows ADK, 下载地址: https://docs.microsoft.com/zh-cn/windows-hardware/get-started/adk-install 同时下载对应的windows PE加载项, 安装到系统中,安装windows ADK时, 可以选择只安装

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

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

    2024年02月10日
    浏览(34)
  • 【免杀前置课——PE文件结构】十八、数据目录表及其内容详解——数据目录表(导出表、导入表、IAT表、TLS表)详解;如何在程序在被调试之前反击?TLS反调试(附代码)

    数据目录表:可选PE头最后一个成员,就是数据目录.一共有16个 分别是:导出表、导入表、资源表、异常信息表、安全证书表、重定位表、调试信息表、版权所以表、全局指针表 TLS表、加载配置表、绑定导入表、IAT表、延迟导入表、COM信息表 最后一个保留未使用,默认为0。

    2024年01月15日
    浏览(27)
  • 2.2 PE结构:文件头详细解析

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

    2024年02月10日
    浏览(24)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包