显然,这段代码试图改变堆栈,以便当main 函数返回时,程序执行不会定期返回到运行时库(这通常会终止程序),而是跳转到保存在shellcode 数组。
1) int *ret;
在堆栈上定义一个变量,就在main 函数的参数下方。
2) ret = (int *)&ret + 2;
让ret 变量指向位于堆栈上ret 上方两个ints 的int *。假设这是返回地址所在的位置,当main 返回时程序将继续运行。
2) (*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 而不是任何其他数字:显然是因为此代码假定返回地址将位于堆栈上的该位置。考虑以下几点:
此代码假定当有东西被压入时调用堆栈会向下增长(确实如此,例如使用 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 的参数——argc 和argv——但这些在这里并不重要。)当函数执行时,堆栈指针指向ret。返回地址位于ret“上方”的64位,对应于+ 2中
ret = (int*)&ret + 2;
它是+ 2,因为ret 是int*,而int 是32 位的,因此加2 表示将其设置为比(int*)&ret 高2 × 32 位(=64 位)的内存位置...如果上一段中的所有假设都是正确的,这将是返回地址的位置。
Excursion:让我用英特尔汇编语言演示如何调用 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 的描述以获取对该主题的另一种解释。