谁能给我解释一下这段代码?

标签 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.
}

谁能给我一个更好的解释?

最佳答案

显然,这段代码试图改变堆栈,以便当 main函数返回,程序执行不会定期返回运行时库(通常会终止程序),而是跳转到保存在 shellcode 中的代码中。大批。

1) int *ret;
在堆栈上定义一个变量,就在 main 的下面函数的参数。

2) ret = (int *)&ret + 2;
ret变量指向 int *即放置两个 int s 以上 ret在堆栈上。据说这就是返回地址所在的位置,当 main 时程序将继续返回。

2) (*ret) = (int)shellcode;
返回地址设置为shellcode的地址数组的内容,所以 shellcode的内容将在 main 时执行返回。
shellcode似乎包含可能执行系统调用以启动 /bin/sh 的机器指令.我可能是错的,因为我实际上并没有拆卸 shellcode .

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

回复您的第二个问题:

and what happens if I use ret=(int)&ret +2 and why did we add 2? why not 3 or 4??? and I think that int is 4 bytes so 2 will be 8bytes no?


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——但这些在这里并不重要。)当函数执行时,堆栈指针指向 ret .返回地址位于“上方”的 64 位 ret ,对应于 + 2
    ret = (int*)&ret + 2; 
    

    + 2因为 retint* , 和 int是 32 位,因此加 2 表示将其设置到 (int*)&ret 上方 2 × 32 位(=64 位)的内存位置...这将是返回地址的位置,如果上一段中的所有假设都是正确的。

    游览:让我用英特尔汇编语言演示如何调用 C 函数(如果我没记错的话——我不是这个主题的专家,所以我可能是错的):
    // 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
    

    在 main 中,可能会发生以下情况:
    // create a new stack frame and make room for local variables:
    push  ebp
    mov   ebp, esp
    sub   esp, 4
    
    // access return address:
    mov   edi, ss:[ebp+4]
    
    // access argument 'argc'
    mov   eax, ss:[ebp+8]
    
    // access argument 'argv'
    mov   ebx, ss:[ebp+12]
    
    // access local variable 'ret'
    mov   edx, ss:[ebp-4]
    
    ...
    
    // restore stack frame and return to caller (by popping the return address)
    mov   esp, ebp
    pop   ebp
    retf
    

    另见: procedure call sequence in C的说明对这个话题的另一种解释。

    关于谁能给我解释一下这段代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2705854/

    相关文章:

    c++ - 什么时候将 double 转换为整数是未定义的行为

    c - 在 C 中将字符串压入和弹出堆栈

    C堆栈奇怪的行为

    java - CVE-2018-14667;为 RichFaces 3.X 生成有效负载

    c - 遍历 C 数组

    CS50 Mario Pyramid - 代码有效,但运行 check50 时仍然出现错误

    security - 如何利用缓冲区溢出来攻击计算机?

    go - 为什么 Go 中的堆是可执行的?

    无法打印可能在 C 中使用 typedef 构造的 MyType 中的值

    JavaScript 堆栈、队列和事件循环?