首页    新闻    下载    文档    论坛     最新漏洞    黑客教程    数据库    搜索    小榕软件实验室怀旧版    星际争霸WEB版    最新IP准确查询   
名称: 密码:      忘记密码  马上注册
安全知识 :: 脚本攻防

编写变形的ShellCode实战篇


http://www.gipsky.com/
适合读者:溢出分析员、漏洞爱好者

前置知识:汇编基本语法

WTF:上期的原理篇我们花了很多笔墨来说明一个变形的Shellcode到底是什么原理,因为Shellcode是所有溢出的灵魂,绝对是大家讨论的热点和重点。这一次推出实战篇,我们就来做一个简单的变形ShellCode。由于篇





适合读者:溢出分析员、漏洞爱好者

前置知识:汇编基本语法

WTF:上期的原理篇我们花了很多笔墨来说明一个变形的Shellcode到底是什么原理,因为Shellcode是所有溢出的灵魂,绝对是大家讨论的热点和重点。这一次推出实战篇,我们就来做一个简单的变形ShellCode。由于篇幅的限制,这个变形的东西还是比较幼稚,但是它已经能够逃脱几乎所有按照ShellCode特征来杀“毒”的各类软件,在ShellCode不限长度的时候,你甚至可以反复的使用这个加密的头部来进行变形。具体内容,我们在文中慢慢聊……



编写变形的ShellCode实战篇

文/ 益田西守歌

在切入正题之前还是按照老传统先科普一下,虽然WTF老大屡次威胁我,但我革命的坚贞是不怕强势斗争到底的(欲知详情,请看2004年9期黑防本栏目)。ShellCode后面一部分是实现真正功能的部分,这一块我们可以用某种加密的方式来进行编码,而前面的Decode部分则是解码。实现功能部分的编码本身就带有变形的意义,比如我用0x99(最常见的)来异或,与我们用0x98或者0xee来异或结果是不同的,这一点外在的表现就是变形。我们需要更多是一种能够变形的解码部分,而且要求这部分可以相对独立,这样子就像一个套子一样,套在一块代码上面就变了一次形,多套几次也没关系,最多变成其它的更加让人不知为何物的东西。

需要明白的是,我们写的这个变形ShellCode是一次性的(也叫做抛弃型的),因为ShellCode在一次Exploit的时候只能用一次,不像病毒一样还要辛辛苦苦的传播。所以我们做的东西,只要能够生成每次不一样的ShellCode就可以,ShellCode不用包含自己让自己变形的部分??现在这句话还有点拗口,看到后面自然就会明白的。

那么,我们的变形之旅还是从一个最基本的Decode部分开始:

jmp l

de: pop ebx

xor ecx, ecx

mov cl, 222h

lp: xor byte ptr [ebx], 0x99

inc ebx

loop lp

jmp stt

l: call de

stt:

看过两期连载的朋友可能都要笑了,又是这个,都看烦了。没关系,从最简单的开始嘛。我们假设起作用的部分是Xor过0x99的,就可以专注于解码部分了。如果异或的数字不是0x99,那只需要改变第五行的那个操作数即可??这也是一个变形。这一块解码部分首先满足了相对的独立性,也就是说它不依赖于任何的环境,比如各种寄存器的值或者是栈上的数据,它只是负责将后面的数据按照异或0x99的方式解码。这样我们把它看作一个“帽子”,并用D来表示,将X()作为异或0x99的编码方法,对于任何一个可执行的ShellCode(表示为字符串S),下面就是我们最常看到的形式:

D X(S)

将帽子再套一层,变成了:

D X( D X(S) )



WTF:这也是可以执行的,就是做了两次解码工作。不过我们平时是见不到这种形式的ShellCode的,因为解多少次码都可以变成解一次码的方式,而黑客们总是喜欢最简便的东西。话说回来,虽然不常见到,对于变形来说,这也是很不错的方法,前提是你的ShellCode没有限制长度。



仔细看这个Decode部分,除了跳转以外,用到了两个寄存器,Ebx和Ecx。其中Ecx是作为计数器使用,一旦确定了要用Loop,那就不能改变,所以真正可以选择的还是Ebx。考虑到栈的完整性和指令的长度,一般不建议使用Esp和Ebp,所以只有Eax/ebx/edx/esi/edi五个寄存器可以选择使用,而随机的选择出一个寄存器后,就要把原来所有的Ebx全部替换成选出来的那个寄存器才行。

这里的替换不是VB中Replace这么简单,我们操作的是最后生成的机器码,寄存器的变换导致字节的变换,不是简单的Replace。在原理篇里面说过,总可以有某个公式来对应不同寄存器相同指令下的机器码,在说明这个问题前,先得了解寄存器的顺序问题。

寄存器本身没有高低的级别,然而对应指令的时候,它们有一个潜在的顺序。简单的举个例子,对INC而言,Inc eax对应指令为0x40,inc ecx对应指令为0x41,inc edx对应指令为0x42……一直到inc edi对应的指令为0x47,排列的寄存器顺序就是eax/ecx/edx/ebx/esp/ebp/esi/edi。倘若给出一个顺序定义如下:

enum Register

{

EAX = 0, ECX = 1, EDX = 2, EBX = 3,

ESP = 4, EBP = 5, ESI = 6, EDI = 7

};

很容易得到Inc exx的对应指令是0x40 Register,同样的Dec、Push、Pop等都满足这样简单的规律。回过头来看那个Decode,要做的工作是选择寄存器然后改变代码,自然而然的就要去寻找其中的规律,INC的已经说了,剩下Pop和Xor byte ptr[exx], 0xXX需要动手找一下。在VC中嵌入汇编然后查看代码后可以清楚地看到,前面说到的五个寄存器,基本上满足的是如下两个公式:

pop exx:

0x58 Register

xor bytr ptr[exx], 0xXX:

[80] [0x30 Register] [XX]

XOR是一个三字节的指令,前面0x80固定,最后一个是操作数。在实验的时候你也看到了,对于Esp和Ebp,这是一个四字节的指令,长度不一样,也是我们要抛弃的一个原因。

准备工作已经就绪,就从一个程序开始,按照原理篇的几个部分来做。第一个程序是Test0.cpp(WTF:请见光盘,下同),我已经写好了,这个没有什么特别的地方,只是告诉你这是最基础的部分,看看可以熟悉一下解码部分的最基本写法,而且在后面我们也可以把每一步生成的Decode部分拿过来测试测试。作为一个基础,我把它命名成了0,也是C程序员的习惯吧。

程序Test1.cpp就是变形的开始。我们先人为的把Decode部分分成了八份,基本上每个指令就是一份,这八个部分,真正与选择寄存器有关的还是2、4和5,第一步“寄存器的选择”,焦点就集中在这三个部分上。



WTF:本来这里有九条指令的,但是Xor ecx,ecx和Mov cl, 222h其实就是Mov ecx, 222h一个指令,不过是我们为了避免0x00出现而耍的花招而已,大体上还是把他们看作一个指令为好。



选择寄存器不用说了,初始化一个随机种子,然后就可以按照获取的随机数来选择一个。根据这个寄存器,按照上面的公式,第二句的Pop exx的机器码就应该是0x58 Register,第四句的XOR中,第二个字节应该是0x30 Register。同样,第五句的Inc exx应该成了0x40 Register。具体的实现在Test1.cpp里面对应了Step1函数,函数虽然很短,但程序微长,建议大家还是打开看看。

除了选择寄存器以外,还有一个函数是Combine,这是将分散的头部写成一个统一的头部。这个函数还有一个另外的功用,就是对代码进行一些可能的调整,交换指令次序一部分也可以在这里来实现。

不管你相不相信,就这么简单的一段程序(test1.cpp),已经是一个变形的头部了。遗憾的是它的变形能力还非常有限,因为归根结底这段代码里面只用到了一个寄存器,我们选择寄存器的组合方式只有区区的五个。如果有更多的寄存器在Decode部分出现,同时我们要选出很多个寄存器备用的话,这样子组合下来的结果就更多,变形的效果更好(相应的会更复杂,所以还是简单的来做例子比较好)。

原理篇里面说到的第二种方法是交换指令的顺序,在这个地方也可以办到,看着两段:

pop ebx

xor ecx, ecx

mov cl, 222h

还是将后面两句看成一个整体,弹出栈顶的值给寄存器或是赋值给Ecx,这两步没有绝对的先后次序,也就是说谁在前面并不影响到最终的结果。因而我们可以随意的调换两者的位置(虽然是“随意”,说到底也就两种方式而已,如果很多条指令可以互换次序的话,情况就麻烦了),对应的实现在函数Step2()中,交换一下字符串的内容,这样的话不影响到后面的一系列函数。

最麻烦的还是所谓的插入NOP-like指令。最容易想到变形方法就是这个,然而却是最难实现的。Decode部分不可避免的要有一些相对跳转和相对调用的指令,一旦其中的某一个指令长度发生了变化,几乎要影响到所有相对跳转的地方,因此,要加入NOP-like指令的时候,需要对每一个指令进行考虑。

对于我们上面写的这个Decode而言,我们已经人为的将其分成了八个部分,之所以这样做,有一个好处是我们可以在加入NOP-like指令的时候,仅仅是加入每一个部分中去,当作这个部分的一个整体,而不是安插在某两个部分之间,难于理解不说,同时也难于处理。

以插入NOP 0x90为例(其它的NOP-like指令我们已经在原理篇里面讨论过了)。如果我们插入到第一句Jmp l后面,毫无疑问的,直接影响到的只有自身而已,最后面的一个Call de也可以算一个,不过Call的地方是一条有意义的指令或者是一个NOP关系不是很大,简便起见,索性就不修改Call de指令。这个修改反映在Test3的Step3()函数中,在对第一句加入了NOP-like以后,Jmp的操作数应该相应的加上增长的字节数,所以Head1[1]就视情况有所修改。

对Head2的插入就更为复杂。Pop exx后面加入NOP-like以后,除了第一个Jmp以外,后面的Call也受到了影响,同样的Xor ecx,ecx和Mov cl加入NOP-like以后也有同样的影响,这两个在前面说过是可以交换的,也容易证明交换后对前后指令影响一致,所以可以一同处理,即:加入Nop-like后前面的Jmp要多跳一跳指令,后面Call的目标也要挪一下。

后面的修正,可以挨着挨着做,都是同样的方法。Test3.cpp中还举了一个Inc exx后的插入,这里就不具体的解释了,道理和前面差不多,不过插入的不再是NOP,而是《原理篇》里面提到过的指令对??Inc eax和Dec eax(0x4048)。Nop-like的指令多种多样,在网上也有相关的讨论,有兴趣的话可以去看看相关文章。

还有一个Misc()函数,这个函数是通过Decode部分的本身性质来变形的,例如上面的循环次数,也就是要解码多少个字节,这个数目可大可小,只要能够保证所有编码过的字符都能够被解到即可。像此类的变形不太能说清楚属于什么方面,只能视情况而定,所以放到杂类中了。

到这个时候,差不多一个变形的Decode部分已经完成。剩下一件小事情,就是将其作用的ShellCode用一个数字来异或,然后将对应的数字填入Decode部分即可,代码我已经有一个简单的实现(WTF:见光盘中给出的Test4.cpp),具体的细节不再做解释,大家看代码一下子就可以明白。

光盘里的东西到此为止,然而变形的路子并没有就此结束,还有一些值得讨论的,顺带在文章中简单的提提。对这个过于简单例子有所不满的朋友,下面的话是可以进一步做到的,希望您能和我交流一下。

第一是关于起作用的部分,也就是所谓Real ShellCode部分的编码方式。例子中给出的是很普通的单字节异或,据我的实验来看,似乎四个字节一组或者是四个字节一组的异或效果比较好,不过限于篇幅的关系,没有给出这样的代码。四个字节一组的主要思想就是平衡解码部分的生成难度和变形能力,对于32位机而言,简单处理情况下四字节(DWORD)刚好是一次性处理的极限;七字节主要考虑的是变形的能力,这种情况下显然不能一次性异或七个字节,而可能要4-2-1或者2-2-2-1或者其他分次异或的方法,对应的指令集比较分散,只是解码部分稍微麻烦了一点。当然,其它的编码方式也可以,只不过写起来可能还要复杂一些。

第二是解码部分的编写。这里给出的编写方法显然太过于复杂,好的办法是在你编写的上面套一层像编译器一样的东西,这样需要做的不过就是不断地加指令,相对位置的调整还有机器码的生成都可以让程序自己完成。我写了个简单的,有兴趣的话可以交换一下(WTF:唉,圈子内的风气啊,没办法啊没办法……),省是省力些,不过不太好用就是。

第三是有关解码头部本身的。这个头部,在前面说过了,可以反复的加,反复的用,没有关系的,代码实现起来也很方便。变形病毒的话,这个头部是集成在了Real ShellCode里面,负责在传播的时候生成新的解码部分,这里我们编写变形的ShellCode不需要这么麻烦(抛弃型的),就单独把头部的生成提取出来做成了程序,这也就是前面说的“不用包含自己让自己变形的部分”。

生成变形的ShellCode不是一件很难的事情,只要你能写出一种编码的方法,然后写出解码的头部就可以了,然而麻烦的是如何在长度(复杂度)与变形的能力之间寻求到一个平衡点。写病毒的话,考虑的可能不是这么多,因为只要能找到足够的空间可以隐藏,变形能力越强,对杀毒软件的考验越大。写ShellCode则不然,通常Exploit需要的ShellCode不能太长,而且IDS/IPS借以判定的字串往往还不是ShellCode,感觉上只要能写出一个让杀毒软件不认识的ShellCode就可以了,从这一点上看,变形ShellCode的唯一好处是每次能够给你一个基本上全新的ShellCode,只要你不公开你的算法,杀毒软件厂商没有哪个精力(也许是能力)来分析你的东西!

(文中涉及的代码已收录到杂志配套光盘“杂志相关”栏目,按文章名查找即可)
<< 编程实现exe转vbs的vbs代码 JSP、靠下载拿shell >>
API:
gipsky.com& 安信网络
网友个人意见,不代表本站立场。对于发言内容,由发表者自负责任。

系统导航

 

Copyright © 2001-2010 安信网络. All Rights Reserved
京ICP备05056747号