编译C以允许缓冲区溢出

时间:2013-02-09 15:56:19

标签: c compilation buffer-overflow

我正在学习缓冲区溢出,我正在尝试制作缓冲区溢出。我有这段代码:

#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可以被覆盖。

2 个答案:

答案 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情况一样(当然,这会导致崩溃的程序。)