移动重定位表

1.移动重定位表
目的:将测试用的DllHello.dll的重定位表移动到一个新增的节中,如果dll还能正常使用说明移动成功;
 
主要步骤:
    1】新增一个节;
        新增一个节表    ->判断是否有足够的空间存放新节表,条件是最后一个节表后面到SizeOfHeaders之间是否有80个字节;
        新增一个节        ->紧跟在最后一个节的后面,注意文件对齐;
        设置新节表属性    ->主要有Misc.VirtualSize、VirtualAddress、SizeOfRawData、PointerToRawData、Characteristics
        修正pe头和可选pe头    ->pe头中的节数NumberOfSections+1;
                                            ->可选pe头的内存镜像大小SizeOfImage+最后一个节的内存对齐量;
    2】移动重定位表到新节;
    3】修正数据目录;
 
代码:
#include "stdafx.h"
#include "PeTool.h"
#include "string.h"
 
#define SRC "C:\\Users\\Administrator\\Desktop\\DllHello.dll"
#define DEST "C:\\Users\\Administrator\\Desktop\\DllHello_new.dll"
 
//新增一个节
DWORD addSec(LPVOID pFileBuffer, LPVOID* pSec){
    //1.定义pe头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_SECTION_HEADER seHeader = NULL;    //节表指针
 
    //2.初始化头结构指针
    dosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    if(dosHeader->e_magic != IMAGE_DOS_SIGNATURE){
        printf("不是有效MZ标记\n");
        return 0;
    }
    if(*((PDWORD)((DWORD)pFileBuffer + dosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){
        printf("不是有效PE标记\n");
        free(pFileBuffer);
        return 0;
    }
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    seHeader = (PIMAGE_SECTION_HEADER) ((DWORD)opHeader + peHeader->SizeOfOptionalHeader);
 
    //3.新增一个节表
    PIMAGE_SECTION_HEADER newSec = seHeader + peHeader->NumberOfSections;    //新节表的指针
    if(((DWORD)pFileBuffer + opHeader->SizeOfHeaders - (DWORD)newSec) < 80){
        printf("空间不足插入新的节表\n");
        return 0;
    }
 
    //4.设置新节表
    strcpy((char*)newSec->Name, ".nReloc");
    newSec->Misc.VirtualSize = 0x2000;                    //新节的内存镜像大小为2000
    newSec->VirtualAddress = opHeader->SizeOfImage;        //新节的内存偏移为内存镜像大小
    newSec->SizeOfRawData = 0x2000;                        //新节的文件镜像大小为2000
    PIMAGE_SECTION_HEADER lastSec = seHeader + (peHeader->NumberOfSections -1);    //最后一个节表
    newSec->PointerToRawData = lastSec->PointerToRawData + lastSec->SizeOfRawData;    //新节的文件偏移紧接最后一个节
    newSec->Characteristics = seHeader->Characteristics;    //新节的属性和第一个节一样即可
    
    //5.设置全0节表
    memset((LPVOID)(newSec+1), 0, 40);
 
    //6.修头信息
    peHeader->NumberOfSections = peHeader->NumberOfSections + 1;
    opHeader->SizeOfImage = opHeader->SizeOfImage + 0x2000;
 
    //7.申请内存
    LPVOID sec = malloc(0x2000);
    if(!sec){
        printf("给新节申请内存失败\n");
        return 0;
    }
    memset(sec, 0, 0x2000);
 
    //8.返回
    *pSec = sec;
    return 0x2000;
 
}
 
//移动重定位表
void moveReloc(){
    //定义pe头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_DATA_DIRECTORY dataDir = NULL;        //数据目录指针
    PIMAGE_BASE_RELOCATION relocDir= NULL;                //重定位表指针
 
    //1.将文件读入内存
    LPVOID pFileBuffer = NULL;
    DWORD fileSize = ReadPEFile(SRC, &pFileBuffer);
    if(!pFileBuffer){
        printf("读取dll文件失败\n");
        return;
    }
 
    //2.新增一个节
    LPVOID newSec = NULL;
    DWORD secSize = addSec(pFileBuffer, &newSec);
    if(!newSec){
        printf("新增节失败\n");
        return;
    }
 
    //3.初始化头结构指针
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    dataDir = opHeader ->DataDirectory;
    relocDir = (PIMAGE_BASE_RELOCATION) ((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, (dataDir + 5)->VirtualAddress ));
 
    //4.复制地址表
    LPVOID copyDest = newSec;
    memcpy(copyDest, relocDir, (dataDir + 5)->Size);    //直接把原来的重定位表拷过去即可,不用做RVA修正,因为重定位表中指向的需要修改的绝对地址没变
    
    //5.修复数据目录
    dataDir[6].VirtualAddress = FoaToRva(pFileBuffer, fileSize);
 
    //6.写出新文件
    FILE* newFile = fopen(DEST, "a+b");
    if(!newFile){
        printf("打开新文件失败\n");
        free(pFileBuffer);
        free(newSec);
        return;
    }
    size_t m = fwrite(pFileBuffer, fileSize, 1, newFile);
    if(!m){
        printf("写出文件第一部分失败\n");
        fclose(newFile);
        free(pFileBuffer);
        free(newSec);
        return;
    }
    //写出新节
    size_t n = fwrite(newSec, secSize, 1, newFile);
    if(!n){
        printf("写出文件第二部分失败\n");
        fclose(newFile);
        free(pFileBuffer);
        free(newSec);
        return;
    }
 
    //关闭文件并返回
    fclose(newFile);
    free(pFileBuffer);
    free(newSec);
    printf("移动导出表成功\n");
    return;
}
 
//测试新dll
void testNewDll(){
    //使用新的dll
    typedef int (__stdcall *lpPlus)(int,int);
    lpPlus myPlus;
    HINSTANCE   hModule = LoadLibrary(DEST);
    myPlus = (lpPlus)GetProcAddress(hModule,   "_Plus@8");
    int a =0;
    a=myPlus(10,2);
    printf("10+2=%d\n",a);
    printf("导出函数可以使用,说明移动导出表成功\n");
}
 
int main(int argc, char* argv[])
{
    //移动导出表
    moveReloc();
    
    //测试是否移动成功
    testNewDll();
 
    getchar();
}
 
2.修正重定位表
什么时候需要修正重定位表:
    可能有一种情况:当dll的ImageBase变动时,此时重定位表中的具体项需要随之变动,否则当需要从重定位表中找需要修正的绝对地址时找到的是错误的地址;
    
预期效果:
    将测试用的DllHello.dll中的可选pe头中的ImageBase+10000,然后修复重定位表,如果修改后的dll依然可以使用则说明成功;
    
 
思路:
    ImageBase+1000,编译到dll中的绝对地址的值也需要+1000才是正确的地址;
    也就是说重定位表中的每一个RVA都要+1000;
    重定位表中的RVA = 每一个块中的重定位表结构的VirtualAddress + 具体项地址;
    也就是说,将每一个块中的VirtualAddress+1000即可;
 
代码:
#include "stdafx.h"
#include "PeTool.h"
#include "string.h"
 
#define SRC "C:\\Users\\Administrator\\Desktop\\DllHello.dll"
#define DEST "C:\\Users\\Administrator\\Desktop\\DllHello_new.dll"
 
//移动重定位表
void updateReloc(){
    //定义pe头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_DATA_DIRECTORY dataDir = NULL;        //数据目录指针
    PIMAGE_BASE_RELOCATION relocDir= NULL;                //重定位表指针
 
    //1.将文件读入内存
    LPVOID pFileBuffer = NULL;
    DWORD fileSize = ReadPEFile(SRC, &pFileBuffer);
    if(!pFileBuffer){
        printf("读取dll文件失败\n");
        return;
    }
 
    //2.初始化头结构指针
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    dataDir = opHeader ->DataDirectory;
    relocDir = (PIMAGE_BASE_RELOCATION) ((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, (dataDir + 5)->VirtualAddress ));
 
    //3.修改ImageBase
    opHeader ->ImageBase = opHeader->ImageBase + 0x1000;
 
    //4.修复重定位表
    while(relocDir->SizeOfBlock){
        relocDir->VirtualAddress = relocDir->VirtualAddress + 0x1000;
        relocDir = (PIMAGE_BASE_RELOCATION)((DWORD)relocDir + relocDir->SizeOfBlock);
    }
 
    //5.写出新文件
    MemeryTOFile(pFileBuffer, fileSize, DEST);
    return;
}
 
//测试新dll
void testNewDll(){
    //使用新的dll
    typedef int (__stdcall *lpPlus)(int,int);
    lpPlus myPlus;
    HINSTANCE   hModule = LoadLibrary(DEST);
    myPlus = (lpPlus)GetProcAddress(hModule,   "_Plus@8");
    int a =0;
    a=myPlus(10,2);
    printf("10+2=%d\n",a);
    printf("导出函数可以使用,说明移动导出表成功\n");
}
 
int main(int argc, char* argv[])
{
    //修正导出表
    updateReloc();
    
    //测试是否移动成功
    testNewDll();
 
 
    getchar();
}

 

结果:
    失败了,新的dll无法运行;
    按道理来说,将每个块的VirtualAddress+1000之后对应的每个RVA也会加1000,为什么不行;
    在网上找不到解决方案,可能是理解还不够到位吧,先放着以后解决;
 
 另一种思路:
  通过重定位表找到绝对地址,然后直接修改绝对地址
void testUseReloc(LPSTR lpszFile)
{
    LPVOID pFileBuffer = NULL;
    pFileBuffer= ReadPEFile(lpszFile);
    if(!pFileBuffer)
    {
        printf("文件读取失败\n");
        return;
    }

    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;
    PIMAGE_DATA_DIRECTORY DataDirectory=NULL;
    PIMAGE_SECTION_HEADER pSectionHeader_LAST = NULL;

    //Header信息
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
    pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+4);
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);
    pSectionHeader_LAST = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader+(pPEHeader->NumberOfSections-1)*40);
    //定位Directory_Data;
    DataDirectory = pOptionHeader->DataDirectory;
    
    //重定位表

    printf("IMAGE_DIRECTORY_ENTRY_BASERELOC: Address: %x ,Size: %x \n",DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress,
        DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);

    //DWORD RVAToFileOffset(LPVOID pFileBuffer,DWORD dwRva)
    DWORD FoA = RVAToFileOffset(pFileBuffer,0x2df10);
    
    DWORD BaseReloc_Directory_Address = DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
    DWORD BaseReloc_Directory_Size = DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
    FoA = RVAToFileOffset(pFileBuffer,BaseReloc_Directory_Address);
    

    //定位到第一个重定位块
    PIMAGE_BASE_RELOCATION pRelocData = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + FoA);
    
    //输出所有的标信息
    while(pRelocData->VirtualAddress||pRelocData->SizeOfBlock)
    {
        DWORD RelocVirtualAddress = pRelocData->VirtualAddress;
        DWORD RelocSize = pRelocData->SizeOfBlock;

        printf("VirtualSize: %x ,Size: %x , Number: %x  \n",RelocVirtualAddress,RelocSize,(RelocSize-8)/2);
        
        int k = (RelocSize-8)/2;
        PWORD pMyRelocAddress = NULL;
        pMyRelocAddress = (PWORD)((DWORD)pRelocData+8);
        
        for(int i=0;i<k;i++)
        {
            printf("第%x个 : 标志 : %x 偏移 : %x\n",i+1,pMyRelocAddress[i]&0xF000,RelocVirtualAddress+(pMyRelocAddress[i]&0x0FFF));
            //依次进行修改
            DWORD changeRVA = RelocVirtualAddress+(pMyRelocAddress[i]&0x0FFF);
            DWORD changeFoa = RVAToFileOffset(pFileBuffer,changeRVA);

            printf("changeRVA:%x   changeFoa: %x \n",changeRVA,changeFoa);

            if((pMyRelocAddress[i]&0xF000) == 0x3000)
            {
                //修改数据:
                PDWORD myAddress = (PDWORD)((DWORD)pFileBuffer + changeFoa);
                printf("myAddress: %x\n",*myAddress);
                *myAddress = *myAddress - 0x10000000 + 0x20000000;
                printf("change :myAddress: %x\n",*myAddress);
            }
        }
        pRelocData = (PIMAGE_BASE_RELOCATION)((DWORD)pRelocData + RelocSize);
    }

    //写出dll
    //确定大小
    LPVOID pFileBuffer_Start = pFileBuffer;
    DWORD FileSize = pSectionHeader_LAST->PointerToRawData + pSectionHeader_LAST->SizeOfRawData;    
    WirteToFile(pFileBuffer,FileSize,"C://changeDll.dll");

}

结果:用这种方式试过了,还是没能成功;

  可能ImageBase的修改造成的影响不只是重定位表这么简单吧;

  先放着,以后解决;

  找到了一篇相关的文章:https://bbs.pediy.com/thread-219232.htm

 

3)发现问题  

可能是ImageBase+0x1000的问题;
0x1000太小,可能不符合模块对齐的要求,因此pe文件直接报错;
改为将ImageBase+0x10000000;
 
思路:
    通过重定位表,找到需要修改的绝对地址处的RVA;
    通过RVA找到需要修改的绝对地址;
    将每个绝对地址+0x10000000;
    这样修改后,绝对地址才能定位到正确的地方;
    只修改RVA是不行的,因为修改RVA后将找不需要修改的绝对地址;
 
代码:
#include "stdafx.h"
#include "PeTool.h"
#include "string.h"

#define SRC "C:\\Users\\Administrator\\Desktop\\DllHello.dll"
#define DEST "C:\\Users\\Administrator\\Desktop\\DllHello_new.dll"

//修复重定位表
void updateReloc(){
    //定义pe头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_DATA_DIRECTORY dataDir = NULL;        //数据目录指针
    PIMAGE_BASE_RELOCATION relocDir= NULL;                //重定位表指针

    //1.将文件读入内存
    LPVOID pFileBuffer = NULL;
    DWORD fileSize = ReadPEFile(SRC, &pFileBuffer);
    if(!pFileBuffer){
        printf("读取dll文件失败\n");
        return;
    }

    //2.初始化头结构指针
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    dataDir = opHeader ->DataDirectory;
    relocDir = (PIMAGE_BASE_RELOCATION) ((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, (dataDir + 5)->VirtualAddress ));

    //3.修改ImageBase
    opHeader ->ImageBase = opHeader->ImageBase + 0x10000000;

    //4.修复重定位表
    while(relocDir->SizeOfBlock){
        int i = (relocDir ->SizeOfBlock - 8)/2;
        for(int j=0;j<i;j++){
            PWORD item = (PWORD)((DWORD)relocDir + 8);    //重定位表具体项的指针
            if((item[j] & 0xF000) >> 12 == 3){    //如果具体项的前4位是3说明有意义,如果是0则是为了内存对齐而产生的无意义数据
                DWORD rva = relocDir->VirtualAddress + (item[j] & 0x0FFF);        //需要修改的绝对地址的rva
                DWORD foa = RvaToFileOffset(pFileBuffer, rva);    //转成foa
                PDWORD changeAddr = (PDWORD)((DWORD)pFileBuffer + foa);    //需要修复的绝对地址的文件镜像地址
                //直接修复绝对地址,而不是修改重定位表
                *changeAddr = *changeAddr + 0x10000000;
            }
        }
        relocDir = (PIMAGE_BASE_RELOCATION)((DWORD)relocDir + relocDir->SizeOfBlock);    //下一个数据块
    }
    //5.写出新文件
     MemeryTOFile(pFileBuffer, fileSize , DEST);
    return;
}

//测试新dll
void testNewDll(){
    //使用新的dll
    typedef int (__stdcall *lpPlus)(int,int);
    lpPlus myPlus;
    HINSTANCE   hModule = LoadLibrary(DEST);
    myPlus = (lpPlus)GetProcAddress(hModule,   "_Plus@8");
    int a =0;
    a=myPlus(10,2);
    printf("10+2=%d\n",a);
    printf("导出函数可以使用,说明修复导出表成功\n");
}

int main(int argc, char* argv[])
{
    //修复重定位表
    updateReloc();
    
    //测试是否移动成功
    testNewDll();

    getchar();
}

结果:

 

 

 

posted @ 2019-11-15 08:39  L丶银甲闪闪  阅读(745)  评论(0编辑  收藏  举报