关于内存加载DLL后修复重定位的问题
2013-05-27 00:03
309 查看
看网上的代码好累,摸索整理了一下,顺便巩固下PE
首先介绍下PE头,分为2个部分
1、DOS头 (IMAGE_DOS_HEADER)
2、NT头 (IMAGE_NT_HEADERS)
NT头还区分为32位和64位,64位就不做介绍了,重点说下32位
- Signature的值为:
IMAGE_DATA_DIRECTORY的结构如下:
注释:
RAV 代表相对虚拟地址。RVA是虚拟空间中到参考点的一段距离。RVA就是类似文件偏移量的东西。当然它是相对虚拟空间里的一个地址,而不是文件头部
重定位表在DataDirectory数组的第5个表内(IMAGE_DIRECTORY_ENTRY_BASERELOC)
即:模块基地址 + 表起始偏移值 = 重定位表数据
重定位表有好几块,每个块的头8个字节为2个DWORD,存放偏移值 和 当前块的大小,因此可以计算出当前块中需要修正的偏移(Word类型)的数量。
即:(当前块的大小 - 8) / 2 = 修正数量
修正偏移 = 原值 & 0xFFF
最后修正直接 将当前载入的基地址 + 修正偏移 就得到的修正数据的地址,直接将地址内的数据加上修正值。
修正值 = 当前载入的基地址 - imagebase
接下来直接给出代码,内存中加载DLL,并且修复重定位(RAV)
首先介绍下PE头,分为2个部分
1、DOS头 (IMAGE_DOS_HEADER)
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // ASCII字符MZ WORD e_cblp; // 文件最后页的字节数 WORD e_cp; // 文件页数 WORD e_crlc; // 重定位元素个数 WORD e_cparhdr; // 以段落为单位的头部大小 WORD e_minalloc; // 所需的最小附加段 WORD e_maxalloc; // 所需的最大附加段 WORD e_ss; // 初始的堆栈段(SS)相对偏移量值 WORD e_sp; // 初始的堆栈指针(SP)值 WORD e_csum; // 校验和 WORD e_ip; // 初始的指令指针(IP)值 WORD e_cs; // 初始的代码段(CS)相对偏移量值 WORD e_lfarlc; // 重定位表在文件中的偏移地址 WORD e_ovno; // 覆盖号 WORD e_res[4]; // 保留字(一般都是为确保对齐而预留) WORD e_oemid; // OEM 标识符(相对于 e_oeminfo) WORD e_oeminfo; // OEM 信息,即 e_oemid 的细节 WORD e_res2[10]; // 保留字(一般都是为确保对齐而预留) LONG e_lfanew; // NT头在文件中的偏移地址 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
2、NT头 (IMAGE_NT_HEADERS)
NT头还区分为32位和64位,64位就不做介绍了,重点说下32位
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; //签名(文件类型标志) IMAGE_FILE_HEADER FileHeader; //PE 文件头结构(占用20个字节) IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选头结构(占用224个字节) } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
- Signature的值为:
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ #define IMAGE_OS2_SIGNATURE 0x454E // NE #define IMAGE_OS2_SIGNATURE_LE 0x454C // LE #define IMAGE_VXD_SIGNATURE 0x454C // LE #define IMAGE_NT_SIGNATURE 0x00004550 // PE00-FileHeader的结构
typedef struct _IMAGE_FILE_HEADER { WORD Machine; //该文件运行所要求的CPU WORD NumberOfSections; //文件的节数目 DWORD TimeDateStamp; //文件创建日期和时间 DWORD PointerToSymbolTable; //COFF 符号表格的偏移位置 DWORD NumberOfSymbols; //COFF 符号表格中的符号个数 WORD SizeOfOptionalHeader; //Optional Header(IMAGE_OPTIONAL_HEADER)结构大小 WORD Characteristics; //0x0001 文件中没有重定位(relocation)、0x0002 文件是一个可执行程序exe(也就是說不是OBJ 或LIB)、0x2000 文件是dll } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;-Optional Header 包含了PE文件的逻辑分布信息
typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; //用来定义 image 的状态:0x0107 一个 ROM image,0x010B 一个正常的(一般的)EXE BYTE MajorLinkerVersion; //产生此PE文件的链接器的版本 BYTE MinorLinkerVersion; //产生此PE文件的链接器的版本 DWORD SizeOfCode; //所有code section 的总和大小 DWORD SizeOfInitializedData; //所有包含初始化内容的 sections(但不包括 code section)的总和大小 DWORD SizeOfUninitializedData; //所有需要PE装载器将内存地址空间赋予它但是却不占用硬盘空间的所有 sections 的大小总和 DWORD AddressOfEntryPoint; //这是PE文件开始执行的位置 DWORD BaseOfCode; //一个RVA,表示程序中的 code section 从何开始 DWORD BaseOfData; //一个RVA,表示程序中的 data section 从何开始 DWORD ImageBase; //PE文件的优先装载地址(Base Address) DWORD SectionAlignment; //内存中节对齐的粒度 DWORD FileAlignment; //文件中节对齐的粒度 WORD MajorOperatingSystemVersion; //使用此可执行程序的操作系统的最小版本 WORD MinorOperatingSystemVersion; //使用此可执行程序的操作系统的最小版本 WORD MajorImageVersion; //WIN32子系统版本 WORD MinorImageVersion; //WIN32子系统版本 WORD MajorSubsystemVersion; //使用者自定义的域,允许你拥有不同版本的exe或dll WORD MinorSubsystemVersion; //使用者自定义的域,允许你拥有不同版本的exe或dll DWORD Win32VersionValue; //似乎总是0 DWORD SizeOfImage; //内存中整个PE映像体的尺寸 DWORD SizeOfHeaders; //所有头 + 节表的大小,也就等于文件尺寸减去文件中所有节的尺寸 DWORD CheckSum; //此程序的一个CRC 校验和 WORD Subsystem; //用来识别PE文件属于哪个子系统 WORD DllCharacteristics; //一组标志位,用来指出dll的初始化函数(例如 DllMain)在什么环境下被调用 DWORD SizeOfStackReserve; //线程初始堆栈的保留大小 DWORD SizeOfStackCommit; //一开始就被指定给执行线程初始堆栈的内存数量 DWORD SizeOfHeapReserve; //保留给最初的进程堆(process heap)的虚拟内存数量 DWORD SizeOfHeapCommit; //一开始就被指定给进程堆(process heap)的内存数量 DWORD LoaderFlags; //Debug用 DWORD NumberOfRvaAndSizes; //在DataDirectory(下一个域)数组的成员结构个数 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //一个IMAGE_DATA_DIRECTORY 结构数组。每个结构给出一个重要数据结构的RVA。 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
IMAGE_DATA_DIRECTORY的结构如下:
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; //虚拟地址 DWORD Size; //长度 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
注释:
RAV 代表相对虚拟地址。RVA是虚拟空间中到参考点的一段距离。RVA就是类似文件偏移量的东西。当然它是相对虚拟空间里的一个地址,而不是文件头部
重定位表在DataDirectory数组的第5个表内(IMAGE_DIRECTORY_ENTRY_BASERELOC)
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; //表起始偏移值 DWORD Size; //表大小 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
即:模块基地址 + 表起始偏移值 = 重定位表数据
重定位表有好几块,每个块的头8个字节为2个DWORD,存放偏移值 和 当前块的大小,因此可以计算出当前块中需要修正的偏移(Word类型)的数量。
即:(当前块的大小 - 8) / 2 = 修正数量
修正偏移 = 原值 & 0xFFF
最后修正直接 将当前载入的基地址 + 修正偏移 就得到的修正数据的地址,直接将地址内的数据加上修正值。
修正值 = 当前载入的基地址 - imagebase
接下来直接给出代码,内存中加载DLL,并且修复重定位(RAV)
byte *MemModule = Null; //用于存放加载的模块 //参数为模块路径,加载成功返回内存地址,失败返回-1 int LoadModuleInMem(char *ModuleName) { int iRet = -1; HANDLE hFile = CreateFile(ModuleName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); //读取文件 if (hFile != INVALID_HANDLE_VALUE) { DWORD dwFileSilze= GetFileSize(hFile,NULL); //获取个文件长度 MemModule = (BYTE*)malloc(dwFileSize); //申请内存 if (ReadFile(hFile, OrgCode, dwFileSize, &dwRead, NULL)) { IMAGE_DOS_HEADER* DosHeader = (IMAGE_DOS_HEADER*)MemModule; IMAGE_NT_HEADERS* PEHeader = (IMAGE_NT_HEADERS*)((DWORD)DosHeader + DosHeader->e_lfanew); PIMAGE_DATA_DIRECTORY pRelocDir = NULL; PIMAGE_BASE_RELOCATION pBaseReloc = NULL; UINT nRelocSize = NULL; DWORD ImageBase = PEHeader->OptionalHeader.ImageBase; //相对文件代码段偏移 pRelocDir = &PEHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; //获取重定位表结构 //判断是否存在重定位 if (pRelocDir->VirtualAddress != 0) { int Delta = 0; if ((DWORD)OrgCode != ImageBase) { //修正值 Delta =(DWORD)MemModule - ImageBase ; } //获取重定位表数据地址 DWORD pRelocBase = pRelocDir->VirtualAddress+(DWORD)hSrcModule; //取出重定位表的第一块 偏移 和 长度 pBaseReloc =(PIMAGE_BASE_RELOCATION)(pRelocBase); //判断是否存在偏移 if (Delta != 0) { //修正重定位 while (pBaseReloc->VirtualAddress + pBaseReloc->SizeOfBlock != 0) { //0x1000 块内的 重定位偏移数量 int NumberOfReloc = (pBaseReloc->SizeOfBlock - 8 ) / 2; for( int i=0 ; i < NumberOfReloc; i++) { WORD pLocData = *(WORD*)(pRelocBase + 8 + i * 2) & 0xFFF; //内存中代码的地址 = 内存基地址 + 重定位RVA偏移 + 偏移; //获取源值 WORD OldCode = *(WORD*)(MemModule + pBaseReloc->VirtualAddress + pLocData); //将修正值写回 *(WORD*)(MemModule + pBaseReloc->VirtualAddress + pLocData) = OldCode + Delta; } //移动指针,指向下个块 pRelocBase = pRelocBase + pBaseReloc->SizeOfBlock; //获取下个块的结构 pBaseReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseReloc +pBaseReloc->SizeOfBlock); } iRet = MemModule; } } } } return iRet; }
相关文章推荐
- 关于内存加载DLL后修复重定位的问题
- 关于java加载dll文件一直报Unable to load library的问题
- 关于Windows平台下应用程序加载DLL模块的问题.
- 关于无法加载DLL"***.dll":找不到指定的模块(异常来自HRESULT:0x8007007E)问题的解决办法
- 关于修复VS2008提示加载安装组件出现问题和点击VS的设计窗口出现一个WINDOW installer的提示的问题的解决办法
- 关于DLL路径加载顺序的问题
- 还是关于无法加载DLL(OCI.DLL)问题解决办法!
- 关于在dll中申请内存,外部释放的问题
- 关于DLL多次加载、卸载后。dll中创建窗口崩溃问题的解决。
- 关于DLL路径加载顺序的问题
- 关于“无法定位程序输入点getaddrinfo于动态链接库WS32_32.dll上”的问题
- 关于“无法定位程序输入点getaddrinfo于动态链接库WS32_32.dll上”的问题
- 关于VS2010 rcxdti.dll is unable to load rcxdtiui.dll加载失败问题的解决方案
- Unity3D关于内存优化,内存泄露、多线程、引用C++的dll的问题研究
- 关于动态加载dll问题
- 关于各种DLL缺失修复问题
- 关于DLL加载的问题
- 动态内存中加载DLL的问题
- 关于在dll中申请内存,外部释放的问题
- 关于ViewPager加载多个导致OOM内存溢出的问题。