您的位置:首页 > 其它

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

2017-09-13 16:08 288 查看
看网上的代码好累,摸索整理了一下,顺便巩固下PE

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

1、DOS头 (IMAGE_DOS_HEADER)

[cpp] view
plain copy

 print?

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位

[cpp] view
plain copy

 print?

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的值为:

[cpp] view
plain copy

 print?

#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的结构

[cpp] view
plain copy

 print?

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文件的逻辑分布信息

[cpp] view
plain copy

 print?

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的结构如下:

[cpp] view
plain copy

 print?

typedef struct _IMAGE_DATA_DIRECTORY {  

    DWORD   VirtualAddress;                         //虚拟地址  

    DWORD   Size;                                   //长度  

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;  

注释:

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

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

[cpp] view
plain copy

 print?

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)

[cpp] view
plain copy

 print?

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;  

 }  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  PE DOS NT