我正在学习缓冲区溢出,我正在尝试制作缓冲区溢出。我有这段代码:
#include <stdio.h>
char *secret = "password";
void go_shell() {
char *shell = "/bin/sh";
char *cmd[] = { "/bin/sh", 0 };
setreuid(0);
execve(shell,cmd,0);
}
int authorize() {
char password[64];
printf("Enter Password: ");
gets(password);
if (!strcmp(password,secret)) {
return 1;
}
else {
return 0;
}
}
int main() {
if (authorize()) {
printf("login successful\n");
go_shell();
} else {
printf("Incorrect password\n");
}
return 0;
}
我用gcc编译它,然后在gdb中运行它
我输入大约100“A”作为密码,程序崩溃。
问题是没有注册被覆盖到0x4141414141414141
我用Google搜索并将-fno-stack-protector
标记添加到gcc
,这样就可以将RBP覆盖到0x4141414141414141
,但不能覆盖其他内容。
我想知道是否有办法编译代码以便RIP可以被覆盖。
答案 0 :(得分:3)
如果使用-fno-stack-protector
进行编译,您的代码已经完成了您想要的操作。您在GDB中看不到RIP
值为0x4141414141414141
的原因是在RIP
更新之前抛出了一般保护错误。 (如果发生页面错误,GPF处理程序通常从交换加载页面,并从失败的指令开始继续执行。)
答案 1 :(得分:1)
您在x32上遇到EIP 0×41414141崩溃的原因是,当程序将先前保存的EIP值从堆栈弹出并返回到EIP时,CPU然后尝试执行内存地址0×41414141处的指令,这会导致一个段错误。 (它必须在执行之前获取页面)
现在,在x64执行期间,当程序将先前保存的RIP值弹回到RIP寄存器时,内核然后尝试执行存储器地址0×4141414141414141处的指令。首先,由于规范形式寻址,任何虚拟地址的位48到63必须是位47的副本(以类似于符号扩展的方式),否则处理器将引发异常。如果这不是问题 - 内核在调用页面错误处理程序之前会执行额外的检查,因为最大用户空间地址是0x00007FFFFFFFFFF。
回顾一下,在x32体系结构中,地址是在没有任何“验证”的情况下传递给页面错误处理程序的,它试图加载触发内核发送程序段错误的页面,但是x64没有达到这个目的。
测试它,用0×0000414141414141覆盖RIP,你会看到预期值放在RIP中,因为内核预先检查通过,然后调用页面错误处理程序,就像x32情况一样(当然,这会导致崩溃的程序。)