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

news2024/10/16 22:20:36

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

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

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

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

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

其中的重定位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;
}

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/992483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二十、MySQL多表关系

1、概述 在项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求以及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种对应关系 2、多表关系分类 &#xff08;1&#xff0…

2023大数据挑战赛全国六强团队获奖经验+ppt分享(四)

团队名称 陛下请移步至地下室 团队成员 张晓立&#xff08;华东师范大学&#xff09; 团队名次 全国第三名 赛题描述说明介绍 2023大数据挑战赛赛题说明决赛评分标准回顾 参赛分享与收获 大赛提供了非常丰富有趣的数据&#xff0c;能把数据分析明白就能获得高分。首先分析metri…

Delphi、C# 和 TypeScript 之父 Anders Hejlsberg(安德斯·海尔斯伯格,海神)赶潮AI

自2022年11月ChatGPT横空出世&#xff0c;一波由AIGC引发的热潮轰轰烈烈持续传播。 全球各国政府及大型互联网企业都在积极布局人工智能解决方案&#xff0c;抢占人工智能高地&#xff0c;推动人工智能产业健康发展。 Delphi、C# 和 TypeScript 之父&#xff0c;位列Microsof…

口袋参谋:如何规避差评风险?一招让你实现宝贝0差评!

​作为卖家&#xff0c;不会还有人不知道差评的威力吧&#xff01; 如果你宝贝销量大&#xff0c;差评率本身就低&#xff0c;偶尔一个差评可能影响不大。 但如果你是新宝贝上架&#xff0c;销量少的可怜&#xff0c;差评就是重磅炸弹&#xff0c;99.99%让店铺的评分绿得发亮…

git bash 常见场景用法

1.git 客户端 2.git 代码操作&#xff08;下载、上传、删除、查看仓库地址&#xff09; 下载代码&#xff1a; git clone 仓库路径 上传代码&#xff1a; git add git commit -m "注释" git push 删除代码&#xff1a; git rm [-r] 代码&#xff1b;git commit -m…

腾讯云新用户:定义、专属福利及优惠活动

在当今的数字化时代&#xff0c;云计算已成为企业和个人不可或缺的技术服务。腾讯云作为国内领先的云计算服务提供商&#xff0c;为新用户提供了一系列专属福利和优惠活动。本文将详细介绍腾讯云新用户的定义、专属福利和优惠活动&#xff0c;助力大家轻松上云&#xff01; 一、…

基于Java+SpringBoot+Vue前后端分离高校专业实习管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Tomcat多实例 + Tomcat负载均衡、动静分离(Nginx联动)

多实例联动 一、Tomcat 多实例1.1 什么是Tomcat多实例&#xff1f;1.2 配置思路1.3 配置实现1.3.1 安装jdk1.3.2 安装tomcat1.3.3 配置 tomcat 环境变量1.3.4 修改端口号1.3.5 修改各 tomcat 实例中的 startup.sh 和 shutdown.sh 文件&#xff0c;添加 tomcat 环境变量1.3.6 启…

01_前端css编写的三种方式

前言 CSS的引入方式共有三种&#xff1a;行内样式、内部样式表、外部样式表 一、内联式引入 用法&#xff1a; 在元素上直接通过style属性进行设置css样式设置 示例&#xff1a; <h1 style"color:red;">style属性的应用</h1> <p style"font-si…

建站系列(五)--- 前端开发语言之HTML、CSS、JavaScript

目录 相关系列文章前言一、前端开发与后端开发二、前端语言简介&#xff08;一&#xff09;、HTML&#xff08;二&#xff09;、CSS&#xff08;三&#xff09;、JavaScript 三、学习指导&#xff08;一&#xff09;、开发环境&#xff08;二&#xff09;、第一个Hello&#xf…

【实训】“宅急送”订餐管理系统(程序设计综合能力实训)

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 前言 大一小学期&#xff0c;我迎来了人生中的第一次实训…

4.6版本Wordpress漏洞复现

文章目录 一、搭建环境二、漏洞复现1.抓包2.准备payload3.发送payload4.检查是否上传成功5.连接payload 国外的&#xff1a;Wordpress&#xff0c;Drupal&#xff0c;Joomla&#xff0c;这是国外最流行的3大CMS。国内则是DedeCMS和帝国&#xff0c;PHPCMS等。 国内的CMS会追求大…

2023/9/8 -- C++/QT

作业 1> 自行封装一个栈的类&#xff0c;包含私有成员属性&#xff1a;栈的数组、记录栈顶的变量 成员函数完成&#xff1a;构造函数、析构函数、拷贝构造函数、入栈、出栈、清空栈、判空、判满、获取栈顶元素、求栈的大小 02stack.h: #ifndef __02STACK_H__ #define __…

​重生奇迹MU魔法师的装备属性​

魔法师的武器主要武器装备&#xff0c;主要分类为单手武器、双手武器、戒指、项链、盾牌、头盔、铠甲、护手、护腿、鞋子&#xff0c;玩家需要根据情况集齐这些装备。 智力果实以及体力果非常重要&#xff0c;在实战的时候非常实用。实获取途径一般是果实合成、宝藏&#xff0…

说说分布式系统容器化

继上一篇浅谈高并发分布式架构演进路径&#xff0c;单体服务完成分布式架构改造&#xff0c;转型为微服务。随着微服务数量的急剧增加&#xff0c;跨应用、跨系统的调用越来越多&#xff0c;调用关系和依赖关系日益复杂&#xff0c;这种复杂性增加了系统的设计、实施和维护的难…

Java并发基石——CAS是如何实现的?

目录 1. 什么是CAS&#xff1f; 2. Java中关于 CAS 的 API 在哪里&#xff1f; 3. CAS API方法和参数解析 4. CAS的底层实现 5. CAS是如何保证多核线程安全的&#xff1f; 6. CAS的缺点&#xff1f; 7. 如何避免ABA问题&#xff1f; 1. 什么是CAS&#xff1f; CAS的全程是…

动态库静态库对比

程序编译成可执行程序的过程 静态在连接阶段会把代码复制到可执行文件中 动态则不&#xff0c;而是打包一些信息进去&#xff0c;在执行的时候根据信息找到动态库执行 制作过程 静态库 动态库 优缺点 库比较小且更新慢的时候一般使用静态&#xff0c;反之则动态 静态库 …

七种 BeanDefinition,各显其能!

聚沙成塔&#xff01;不知不觉 Spring 源码已经连续更了两个月啦&#xff0c;视频也录制了不少了&#xff0c;对 Spring 源码分析感兴趣的小伙伴戳这里哦Spring源码应该怎么学&#xff1f;&#xff5e; 今天我们继续来看 Spring 源码中一个非常重要的概念&#xff1a;BeanDefi…

万物皆可连的腾讯轻联

文章目录 前言关于腾讯轻联小试牛刀新建流程配置逻辑组件配置连接器流程上线 其它功能核心能力总结 前言 工作中有这样一个需求&#xff0c;每周五下午三点定时给小组内成员发送邮件&#xff0c;邮件内容固定&#xff0c;主要作用是提醒大家记得发周报&#xff1b;其实这个需求…

iOS:解决Could not find a storyboard named ‘LaunchScreen.storyboard‘ in bundle NSBundle

打开项目的&#xff1a;HBuilder-uniPlugin-Info.plist 删除Launch screen interface file base name 然后看图&#xff0c;清空掉之前的LaunchScreen.storyboard东西 再运行就可以了&#xff0c;我也是改自定基座出的问题略