缓冲区溢出在x86上需要16个字节,在x64上需要29个字节

时间:2017-01-08 20:08:57

标签: c security stack x86-64 buffer-overflow

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
  char buff[15];
  int auth = 0;

  printf("\nEnter password: ");
  gets(buff);

  if (strcmp(buff, "password") != 0) {
    printf("\nAccess denied\n");
  } else {
    auth = 1;
  }

  if (auth) {
    printf("\nAccess granted\n");
  }

  return 0;
}

这段代码需要16个字节(用户输入的字符)才能在x86上溢出auth并打印&#34;访问被授予&#34;。在x64上,需要29个字节才能执行相同操作。为什么是这样?似乎我的变量之间存在一些填充或者它们之间存在其他东西的地址。我不相信这是影子空间的影响(这也适用于* nix吗?)因为只有前32个字节被保留https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention

请注意,我没有使用任何优化来编译它,以避免事情进入寄存器。

我使用GCC 6.2.0在OS X上。这是x86版本的程序集输出:

    .cstring
LC0:
    .ascii "\12Enter password: \0"
LC1:
    .ascii "password\0"
LC2:
    .ascii "\12Access denied\0"
LC3:
    .ascii "\12Access granted\0"
    .text
    .globl _main
_main:
LFB1:
    pushl   %ebp
LCFI0:
    movl    %esp, %ebp
LCFI1:
    pushl   %ebx
    subl    $36, %esp
LCFI2:
    call    ___x86.get_pc_thunk.bx
L1$pb:
    movl    $0, -12(%ebp)
    subl    $12, %esp
    leal    LC0-L1$pb(%ebx), %eax
    pushl   %eax
    call    _printf
    addl    $16, %esp
    subl    $12, %esp
    leal    -27(%ebp), %eax
    pushl   %eax
    call    _gets
    addl    $16, %esp
    subl    $8, %esp
    leal    LC1-L1$pb(%ebx), %eax
    pushl   %eax
    leal    -27(%ebp), %eax
    pushl   %eax
    call    _strcmp
    addl    $16, %esp
    testl   %eax, %eax
    je  L2
    subl    $12, %esp
    leal    LC2-L1$pb(%ebx), %eax
    pushl   %eax
    call    _puts
    addl    $16, %esp
    jmp L3
L2:
    movl    $1, -12(%ebp)
L3:
    cmpl    $0, -12(%ebp)
    je  L4
    subl    $12, %esp
    leal    LC3-L1$pb(%ebx), %eax
    pushl   %eax
    call    _puts
    addl    $16, %esp
L4:
    movl    $0, %eax
    movl    -4(%ebp), %ebx
    leave
LCFI3:
    ret
LFE1:
    .section __TEXT,__textcoal_nt,coalesced,pure_instructions
    .weak_definition    ___x86.get_pc_thunk.bx
    .private_extern ___x86.get_pc_thunk.bx
___x86.get_pc_thunk.bx:
LFB2:
    movl    (%esp), %ebx
    ret
LFE2:
    .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame1:
    .set L$set$0,LECIE1-LSCIE1
    .long L$set$0
LSCIE1:
    .long   0
    .byte   0x1
    .ascii "zR\0"
    .byte   0x1
    .byte   0x7c
    .byte   0x8
    .byte   0x1
    .byte   0x10
    .byte   0xc
    .byte   0x5
    .byte   0x4
    .byte   0x88
    .byte   0x1
    .align 2
LECIE1:
LSFDE1:
    .set L$set$1,LEFDE1-LASFDE1
    .long L$set$1
LASFDE1:
    .long   LASFDE1-EH_frame1
    .long   LFB1-.
    .set L$set$2,LFE1-LFB1
    .long L$set$2
    .byte   0
    .byte   0x4
    .set L$set$3,LCFI0-LFB1
    .long L$set$3
    .byte   0xe
    .byte   0x8
    .byte   0x84
    .byte   0x2
    .byte   0x4
    .set L$set$4,LCFI1-LCFI0
    .long L$set$4
    .byte   0xd
    .byte   0x4
    .byte   0x4
    .set L$set$5,LCFI2-LCFI1
    .long L$set$5
    .byte   0x83
    .byte   0x3
    .byte   0x4
    .set L$set$6,LCFI3-LCFI2
    .long L$set$6
    .byte   0xc4
    .byte   0xc3
    .byte   0xc
    .byte   0x5
    .byte   0x4
    .align 2
LEFDE1:
LSFDE3:
    .set L$set$7,LEFDE3-LASFDE3
    .long L$set$7
LASFDE3:
    .long   LASFDE3-EH_frame1
    .long   LFB2-.
    .set L$set$8,LFE2-LFB2
    .long L$set$8
    .byte   0
    .align 2
LEFDE3:
    .subsections_via_symbols

对于x64版本:

    .cstring
LC0:
    .ascii "\12Enter password: \0"
LC1:
    .ascii "password\0"
LC2:
    .ascii "\12Access denied\0"
LC3:
    .ascii "\12Access granted\0"
    .text
    .globl _main
_main:
LFB1:
    pushq   %rbp
LCFI0:
    movq    %rsp, %rbp
LCFI1:
    subq    $48, %rsp
    movl    %edi, -36(%rbp)
    movq    %rsi, -48(%rbp)
    movl    $0, -4(%rbp)
    leaq    LC0(%rip), %rdi
    movl    $0, %eax
    call    _printf
    leaq    -32(%rbp), %rax
    movq    %rax, %rdi
    call    _gets
    leaq    -32(%rbp), %rax
    leaq    LC1(%rip), %rsi
    movq    %rax, %rdi
    call    _strcmp
    testl   %eax, %eax
    je  L2
    leaq    LC2(%rip), %rdi
    call    _puts
    jmp L3
L2:
    movl    $1, -4(%rbp)
L3:
    cmpl    $0, -4(%rbp)
    je  L4
    leaq    LC3(%rip), %rdi
    call    _puts
L4:
    movl    $0, %eax
    leave
LCFI2:
    ret
LFE1:
    .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame1:
    .set L$set$0,LECIE1-LSCIE1
    .long L$set$0
LSCIE1:
    .long   0
    .byte   0x1
    .ascii "zR\0"
    .byte   0x1
    .byte   0x78
    .byte   0x10
    .byte   0x1
    .byte   0x10
    .byte   0xc
    .byte   0x7
    .byte   0x8
    .byte   0x90
    .byte   0x1
    .align 3
LECIE1:
LSFDE1:
    .set L$set$1,LEFDE1-LASFDE1
    .long L$set$1
LASFDE1:
    .long   LASFDE1-EH_frame1
    .quad   LFB1-.
    .set L$set$2,LFE1-LFB1
    .quad L$set$2
    .byte   0
    .byte   0x4
    .set L$set$3,LCFI0-LFB1
    .long L$set$3
    .byte   0xe
    .byte   0x10
    .byte   0x86
    .byte   0x2
    .byte   0x4
    .set L$set$4,LCFI1-LCFI0
    .long L$set$4
    .byte   0xd
    .byte   0x6
    .byte   0x4
    .set L$set$5,LCFI2-LCFI1
    .long L$set$5
    .byte   0xc
    .byte   0x7
    .byte   0x8
    .align 3
LEFDE1:
    .subsections_via_symbols

1 个答案:

答案 0 :(得分:2)

我再次考虑这个问题,并记得我在聊天时发送给你的编译器标志:

-mpreferred-stack-boundary=num

特别是以下段落:

  

尝试保持堆栈边界对齐2到num字节边界。如果未指定-mpreferred-stack-boundary,默认值为4(16字节或128位)

     

警告:为禁用SSE扩展的x86-64体系结构生成代码时,可以使用-mpreferred-stack-boundary = 3来保持堆栈边界与8字节边界对齐。 由于x86-64 ABI需要16字节堆栈对齐,因此这是ABI不兼容的,并且打算在受控环境中使用,其中堆栈空间是重要的限制。

因此,15字节数组需要一个16字节的堆栈槽来保持它在堆栈边界内。

假设4字节int authint还需要16字节的堆栈槽来尊重gcc的堆栈边界标志。使用gdb调试程序时,我注意到int auth地址为BSP-0x04BSP-0x04最多BSP-0x10填充,因此它适合堆栈槽。因此要溢出缓冲区直到达到int auth,您需要15个字节(缓冲区大小)+ 1个字节(缓冲区填充)+ 12个字节(int auth填充)+ 1个字节才能到达{{1 }}

最后,我提到你在调试时我在数组和int之间找到了一个地址。它可能是内存上的一些垃圾:由于程序并不关心int之前的额外12个字节,它可能不会清理它并且它可能已被用于存储一些内存指针。

下面是x64代码程序堆栈的表示。

主堆栈

----------------------------------------------- -------
保存的RBP
-------------------------------------------------- ----新RBP = 0x7FFFFFFFDD10
AUTH
-------------------------------------------------- ---- RBP - 0x04 = 0x7FFFFFFFDD0C
auth 12字节填充
-------------------------------------------------- ---- RBP - 0x10 = 0x7FFFFFFFDD00
buff 1字节填充
-------------------------------------------------- ---- RBP - 0x11 = 0x7FFFFFFFDCFF
浅黄色
-------------------------------------------------- ---- RBP - 0x20 = 0x7FFFFFFFDCF0
EDI值
-------------------------------------------------- ---- RBP - 0x24 = 0x7FFFFFFFDCEC
RSI的值
-------------------------------------------------- ---- RBP - 0x30 = 0x7FFFFFFFDCE0 堆栈顶部