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个字节,包括结尾的 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 导入表注入