【问题标题】:Can't change return address of the function x64无法更改函数 x64 的返回地址
【发布时间】:2019-10-04 17:42:17
【问题描述】:

我正在尝试更改 C 中某些函数的返回地址以跳过一条指令。我正在使用 Ubuntu Server 的虚拟机上执行此操作(因为在 Mac gcc 上不允许我关闭堆栈保护)。

我正在用 gcc 编译我的代码:

gcc –g –fno-stack-protector –z execstack –o bufover bufover.c

这是代码:

void foo(int a, int b, int c) {
   char buff[256];
   long *ret, *ret2;

   ret = buff + 256 + 8;
   (*ret) += 5; 
}

int main() {
  char x;
  x = '0';
  foo(1,2,3);
  x = '1';
  printf("%c\n",x);
}

buff 的地址我添加了 256(buff 的大小)和 8(%RBP 的大小)。在此之前的堆栈上应该是返回地址。 接下来,我在地址上添加了 5 个字节,因为我使用 gdb 检查了下一条指令在 5 个字节中。

但它不起作用...... 我正在使用 gdb 逐步分析变量(地址),但我没有看到任何错误。 有什么想法吗?

编辑:汇编代码:

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 14
    .globl  _foo                    ## -- Begin function foo
    .p2align    4, 0x90
_foo:                                   ## @foo
Lfunc_begin0:
    .file   1 "me.c"
    .loc    1 3 0                   ## me.c:3:0
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $160, %rsp
    leaq    -272(%rbp), %rax
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    %edx, -12(%rbp)
Ltmp0:
    ##DEBUG_VALUE: foo:buff <- [%rax+0]
    .loc    1 7 19 prologue_end     ## me.c:7:19
    addq    $256, %rax              ## imm = 0x100
Ltmp1:
    .loc    1 7 25 is_stmt 0        ## me.c:7:25
    addq    $8, %rax
    .loc    1 7 12                  ## me.c:7:12
    movq    %rax, -280(%rbp)
    .loc    1 8 10 is_stmt 1        ## me.c:8:10
    movq    -280(%rbp), %rax
    .loc    1 8 15 is_stmt 0        ## me.c:8:15
    movq    (%rax), %rcx
    addq    $5, %rcx
    movq    %rcx, (%rax)
    .loc    1 9 5 is_stmt 1         ## me.c:9:5
    addq    $160, %rsp
    popq    %rbp
    retq
Ltmp2:
Lfunc_end0:
    .cfi_endproc
                                        ## -- End function
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
Lfunc_begin1:
    .loc    1 11 0                  ## me.c:11:0
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movl    $1, %edi
    movl    $2, %esi
    movl    $3, %edx
Ltmp3:
    .loc    1 13 9 prologue_end     ## me.c:13:9
    movb    $48, -1(%rbp)
    .loc    1 14 7                  ## me.c:14:7
    callq   _foo
    leaq    L_.str(%rip), %rdi
    .loc    1 15 9                  ## me.c:15:9
    movb    $49, -1(%rbp)
    .loc    1 16 21                 ## me.c:16:21
    movsbl  -1(%rbp), %esi
    .loc    1 16 7 is_stmt 0        ## me.c:16:7
    movb    $0, %al
    callq   _printf
    xorl    %edx, %edx
    .loc    1 17 5 is_stmt 1        ## me.c:17:5
    movl    %eax, -8(%rbp)          ## 4-byte Spill
    movl    %edx, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
Ltmp4:
Lfunc_end1:
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "%c\n"

    .section    __DWARF,__debug_str,regular,debug

【问题讨论】:

  • 这在标准 C 中不可能安全地完成。您必须使用汇编程序或非标准扩展。
  • www-inst.eecs.berkeley.edu/~cs161/fa08/papers/… 第 5 页,这是我的基地。它说有可能
  • 使用-S 编译并将生成的程序集粘贴到问题中。
  • @demoo “它说有可能”:这基本上意味着:在作者当时使用的特定平台上的特定编译器是可能的已经写好了。
  • @Jabberwocky 可能你是对的,但我不想放弃。我也将 /proc/sys/kernel/randomize_va_space 从 2 更改为 0。结果还是一样...

标签: c gdb stack buffer-overflow


【解决方案1】:

您应该使用 GCC return-address related 内置函数,例如 __builtin_frame_address__builtin_return_address,并且您应该仔细研究 x86-64 ABI specification 以了解详细信息相关的 x86 calling conventions

也尝试通过在foo.c 中编写一些C 代码并使用gcc -O -fverbose-asm -S foo.c 编译它然后查看生成的foo.s 来理解它们

最后,堆栈段通常是不可执行的(这对于某些trampoline 技术可能很重要)。阅读NX bit。在 Linux 上,学习使用mprotect(2)mmap(2)backtrace(3)

无法保证 GCC 甚至使用任何 call stack。它可以优化以避免使用它(tail-call 优化可能有时会发生),并且您的代码甚至可能不需要额外的调用帧。所以当然你不能在标准 C 中实现你的目标,或者没有对你的特定 GCC 编译器的额外假设(并且 GCC 8 和 GCC 9 可以进行不同的优化)。

当然改回地址是undefined behavior

【讨论】:

  • 代码并没有尝试执行堆栈,只是通过添加可能存在的返回地址来修改其内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多