9

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

#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

我用谷歌搜索了这个并将-fno-stack-protector标志添加到gcc,这允许 RBP 被覆盖0x4141414141414141但没有别的。

我想知道是否有办法编译代码以便可以覆盖 RIP。

4

2 回答 2

3

如果您使用-fno-stack-protector. 您在 GDB 中看不到RIP值的原因是在更新0x4141414141414141之前引发了一般保护错误。RIP(如果发生页面错误,GPF 处理程序通常从交换加载页面并从失败的指令开始恢复执行。)

于 2013-02-10T21:54:49.233 回答
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 情况一样调用了页面错误处理程序(当然,这会导致程序碰撞)。

于 2013-05-07T01:29:37.113 回答