1.修改导入表,即添加一个新的导入表描述符及其iat,int. 这样系统加载该pe文件时将自动加载添加的dll,从而实现dll注入
2.思路:
可以手工修改,但是复杂程度一点也不比写代码低,而且低效. 通过代码实现可以一劳永逸.
新增一个节来存储新的导入表. 其实也可以在原来的pe文件找空隙插入进去,但是又很难实现通用,因为不同的pe文件空隙位置大小不同.容易出错
新增的导入表描述符通过序号导入,这样更简单一些
基本步骤: 修改节区数量,在原来的节区头中添加一个节,如果空间不够就不行了,但绝大多数pe文件的节区空间都是足够的 ->
填好新节头的字段->申请节内存->复制原来的导入表到新节中->增加新的导入表描述符并填写相应字段->增加该导入表描述符的iat和int->将新增数据
写入到文件尾部-> 修改pe文件中重要字段
以上步骤并不是严格顺序的,因为涉及到rva, 文件偏移,各种大小的计算.
3.代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | //导入表注入 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 导入表注入 |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
· 用 C# 插值字符串处理器写一个 sscanf