博客网 >

Shellcode技术杂谈
作者:分类:默认分类标签:
已发表在《黑客X档案》(2005-9期)

    Shellcode是溢出程序和蠕虫病毒的核心,提到它自然就会和漏洞联想在一起,毕竟Shellcode只对没有打补丁的
主机有用武之地。网络上数以万计带着漏洞顽强运行着的服务器给hacker和Vxer丰盛的晚餐。漏洞利用中最关键的是
Shellcode的编写。由于漏洞发现者在漏洞发现之初并不会给出完整Shellcode,因此掌握Shellcode编写技术就显得
尤为重要。
    小弟经过一段时间的修炼,自以为对Shellcode小有心得,故在这里写出来,博君一笑。不当之处,还请各位高人
不吝指教。
                                    什么是Shellcode
    我在这里简单讲一下什么是Shellcode。Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器
利用特定漏洞的代码,一般可以获取权限。另外,Shellcode一般是作为数据发送给受攻击服务的。
                                    Shellcode编写考虑因素
    Shellcode一般作为数据发送给服务端造成溢出,不同数据对数据要求不同,因此,Shellcode也不一定相同。但
Shellcode在编写过程中,有些问题是一致的:
    ⒈Shellcode的编写语言。
    用什么语言编写最适合Shellcode呢?这个问题没有定论。一般采用的是C语言,速度较快,但是ASM更便于控制
Shellcode的生成。到底是快速编写还是完全控制呢?很难回答呢。
    ⒉Shellcode本身代码的重定位。Shellcode的流程控制,即如何通过溢出使控制权落在Shellcode手中
    ⒊。Shellcode中使用的API地址定位。
    ⒋Shellcode编码问题。
    ⒌多态技术躲避IDS检测。
    现在我们就来研究这些问题在处理时常用的方法。
                                    Shellcode编写技术
    ⒈Shellcode编写语言
    Shellcode本质上可以使用任何编程语言,但我们需要的是提取其中的机器码。Shellcode使用汇编语言编写是最
具可控性的,因为我们完全可以通过指令控制代码生成,缺点就是需要大量的时间,而且还要你深入了解汇编。如果你
想追求速度,C是不错的选择。C语言编写起来较为省力,但Shellcode提取较为复杂,不过,一旦写好模板,就省事许
多。例如,这里有一个写好的模板:
   void Shellcode()
   {
       __asm
   {
       nop
       nop
       nop
       nop
       nop
       nop
       nop
       nop
    }
    }
    然后在main()中用函数指针操作和memcmp定位shellcode,用pintf之类函数将shellcode打出来或保存即可。示
例代码略。纵观当前shellcode,大部分是由C完成的,因此,想来大家已经取舍完了吧?
    ⒉Shellcode代码地址定位,获取程序EIP。
    为什么要获取EIP呢?原因是,我们需要我们的Shellcode能够执行,对病毒技术有了解的话,应该知道他们是怎么
定位的:利用CALL/POP来实现。
    这里就不得不提到两种方法:JMP ESP和CALL/POP EBX。这是人们在对windows系统熟悉之后的方法,成功率非常
高。相信看过王炜兄的教程的朋友应该有印象吧。这里我就简单说一下。
    我们的方法时通过Shellcode地址覆盖返回地址,在溢出后即可跳转到我们的代码中,以获取权限。而Shellcode
在内存中的地址并不固定,因此我们利用系统的DLL文件中的JMP ESP或CALL ESP、CALL EBP来实现对Shellcode地址
的间接跳转。这样有两个好处,一是不必准确定位Shellcode地址;二是可以防止strcpy对00字节的截断,因为DLL文
件中,地址一般为7FXXXXXX。具体细节,网上已有相关的东东,大家自己找来看看吧。
    ⒊Shellcode中的API地址定位。
    Shellcode代码的运行环境和病毒在某些方面是类似的,由于系统不同,Api的地址也不尽相同。因此,要想让
Shellcode在不同Windows下运行就必须解决Api的定位问题。API定位的关键是了解Windows DLL映像文件格式,即PE
文件格式,然后通过搜索函数的Export表获取API地址。定位方法有暴力搜索法、从进程PEB中获取和遍历SEH链法。我
们这里使用从进程PEB中获取,示例代码如下:
__asm
        {
                push ebp;
                sub  esp, 0x40;
                mov  ebp,esp;

                push ebp;
                        mov eax, fs:0x30                        ;PEB
                        mov eax, [eax + 0x0c]                        ;Ldr
                        mov esi, [eax + 0x1c]                        ;Flink
                        lodsd               
                        mov edi, [eax + 0x08]                        ;edi就是kernel32.dll的地址
                       
                        mov         eax, [edi+3Ch]                ;eax = PE首部
                        mov         edx,[edi+eax+78h]
                        add         edx,edi                        ;edx = 输出表地址
                        mov         ecx,[edx+18h]                ;ecx = 输出函数的个数
            mov         ebx,[edx+20h]                                 
                        add         ebx,edi                        ;ebx =函数名地址,AddressOfName
search:
                        dec         ecx
                        mov         esi,[ebx+ecx*4]                               
                        add         esi,edi                        ;依次找每个函数名称
                        ;GetProcAddress
                        mov                        eax,0x50746547
                        cmp                        [esi],        eax;        'PteG'
                        jne                search
                        mov                        eax,0x41636f72
                        cmp                        [esi+4],eax;        'Acor'
                        jne                search                               
                       
                        ;如果是GetProcA,表示找到了               
            mov         ebx,[edx+24h]
                        add         ebx,edi                        ;ebx = 索引号地址,AddressOf
                        mov         cx,[ebx+ecx*2]                ;ecx = 计算出的索引号值
            mov         ebx,[edx+1Ch]
                        add         ebx,edi                        ;ebx = 函数地址的起始位置,AddressOfFunction
                        mov         eax,[ebx+ecx*4]                       
                        add         eax,edi                        ;利用索引值,计算出GetProcAddress的地址


                        mov        [ebp+40h], eax                                ;把GetProcAddress的地址存在 ebp+40中
    接下来是使用GetProcAddress()和LoadLibraryA()获取其他需要函数了,和C没什么两样,略过了吧,很累呢。
    ⒋Shellcode的编码问题。
    写过Shellcode的兄弟对这个应该恨熟吧?例如:strcpy函数中不能有0x00,RPC DOCM溢出时不能用0x5c等等。
因为假如有这些字符,会导致服务中断Shellcode,溢出失败。不同溢出对shellcode要求不同,当然需要精选字符来
达到目的,这样太累了些,简单点就是写一段代码,示例如下:
  for(i=0;i     ch=sc_buff^Enc_key;
     //对可能字符进行替换
     if(ch<=0x1f||ch==' '||ch=='.'||ch=='/'||ch=='\\'||ch=='0'||ch=='?'||ch=='%'||ch=='+')
     {
          buff='0';
          ++k;
          ch+=0x31;
      }
     //将编码Code放在DecryptSc后
     buff[k]=ch;
     ++k;
    }
    解码时代码 解码时代码,示例如下:
     jmp   next
getEncodeAddr:
     pop   edi
     push   edi
     pop    esi
     xor    ecx,ecx
Decrypt_lop:
     loasb
     cmp   al,cl
     jz    shell
     cmp   al,0x30  //判断是否为特殊字符
     jz    specal_char_clean
store:
     xor   al,Enc_key
     stosb
     jmp   Decrypt_lop
special_char_clean:
     lodsb
     sub   al,0x31
     jmp   store
next:
     call  getEncodeAddr
    这里只给了一个简单的例子,面对对Unicode编码有要求的,限于篇幅,这里就不详解了。
                                              写在最后
    编写调试Shellcode很是辛苦,但完成之后却有巨大的成就感。这里不是教你做EXP去害人,只是从研究角度出发,
让大家了解这种技术,从而加以防范,为网络和平安宁奉献自己的力量。最后,我附带了一个作为示例的Shellcode代
码,大家可以参考Sample.cpp来看看。
<< 经典RPG游戏盘点 / 浅析OPE技术 >>

专题推荐

不平凡的水果世界

不平凡的水果世界

平凡的水果世界,平凡中的不平凡。 今朝看水果是水果 ,看水果还是水果 ,看水果已不是水果。这境界,谁人可比?在不平凡的水果世界里,仁者见仁,智者见智。

中国春节的那些习俗

中国春节的那些习俗

正月是农历新年的开始,人们往往将它看作是新的一年年运好坏的兆示期。所以,过年的时候“禁忌”特别多。当然,各个地方的风俗习惯不一样,过年的禁忌也是不一样的。

评论
0/200
表情 验证码:

janxin

  • 文章总数0
  • 画报总数0
  • 画报点击数0
  • 文章点击数0
个人排行
        博文分类
        日期归档