显然,这段代码试图更改堆栈,以便当主函数返回时,程序执行不会正常返回到运行时库(这通常会终止程序),而是会跳转到保存在shellcode数组中的代码。
1) int *ret;
定义一个变量在堆栈上,就在main函数的参数下方。
2) ret = (int *)&ret + 2;
让ret变量指向一个int*,该int*位于ret以上两个int的位置。据说这就是程序将继续运行的返回地址所在的位置,当main函数返回时。
3) (*ret) = (int)shellcode;
将返回地址设置为shellcode数组内容的地址,这样当main函数返回时,shellcode的内容将被执行。
shellcode似乎包含可能调用系统调用来启动/bin/sh的机器指令。我可能错了,因为我实际上没有反汇编shellcode。
P.S.:此代码依赖于计算机和编译器,并且可能无法在所有平台上工作。
回答您的第二个问题:
ret=(int)&ret +2会发生什么?为什么我们要加2?为什么不是3或4?我认为int是4字节,所以2将是8字节,对吗?
ret声明为int*,因此将int(例如(int)&ret)分配给它将导致错误。至于为什么要加2而不是其他任何数字:显然是因为此代码假定返回地址将位于堆栈上的那个位置。考虑以下内容:
这段代码假设当某些内容被压入时,调用堆栈向下增长(如在英特尔处理器上确实如此)。这就是为什么要添加数字而不是减去数字的原因:返回地址位于高于自动(本地)变量(例如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
的参数--argc
和argv
,但这些在这里不重要。)当函数执行时,堆栈指针指向ret
。返回地址位于ret
的64位“上方”,对应于+ 2
中的“+2”。
ret = (int*)&ret + 2;
它是+2
,因为ret
是一个int*
,而int
是32位的,因此加上2意味着将其设置为比(int*)&ret
高64位的内存位置......如果上面段落中的所有假设都是正确的话,那么这将是返回地址的位置。
旁注:让我用Intel汇编语言演示一下C函数的调用方式(如果我记得正确的话——我不是这个主题的专家,所以我可能会错):
// first, push all function arguments on the stack in reverse order:
push argv
push argc
// then, call the function
// 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
在主函数中,可能会发生以下情况:
// 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
参见:在C语言中,过程调用顺序的说明也提供了另一种解释此主题的方法。