我破坏了哪个 return 地址?
Which return address am I corrupting?
我有一个非常简单的学习堆栈溢出的程序。
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char buf[128];
if(argc < 2) return 1;
strcpy(buf, argv[1]);
printf("Hello\n");
return 0;
}
策略是在 argv[1] 中提供大字符串以溢出 buf 并覆盖 return 地址。但是哪个 return 地址呢?我以为是我进入strcpy之前保存的地址,所以当我们正常从strcpyreturn的时候,我们会执行printf。
但是,在我使用 shell 代码负载溢出缓冲区后,将此 return 地址更改为我的 shell 代码。我看到 printf 仍在执行。就算我多加几个printf,也都执行完了。显然,我更改的return地址只影响主函数return,否则我什至看不到正在执行的printfs。
为什么会这样?是不是当我溢出缓冲区把return地址改成我的shell代码时,主程序会直接跳转到我的shell代码而不执行下一个printf?
buf
是一个函数的变量,所以它存在于栈中。您的缓冲区溢出会损坏堆栈
当程序执行进入main
时,从main
出来的return地址在栈上。接下来,为 buf
分配了 128 个字节。 strcpy
调用的第二个参数长于 128 字节,超出了为 buf
分配的 space,并且可能破坏了 return 地址。
接下来将printf
和return地址的参数(指向printf
后面的语句)压入栈,执行跳转到printf
函数。执行请求的打印后,return 地址被弹出堆栈,并继续执行下一条语句。
终于到了return 0;
语句。分配给堆栈上局部变量的 space 被恢复,并且(损坏的)return 地址从堆栈中弹出,并执行 "returns" 到由字节给定的损坏地址用于破坏内存的字符串。
简而言之,缓冲区溢出只能把已经写在栈上的信息(过去的)涂掉。它不能涂写尚未写入堆栈(未来)的信息。这就是为什么 printf
损坏后的调用仍然可以正常执行的原因;损坏的数据尚未使用。
堆栈,在 strcpy
调用:
xxxx
xxxx
return address from strcpy
&buf (arg to strcpy)
argv[1] (arg to strcpy)
buf[0]
...
buf[127]
return address from main
argc (arg to main)
argv[0] (arg to main)
argv[1] (arg to main)
...
伤害是在 buf[127]
之后造成的。来自 strcpy
的 return 没有损坏。
注:也有可能栈上没有"return from strcpy
";编译器可能已内联函数调用。
在您的典型 PC 上,堆栈向下增长。这意味着在调用 strcpy
:
时堆栈的内存布局将如下所示
// ^^^ higher addresses ^^^
[stuff]
[return address of main]
[buf[127]]
[buf[126]]
...
[buf[1]]
[buf[0]]
[argument 2 (pointer to argv[1])]
[argument 1 (pointer to buf)]
[return address of strcpy (points into main)]
[local variables in strcpy]
// vvv lower addresses vvv
通过溢出 buf
(写入 buf[128]
、buf[129]
等),您可以覆盖 main
的调用帧(最重要的是,main
的 return 地址)。您不能影响 strcpy
的调用框架,因为它在内存中存在于 buf
之前。
我有一个非常简单的学习堆栈溢出的程序。
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char buf[128];
if(argc < 2) return 1;
strcpy(buf, argv[1]);
printf("Hello\n");
return 0;
}
策略是在 argv[1] 中提供大字符串以溢出 buf 并覆盖 return 地址。但是哪个 return 地址呢?我以为是我进入strcpy之前保存的地址,所以当我们正常从strcpyreturn的时候,我们会执行printf。
但是,在我使用 shell 代码负载溢出缓冲区后,将此 return 地址更改为我的 shell 代码。我看到 printf 仍在执行。就算我多加几个printf,也都执行完了。显然,我更改的return地址只影响主函数return,否则我什至看不到正在执行的printfs。
为什么会这样?是不是当我溢出缓冲区把return地址改成我的shell代码时,主程序会直接跳转到我的shell代码而不执行下一个printf?
buf
是一个函数的变量,所以它存在于栈中。您的缓冲区溢出会损坏堆栈
当程序执行进入main
时,从main
出来的return地址在栈上。接下来,为 buf
分配了 128 个字节。 strcpy
调用的第二个参数长于 128 字节,超出了为 buf
分配的 space,并且可能破坏了 return 地址。
接下来将printf
和return地址的参数(指向printf
后面的语句)压入栈,执行跳转到printf
函数。执行请求的打印后,return 地址被弹出堆栈,并继续执行下一条语句。
终于到了return 0;
语句。分配给堆栈上局部变量的 space 被恢复,并且(损坏的)return 地址从堆栈中弹出,并执行 "returns" 到由字节给定的损坏地址用于破坏内存的字符串。
简而言之,缓冲区溢出只能把已经写在栈上的信息(过去的)涂掉。它不能涂写尚未写入堆栈(未来)的信息。这就是为什么 printf
损坏后的调用仍然可以正常执行的原因;损坏的数据尚未使用。
堆栈,在 strcpy
调用:
xxxx
xxxx
return address from strcpy
&buf (arg to strcpy)
argv[1] (arg to strcpy)
buf[0]
...
buf[127]
return address from main
argc (arg to main)
argv[0] (arg to main)
argv[1] (arg to main)
...
伤害是在 buf[127]
之后造成的。来自 strcpy
的 return 没有损坏。
注:也有可能栈上没有"return from strcpy
";编译器可能已内联函数调用。
在您的典型 PC 上,堆栈向下增长。这意味着在调用 strcpy
:
// ^^^ higher addresses ^^^
[stuff]
[return address of main]
[buf[127]]
[buf[126]]
...
[buf[1]]
[buf[0]]
[argument 2 (pointer to argv[1])]
[argument 1 (pointer to buf)]
[return address of strcpy (points into main)]
[local variables in strcpy]
// vvv lower addresses vvv
通过溢出 buf
(写入 buf[128]
、buf[129]
等),您可以覆盖 main
的调用帧(最重要的是,main
的 return 地址)。您不能影响 strcpy
的调用框架,因为它在内存中存在于 buf
之前。