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 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.初始化头结构指针
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[])
{
//移动导出表
moveReloc();
//测试是否移动成功
testNewDll();
getchar();
}
结果:
失败了,新的dll无法运行;
按道理来说,将每个块的VirtualAddress+1000之后对应的每个RVA也会加1000,为什么不行;
在网上找不到解决方案,可能是理解还不够到位吧,先放着以后解决;