1.修改導入表,即添加一個新的導入表描述符及其iat,int. 這樣系統加載該pe文件時將自動加載添加的dll,從而實現dll注入
2.思路:
可以手工修改,但是復雜程度一點也不比寫代碼低,而且低效. 通過代碼實現可以一勞永逸.
新增一個節來存儲新的導入表. 其實也可以在原來的pe文件找空隙插入進去,但是又很難實現通用,因為不同的pe文件空隙位置大小不同.容易出錯
新增的導入表描述符通過序號導入,這樣更簡單一些
基本步驟: 修改節區數量,在原來的節區頭中添加一個節,如果空間不夠就不行了,但絕大多數pe文件的節區空間都是足夠的 ->
填好新節頭的字段->申請節內存->復制原來的導入表到新節中->增加新的導入表描述符並填寫相應字段->增加該導入表描述符的iat和int->將新增數據
寫入到文件尾部-> 修改pe文件中重要字段
以上步驟並不是嚴格順序的,因為涉及到rva, 文件偏移,各種大小的計算.
3.代碼:
//導入表注入 DWORD rva2offset(LPVOID base, DWORD rva) { IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)base; IMAGE_NT_HEADERS32* ntHeader = (IMAGE_NT_HEADERS32*)(dosHeader->e_lfanew + (DWORD)base); IMAGE_SECTION_HEADER* sectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)ntHeader + sizeof(IMAGE_NT_HEADERS32)); if (rva<ntHeader->OptionalHeader.SizeOfHeaders) { return rva; } for (DWORD i = 1; i <= ntHeader->FileHeader.NumberOfSections; i++) { //如果到了最后一個節時,就可以直接計算了,否則可以通過前后節頭的VirtualAddress確定在哪個節中 if (i == ntHeader->FileHeader.NumberOfSections) { return rva - sectionHeader->VirtualAddress + sectionHeader->PointerToRawData; } else if (rva >= sectionHeader->VirtualAddress && rva < (sectionHeader + 1)->VirtualAddress) { return rva - sectionHeader->VirtualAddress + sectionHeader->PointerToRawData; } sectionHeader++; } return 0; } //文件或者內存對齊 DWORD PEAlign(DWORD size, DWORD dwAlignTo) { return(((size + dwAlignTo - 1) / dwAlignTo)*dwAlignTo); } DWORD importTableInject(WCHAR* modulepath,char* dllpath)//dllpath傳入dll的名字,而不是路徑 { //先備份源文件 WCHAR newFile[MAX_PATH]; wsprintf(newFile, L"%s.bak", modulepath); CopyFileW(modulepath, newFile, 0); HANDLE hFile = CreateFileW(modulepath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile==0||hFile==INVALID_HANDLE_VALUE) { return 0; } DWORD fileSize = GetFileSize(hFile, 0); HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0 , 0); if (hMap<=0) { CloseHandle(hFile); return 0; } LPVOID imagebase = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (imagebase==0) { CloseHandle(hFile); CloseHandle(hMap); return 0; } PIMAGE_NT_HEADERS32 ntHeader = (PIMAGE_NT_HEADERS32)((DWORD)imagebase + ((PIMAGE_DOS_HEADER)(imagebase))->e_lfanew); if ((ntHeader->FileHeader.NumberOfSections+1)*sizeof(IMAGE_SECTION_HEADER)>ntHeader->OptionalHeader.SizeOfHeaders) { CloseHandle(hFile); CloseHandle(hMap); return 0; } //定位到最后一個節區的最外面地址,就是nt頭最后的尾部 PIMAGE_SECTION_HEADER newSection = (PIMAGE_SECTION_HEADER)(ntHeader + 1) + ntHeader->FileHeader.NumberOfSections; //添加節區頭 memcpy(newSection->Name, "freesec", 8); //節區頭名字最多為8個字節,包括結尾的\0 newSection->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; newSection->Misc.VirtualSize = //添加一個dll信息,所以在原來大小的基礎上加一個導入表描述符大小加dll名字字符串大小+4個IMAGE_THUNK_DATA32大小 ntHeader->OptionalHeader.DataDirectory[1].Size + sizeof(IMAGE_IMPORT_DESCRIPTOR) + strlen(dllpath) + 1 + sizeof(IMAGE_THUNK_DATA32) * 4; newSection->NumberOfLinenumbers = 0; newSection->NumberOfRelocations = 0; newSection->PointerToLinenumbers = 0; newSection->PointerToRawData = (newSection-1)->PointerToRawData+(newSection-1)->SizeOfRawData; newSection->PointerToRelocations = 0; newSection->VirtualAddress = (newSection - 1)->VirtualAddress + PEAlign((newSection - 1)->SizeOfRawData, ntHeader->OptionalHeader.SectionAlignment); newSection->SizeOfRawData = PEAlign(newSection->Misc.VirtualSize, ntHeader->OptionalHeader.FileAlignment); DWORD sectionSize = newSection->SizeOfRawData; //添加節表 SetFilePointer(hFile, 0, 0, FILE_END); //文件指針向文件尾部 LPVOID content = malloc(newSection->SizeOfRawData); memset(content, 0, newSection->SizeOfRawData); char* p = (char*)content; memcpy(content, (LPVOID)((DWORD)imagebase+rva2offset(imagebase, ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress)), ntHeader->OptionalHeader.DataDirectory[1].Size-sizeof(IMAGE_IMPORT_DESCRIPTOR)); p =(char*)((DWORD)p + ntHeader->OptionalHeader.DataDirectory[1].Size - sizeof(IMAGE_IMPORT_DESCRIPTOR)); ((PIMAGE_IMPORT_DESCRIPTOR)p)->OriginalFirstThunk = newSection->VirtualAddress + ntHeader->OptionalHeader.DataDirectory[1].Size + sizeof(IMAGE_IMPORT_DESCRIPTOR) + strlen(dllpath) + 1; ((PIMAGE_IMPORT_DESCRIPTOR)p)->FirstThunk = ((PIMAGE_IMPORT_DESCRIPTOR)p)->OriginalFirstThunk + sizeof(IMAGE_THUNK_DATA32)*2; ((PIMAGE_IMPORT_DESCRIPTOR)p)->ForwarderChain = 0; ((PIMAGE_IMPORT_DESCRIPTOR)p)->Name = newSection->VirtualAddress + ntHeader->OptionalHeader.DataDirectory[1].Size + sizeof(IMAGE_IMPORT_DESCRIPTOR); ((PIMAGE_IMPORT_DESCRIPTOR)p)->TimeDateStamp = 0; p += sizeof(IMAGE_IMPORT_DESCRIPTOR)*2; //越過新增節和空節尾 //導入表描述符中的name字段,注意2者之間rva的關聯 memcpy(p, dllpath, strlen(dllpath)+1); p += strlen(dllpath) + 1; //添加注入的dll的iat和int.4個元素,前2個是給iat的,后2個給int的均以 IMAGE_THUNK_DATA32 ixt[4] = { 0 }; ixt[0].u1.AddressOfData |= 0x80000000; //將最高位置1,表示是以序號導入 ixt[0].u1.AddressOfData += 1; ixt[2].u1.AddressOfData |= 0x80000000; //將最高位置1,表示是以序號導入 ixt[2].u1.AddressOfData += 1; memcpy(p, ixt, 4 * sizeof(IMAGE_THUNK_DATA32)); //修改必要字段 ntHeader->FileHeader.NumberOfSections++; ntHeader->OptionalHeader.SizeOfImage += newSection->SizeOfRawData; //綁定導入表改為0,保險一些 ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress = newSection->VirtualAddress; ntHeader->OptionalHeader.DataDirectory[1].Size = newSection->Misc.VirtualSize; UnmapViewOfFile(imagebase); //這個操作需要在writefile之前調用 if (!WriteFile(hFile, content, sectionSize, 0, 0)) { CloseHandle(hMap); CloseHandle(hFile); free(content); return 0; } CloseHandle(hMap); CloseHandle(hFile); free(content); return 1; } //end 導入表注入