将 C 函数返回给它的祖父

逆向工程 C 数据库 x64 调用栈
2021-06-24 21:49:15

这是一个家庭作业,所以如果我能得到一个提示,而不是一个完整的答案,我将不胜感激。我写了这个程序,它应该打印以下内容:

Executing function_a
Executing function_b
Finished!

main()function_a()功能给予,而我只能更改function_b()的标记部分。

#include <stdio.h>
#include <stdint.h>

void function_b(void) {
    char buffer[4];

    // edit between here...
    uint32_t * x = &buffer;
    while (*(x++) != 0xa0b1c2d3);           // Find the beacon
    *(uint32_t*)(&buffer + 6) = *(x + 2);   // Copy return address from caller
    *(uint32_t*)(&buffer + 5) = *(x + 1);   // Copy frame pointer from caller
    // ... and here

    fprintf(stdout, "Executing function_b\n");
}

void function_a(void) {
    int beacon = 0xa0b1c2d3;
    fprintf(stdout, "Executing function_a\n");
    function_b();
    fprintf(stdout, "Executed function_b\n");
}

int main(void) {
    function_a();
    fprintf(stdout, "Finished!\n");
    return 0;
}

问题当然是确保不输出“已执行的函数_b”。我们必须操纵堆栈,以便在返回时function_b()我们不会回到它的实际父级,function_a()而是返回到它的祖父main()

我写的那部分代码从中找到信标function_a(),然后将 的返回地址和保存的帧指针复制function_a()function_b(). 我的程序执行以下操作:

Executing function_a
Executing function_b
Finished!
Segmentation fault (core dumped)

所以它做正确的事情,除了段错误。main()函数返回时失败我用 gdb 来得到这个:

(gdb) run
Starting program: /.../exercise2c
Executing function_a

Breakpoint 1, function_b () at exercise2c.c:9
9       *(uint32_t*)(&buffer + 6) = *(x + 2);   // Copy return address from caller
(gdb) backtrace
#0  function_b () at exercise2c.c:9
#1  0x0000000000400607 in function_a () at exercise2c.c:18
#2  0x0000000000400630 in main () at exercise2c.c:23
(gdb) info frame
Stack level 0, frame at 0x7fffffffdcb0:
 rip = 0x400593 in function_b (exercise2c.c:9); saved rip = 0x400607
 called by frame at 0x7fffffffdcd0
 source language c.
 Arglist at 0x7fffffffdca0, args: 
 Locals at 0x7fffffffdca0, Previous frame's sp is 0x7fffffffdcb0
 Saved registers:
  rbp at 0x7fffffffdca0, rip at 0x7fffffffdca8
(gdb) frame 1
#1  0x0000000000400607 in function_a () at exercise2c.c:18
18      function_b();
(gdb) info frame
Stack level 1, frame at 0x7fffffffdcd0:
 rip = 0x400607 in function_a (exercise2c.c:18); saved rip = 0x400630
 called by frame at 0x7fffffffdce0, caller of frame at 0x7fffffffdcb0
 source language c.
 Arglist at 0x7fffffffdcc0, args: 
 Locals at 0x7fffffffdcc0, Previous frame's sp is 0x7fffffffdcd0
 Saved registers:
  rbp at 0x7fffffffdcc0, rip at 0x7fffffffdcc8
(gdb) step
10      *(uint32_t*)(&buffer + 5) = *(x + 1);   // Copy frame pointer from caller
(gdb) step
12      fprintf(stdout, "Executing function_b\n");
(gdb) frame 0
#0  function_b () at exercise2c.c:12
12      fprintf(stdout, "Executing function_b\n");
(gdb) info frame
Stack level 0, frame at 0x7fffffffdcb0:
 rip = 0x4005b5 in function_b (exercise2c.c:12); saved rip = 0x400630
 called by frame at 0x7fffffffdcd0
 source language c.
 Arglist at 0x7fffffffdca0, args: 
 Locals at 0x7fffffffdca0, Previous frame's sp is 0x7fffffffdcb0
 Saved registers:
  rbp at 0x7fffffffdca0, rip at 0x7fffffffdca8
(gdb) backtrace
#0  function_b () at exercise2c.c:12
#1  0x0000000000400630 in main () at exercise2c.c:23
(gdb) continue
Continuing.
Executing function_b
Finished!

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400654 in main () at exercise2c.c:26
26  }(gdb) 
Continuing.

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.

您可以清楚地看到function_b()确实返回到main(),但出于某种原因,我不了解main()崩溃。我能看到这种情况发生的唯一方法是我弄乱了 的堆栈框架main(),我没有,或者我应该在那里改变一些东西 - 但我不知道是什么。

这里发生了什么?

注意:程序是用 GCC 和 flags 编译的-g -fno-omit-frame-pointer -fno-stack-protector我在 64 位机器上。

2个回答

我在 64 位机器上。

您的代码function_b()将指针视为 32 位值而不是 64 位值。您应该使用uint64_t*而不是uint32_t*.

正如 Jason 所写,我应该使用 64 位指针。除此之外,事实证明我必须从堆栈帧中复制四个数字。这是工作代码:

uint64_t * my_ptr = &buf;
int * x = my_ptr;

while (*(++x)  != 0xa0b1c2d3);  // Find the beacon. We could of course simply have a look
uint64_t * y = x + 1;           // with gdb where it's stored, but this works generically.

*(my_ptr+7) = *(y+3);           // Copy frame information
*(my_ptr+6) = *(y+2);
*(my_ptr+5) = *(y+1);
*(my_ptr+4) = *y;

通常,您需要稍微修改一下必须复制的地址。您可以像以前一样使用 gdb 获得一个粗略的想法。确切的数字取决于函数及其变量。