关于内存加载DLL后修复重定位的问题

看网上的代码好累,摸索整理了一下,顺便巩固下PE

首先介绍下PE头,分为2个部分

1、DOS头 (IMAGE_DOS_HEADER)

[cpp]  view plain  copy
 print ?
  1. typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header  
  2.     WORD   e_magic;                     // ASCII字符MZ    
  3.     WORD   e_cblp;                      // 文件最后页的字节数  
  4.     WORD   e_cp;                        // 文件页数  
  5.     WORD   e_crlc;                      // 重定位元素个数  
  6.     WORD   e_cparhdr;                   // 以段落为单位的头部大小  
  7.     WORD   e_minalloc;                  // 所需的最小附加段  
  8.     WORD   e_maxalloc;                  // 所需的最大附加段  
  9.     WORD   e_ss;                        // 初始的堆栈段(SS)相对偏移量值  
  10.     WORD   e_sp;                        // 初始的堆栈指针(SP)值  
  11.     WORD   e_csum;                      // 校验和  
  12.     WORD   e_ip;                        // 初始的指令指针(IP)值  
  13.     WORD   e_cs;                        // 初始的代码段(CS)相对偏移量值  
  14.     WORD   e_lfarlc;                    // 重定位表在文件中的偏移地址  
  15.     WORD   e_ovno;                      // 覆盖号  
  16.     WORD   e_res[4];                    // 保留字(一般都是为确保对齐而预留)  
  17.     WORD   e_oemid;                     // OEM 标识符(相对于 e_oeminfo)   
  18.     WORD   e_oeminfo;                   // OEM 信息,即 e_oemid 的细节  
  19.     WORD   e_res2[10];                  // 保留字(一般都是为确保对齐而预留)   
  20.     LONG   e_lfanew;                    // NT头在文件中的偏移地址  
  21.   } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;  


2、NT头 (IMAGE_NT_HEADERS)

NT头还区分为32位和64位,64位就不做介绍了,重点说下32位

[cpp]  view plain  copy
 print ?
  1. typedef struct _IMAGE_NT_HEADERS {  
  2.     DWORD Signature;                         //签名(文件类型标志)  
  3.     IMAGE_FILE_HEADER FileHeader;            //PE 文件头结构(占用20个字节)  
  4.     IMAGE_OPTIONAL_HEADER32 OptionalHeader;  //可选头结构(占用224个字节)  
  5. } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;  

- Signature的值为:

[cpp]  view plain  copy
 print ?
  1. #define IMAGE_DOS_SIGNATURE                 0x5A4D      // MZ  
  2. #define IMAGE_OS2_SIGNATURE                 0x454E      // NE  
  3. #define IMAGE_OS2_SIGNATURE_LE              0x454C      // LE  
  4. #define IMAGE_VXD_SIGNATURE                 0x454C      // LE  
  5. #define IMAGE_NT_SIGNATURE                  0x00004550 // PE00  
-FileHeader的结构

[cpp]  view plain  copy
 print ?
  1. typedef struct _IMAGE_FILE_HEADER {  
  2.     WORD    Machine;                   //该文件运行所要求的CPU  
  3.     WORD    NumberOfSections;          //文件的节数目  
  4.     DWORD   TimeDateStamp;             //文件创建日期和时间  
  5.     DWORD   PointerToSymbolTable;      //COFF 符号表格的偏移位置  
  6.     DWORD   NumberOfSymbols;           //COFF 符号表格中的符号个数  
  7.     WORD    SizeOfOptionalHeader;      //Optional Header(IMAGE_OPTIONAL_HEADER)结构大小  
  8.     WORD    Characteristics;           //0x0001 文件中没有重定位(relocation)、0x0002 文件是一个可执行程序exe(也就是說不是OBJ 或LIB)、0x2000 文件是dll  
  9. } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;  
-Optional Header 包含了PE文件的逻辑分布信息

[cpp]  view plain  copy
 print ?
  1. typedef struct _IMAGE_OPTIONAL_HEADER {  
  2.     WORD    Magic;                               //用来定义 image 的状态:0x0107 一个 ROM image,0x010B 一个正常的(一般的)EXE  
  3.     BYTE    MajorLinkerVersion;                  //产生此PE文件的链接器的版本  
  4.     BYTE    MinorLinkerVersion;                  //产生此PE文件的链接器的版本  
  5.     DWORD   SizeOfCode;                          //所有code section 的总和大小  
  6.     DWORD   SizeOfInitializedData;               //所有包含初始化内容的 sections(但不包括 code section)的总和大小  
  7.     DWORD   SizeOfUninitializedData;             //所有需要PE装载器将内存地址空间赋予它但是却不占用硬盘空间的所有 sections 的大小总和  
  8.     DWORD   AddressOfEntryPoint;                 //这是PE文件开始执行的位置  
  9.     DWORD   BaseOfCode;                          //一个RVA,表示程序中的 code section 从何开始  
  10.     DWORD   BaseOfData;                          //一个RVA,表示程序中的 data section 从何开始  
  11.   
  12.     DWORD   ImageBase;                           //PE文件的优先装载地址(Base Address)  
  13.     DWORD   SectionAlignment;                    //内存中节对齐的粒度  
  14.     DWORD   FileAlignment;                       //文件中节对齐的粒度  
  15.     WORD    MajorOperatingSystemVersion;         //使用此可执行程序的操作系统的最小版本  
  16.     WORD    MinorOperatingSystemVersion;         //使用此可执行程序的操作系统的最小版本  
  17.     WORD    MajorImageVersion;                   //WIN32子系统版本  
  18.     WORD    MinorImageVersion;                   //WIN32子系统版本  
  19.     WORD    MajorSubsystemVersion;               //使用者自定义的域,允许你拥有不同版本的exe或dll  
  20.     WORD    MinorSubsystemVersion;               //使用者自定义的域,允许你拥有不同版本的exe或dll  
  21.     DWORD   Win32VersionValue;                   //似乎总是0  
  22.     DWORD   SizeOfImage;                         //内存中整个PE映像体的尺寸  
  23.     DWORD   SizeOfHeaders;                       //所有头 + 节表的大小,也就等于文件尺寸减去文件中所有节的尺寸  
  24.     DWORD   CheckSum;                            //此程序的一个CRC 校验和  
  25.     WORD    Subsystem;                           //用来识别PE文件属于哪个子系统  
  26.     WORD    DllCharacteristics;                  //一组标志位,用来指出dll的初始化函数(例如 DllMain)在什么环境下被调用  
  27.     DWORD   SizeOfStackReserve;                  //线程初始堆栈的保留大小  
  28.     DWORD   SizeOfStackCommit;                   //一开始就被指定给执行线程初始堆栈的内存数量  
  29.     DWORD   SizeOfHeapReserve;                   //保留给最初的进程堆(process heap)的虚拟内存数量  
  30.     DWORD   SizeOfHeapCommit;                    //一开始就被指定给进程堆(process heap)的内存数量  
  31.     DWORD   LoaderFlags;                         //Debug用  
  32.     DWORD   NumberOfRvaAndSizes;                 //在DataDirectory(下一个域)数组的成员结构个数  
  33.     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];  //一个IMAGE_DATA_DIRECTORY 结构数组。每个结构给出一个重要数据结构的RVA。  
  34. } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;  

IMAGE_DATA_DIRECTORY的结构如下:

[cpp]  view plain  copy
 print ?
  1. typedef struct _IMAGE_DATA_DIRECTORY {  
  2.     DWORD   VirtualAddress;                         //虚拟地址  
  3.     DWORD   Size;                                   //长度  
  4. } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;  

注释:

RAV 代表相对虚拟地址。RVA是虚拟空间中到参考点的一段距离。RVA就是类似文件偏移量的东西。当然它是相对虚拟空间里的一个地址,而不是文件头部


重定位表在DataDirectory数组的第5个表内(IMAGE_DIRECTORY_ENTRY_BASERELOC)

[cpp]  view plain  copy
 print ?
  1. typedef struct _IMAGE_DATA_DIRECTORY {  
  2.     DWORD   VirtualAddress;                         //表起始偏移值  
  3.     DWORD   Size;                                   //表大小  
  4. } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;  

即:模块基地址  + 表起始偏移值 = 重定位表数据

重定位表有好几块,每个块的头8个字节为2个DWORD,存放偏移值 和 当前块的大小,因此可以计算出当前块中需要修正的偏移(Word类型)的数量。

即:(当前块的大小 - 8) / 2 = 修正数量

修正偏移 = 原值  & 0xFFF 

最后修正直接 将当前载入的基地址  + 修正偏移 就得到的修正数据的地址,直接将地址内的数据加上修正值。

修正值  = 当前载入的基地址 - imagebase

接下来直接给出代码,内存中加载DLL,并且修复重定位(RAV)

[cpp]  view plain  copy
 print ?
  1. byte *MemModule = Null;   //用于存放加载的模块  
  2. //参数为模块路径,加载成功返回内存地址,失败返回-1  
  3. int LoadModuleInMem(char *ModuleName)  
  4. {  
  5.     int iRet = -1;  
  6.     HANDLE hFile = CreateFile(ModuleName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);  //读取文件  
  7.     if (hFile != INVALID_HANDLE_VALUE)  
  8.     {  
  9.         DWORD dwFileSilze= GetFileSize(hFile,NULL);   //获取个文件长度  
  10.         MemModule = (BYTE*)malloc(dwFileSize);        //申请内存  
  11.         if (ReadFile(hFile, OrgCode, dwFileSize, &dwRead, NULL))  
  12.         {  
  13.             IMAGE_DOS_HEADER* DosHeader = (IMAGE_DOS_HEADER*)MemModule;  
  14.             IMAGE_NT_HEADERS* PEHeader = (IMAGE_NT_HEADERS*)((DWORD)DosHeader + DosHeader->e_lfanew);  
  15.             PIMAGE_DATA_DIRECTORY        pRelocDir = NULL;  
  16.             PIMAGE_BASE_RELOCATION       pBaseReloc = NULL;  
  17.             UINT                         nRelocSize = NULL;  
  18.             DWORD ImageBase = PEHeader->OptionalHeader.ImageBase;  //相对文件代码段偏移          
  19.             pRelocDir = &PEHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; //获取重定位表结构  
  20.             //判断是否存在重定位  
  21.             if (pRelocDir->VirtualAddress != 0)  
  22.             {  
  23.                 int Delta = 0;  
  24.                 if ((DWORD)OrgCode != ImageBase)  
  25.                 {  
  26.                    //修正值  
  27.                    Delta =(DWORD)MemModule - ImageBase ;  
  28.                 }  
  29.                 //获取重定位表数据地址  
  30.                 DWORD pRelocBase = pRelocDir->VirtualAddress+(DWORD)hSrcModule;  
  31.   
  32.                 //取出重定位表的第一块 偏移 和 长度  
  33.                 pBaseReloc =(PIMAGE_BASE_RELOCATION)(pRelocBase);  
  34.   
  35.                 //判断是否存在偏移  
  36.                 if (Delta != 0)  
  37.                 {  
  38.                     //修正重定位  
  39.                     while (pBaseReloc->VirtualAddress + pBaseReloc->SizeOfBlock != 0)   
  40.                     {  
  41.                        //0x1000 块内的 重定位偏移数量  
  42.                        int NumberOfReloc = (pBaseReloc->SizeOfBlock - 8 ) / 2;  
  43.                        forint i=0 ; i < NumberOfReloc; i++)  
  44.                        {  
  45.                           WORD pLocData = *(WORD*)(pRelocBase + 8 + i * 2) & 0xFFF;  
  46.                           //内存中代码的地址 = 内存基地址 + 重定位RVA偏移 + 偏移;  
  47.                           //获取源值  
  48.                           WORD OldCode = *(WORD*)(MemModule + pBaseReloc->VirtualAddress + pLocData);  
  49.                           //将修正值写回  
  50.                           *(WORD*)(MemModule + pBaseReloc->VirtualAddress + pLocData) = OldCode + Delta;  
  51.                        }  
  52.                        //移动指针,指向下个块  
  53.                        pRelocBase = pRelocBase + pBaseReloc->SizeOfBlock;  
  54.                        //获取下个块的结构  
  55.                        pBaseReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseReloc +pBaseReloc->SizeOfBlock);          
  56.                     }  
  57.                     iRet = MemModule;  
  58.                 }  
  59.             }  
  60.         }  
  61.     }  
  62.     return iRet;  
  63.  }  
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值