您的位置:首页 > 其它

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

2013-05-27 00:03 309 查看
看网上的代码好累,摸索整理了一下,顺便巩固下PE

首先介绍下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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: