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


    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 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159

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

  • 相关阅读:
    如何使用腾讯云GPU云服务器完成 blender 的动画图片渲染
    万物皆可集成系列:低代码释放用友U8+深度价值(3)— 数据融合应用
    一米facebook功能点
    油封有哪些优点?
    新建esp32的vscode工程的三种方式
    Python Json 处理,列表生成式的项目使用 笔记
    【论文精度】Transformer--Attention Is All You Need
    php实现无限级分类的树形结构
    python 基础语法及保留字
    制作一个谷歌浏览器插件,实现网页数据爬虫
  • 原文地址:https://blog.csdn.net/lyshark_csdn/article/details/132776737