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被成功注入;
