1.主要思路
新建一个测试用的dll,在dll的入口函数中添加一个在加载阶段执行的函数Init();
给目标程序添加一个新节,将导入表移动到新节中;
IAT表不能移动,因为程序在调用dll中的函数时,都是call的IAT表的绝对地址;然后在IAT表中找到真实的函数地址;
如果移动了IAT表,需要修复程序中所有使用了IAT表绝对地址的地方,而这些地方难以确定,加上数量多,代价太大;
在新节中添加一个新的导入表,引用测试用的dll;
必须至少导入一个dll中的函数,也就是IAT和INT表中至少插入一项,否则系统会忽略该dll;
踩坑:
新节表的Characteristics属性应该为:c0000040,否则程序无法运行;
2.实现
#include "stdafx.h" #include "PeTool.h" #include "string.h" #define SRC "C:\\Users\\Administrator\\Desktop\\TraceMe.exe" #define DEST "C:\\Users\\Administrator\\Desktop\\TraceMe_new.exe" //新增一个节 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, ".Inject"); newSec->Misc.VirtualSize = 0x1000; //新节的内存镜像大小为1000 newSec->VirtualAddress = opHeader->SizeOfImage; //新节的内存偏移为内存镜像大小 newSec->SizeOfRawData = 0x1000; //新节的文件镜像大小为1000 PIMAGE_SECTION_HEADER lastSec = seHeader + (peHeader->NumberOfSections -1); //最后一个节表 newSec->PointerToRawData = lastSec->PointerToRawData + lastSec->SizeOfRawData; //新节的文件偏移紧接最后一个节 newSec->Characteristics = 0xc0000040; //导入表所在节的属性为c0000040,否则程序无法运行 //5.设置全0节表 memset((LPVOID)(newSec+1), 0, 40); //6.修头信息 peHeader->NumberOfSections = peHeader->NumberOfSections + 1; opHeader->SizeOfImage = opHeader->SizeOfImage + 0x1000; //7.申请内存 LPVOID sec = malloc(0x1000); if(!sec){ printf("给新节申请内存失败\n"); return 0; } memset(sec, 0, 0x1000); //8.返回 *pSec = sec; return 0x1000; } //导入表注入 void dllInject(){ //定义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_IMPORT_DESCRIPTOR importDir= 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; importDir = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, (dataDir + 1)->VirtualAddress )); //3.新增一个节 LPVOID newSec = NULL; DWORD secSize = addSec(pFileBuffer, &newSec); if(!newSec){ printf("新增节失败\n"); return; } //4.复制导入表 PIMAGE_IMPORT_DESCRIPTOR copyImport = (PIMAGE_IMPORT_DESCRIPTOR)newSec; LPVOID copyDir = newSec; int i = 0; while((importDir+i) -> OriginalFirstThunk){ memcpy(copyDir, (LPVOID)(importDir+i), 20); copyDir = (LPVOID)((DWORD)copyDir + 20); i++; } (dataDir + 1) -> VirtualAddress = fileSize; //修复数据目录 //5.新增一个导入表 PIMAGE_IMPORT_DESCRIPTOR newImport = (PIMAGE_IMPORT_DESCRIPTOR)copyDir; //添加IMAGE_IMPORT_BY_NAME PIMAGE_IMPORT_BY_NAME newImportName = (PIMAGE_IMPORT_BY_NAME)((DWORD)newImport + 20*2); newImportName -> Hint = 1; LPSTR newFunName = (LPSTR)((DWORD)newImportName + 2); strcpy(newFunName, "ExportFunction"); //将待注入的dll中的导出的函数名设置到这里 //添加INT表 PDWORD newInt = (PDWORD) ((DWORD)newFunName + strlen(newFunName) + 1); *newInt = fileSize + ((DWORD)newImportName - (DWORD)newSec); newImport ->OriginalFirstThunk = fileSize + ((DWORD)newInt - (DWORD)newSec); //修复新导入表 //添加IAT表 LPVOID newIat = (LPVOID) ((DWORD)newInt + 8); memcpy(newIat, (LPVOID)newInt, 8); newImport ->FirstThunk = fileSize + ((DWORD)newIat - (DWORD)newSec); //修复新导入表 //添加dll名 LPSTR newDllName = (LPSTR)((DWORD)newIat + 8); strcpy(newDllName, "InjectDll.dll"); //将待注入的dll名设置在这里 newImport ->Name = fileSize + ((DWORD)newDllName - (DWORD)newSec); //修复新导入表 //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("done\n"); return; } int main(int argc, char* argv[]) { //导入表注入 dllInject(); getchar(); }
结果:可以看到在程序运行前弹出了窗口;
说明dll的入后函数被调用;
也就是dll被成功注入;
