简体   繁体   English

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

[英]Buffer overflow needs 16 bytes on x86 but 29 bytes on x64

#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;
}

This piece of code needs 16 bytes (characters input by the user) to overflow auth on x86 and print "Access granted". 这段代码需要16个字节(用户输入的字符)才能在x86上溢出auth并显示“ Access grant”。 On x64, 29 bytes are required to do the same. 在x64上,需要29个字节才能执行相同操作。 Why is this? 为什么是这样? It seems like there is either some padding going on between my variables or there's an address for something else in between them. 似乎在我的变量之间存在一些填充,或者在它们之间存在其他地址。 I don't believe this is the effect of shadow space (does this apply on *nix too?) since only the first 32 bytes are reserved https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention 我不相信这是阴影空间的影响(这是否也适用于* nix吗?),因为仅保留了前32个字节https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention

Note that I'm not compiling this with any optimizations to avoid things being inside of a register. 请注意,我不会对此进行任何优化来避免将其放入寄存器中。

I'm on OS X using GCC 6.2.0. 我在使用GCC 6.2.0的OS X上。 This is the assembly output for the x86 version: 这是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

And for the x64 version: 对于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

I was thinking about this issue again and remembered about the compiler flag I sent you on chat: 我再次考虑此问题,并想起了我在聊天中向您发送的编译器标志:

-mpreferred-stack-boundary=num

Particularly the following paragraphs: 特别是以下几段:

Attempt to keep the stack boundary aligned to a 2 raised to num byte boundary. 尝试使堆栈边界与2对齐,以提高到num字节边界。 If -mpreferred-stack-boundary is not specified, the default is 4 (16 bytes or 128 bits) . 如果未指定-mpreferred-stack-boundary, 则默认值为4(16字节或128位)

Warning: When generating code for the x86-64 architecture with SSE extensions disabled, -mpreferred-stack-boundary=3 can be used to keep the stack boundary aligned to 8 byte boundary. 警告:当为禁用SSE扩展的x86-64体系结构生成代码时,可以使用-mpreferred-stack-boundary = 3来使堆栈边界与8字节边界对齐。 Since x86-64 ABI require 16 byte stack alignment, this is ABI incompatible and intended to be used in controlled environment where stack space is important limitation. 由于x86-64 ABI需要16字节的堆栈对齐,因此这是ABI不兼容的,旨在用于对堆栈空间有重要限制的受控环境中。

So, the 15 bytes array would require a 16 bytes stack-slot to keep it inside the stack boundary. 因此,15个字节的数组将需要16个字节的堆栈插槽,以使其保持在堆栈边界之内。

The int auth , assuming a 4 byte int , would also require a 16 bytes stack-slot to respect gcc s stack boundary flag. int auth ,假定一个4字节int ,也将需要16个字节堆栈槽尊重gcc的堆栈边界标志。 Debugging the program with gdb , I noticed the int auth address is BSP-0x04 . gdb调试程序,我注意到int auth地址是BSP-0x04 BSP-0x04 up to BSP-0x10 is padding so it fits the stack-slot. BSP-0x04直到BSP-0x10都处于填充状态,因此适合堆栈插槽。 So to overflow the buffer until you reach the int auth you would need 15 bytes (buffer size) + 1 byte (buffer padding) + 12 bytes ( int auth padding) + 1 byte to reach the int . 因此,要使缓冲区溢出直到到达int auth您将需要15个字节(缓冲区大小)+1个字节(缓冲区填充)+ 12个字节( int auth填充)+1个字节才能到达int

Finally, I mentioned to you I found a address between the array and the int while debugging. 最后,我向您提到在调试时我在数组和int之间找到了一个地址。 It's probably some junk leftover on memory: Since the program doesn't care about the extra 12 bytes before the int , it probably doesn't clean it and it could have been used to store some memory pointer. 这可能是内存上的一些垃圾:由于程序并不关心int之前的额外12个字节,因此它可能不会清除它,并且它可能已被用来存储一些内存指针。

Below there is a representation of the stack of the x64 code program. 下面是x64代码程序堆栈的表示。

Main Stack 主栈

------------------------------------------------------ -------------------------------------------------- ----
Saved RBP 保存的RBP
------------------------------------------------------ New RBP = 0x7FFFFFFFDD10 -------------------------------------------------- ----新的RBP = 0x7FFFFFFFDD10
auth AUTH
------------------------------------------------------ RBP - 0x04 = 0x7FFFFFFFDD0C -------------------------------------------------- ---- RBP-0x04 = 0x7FFFFFFFDD0C
auth 12 bytes padding 认证12字节填充
------------------------------------------------------ RBP - 0x10 = 0x7FFFFFFFDD00 -------------------------------------------------- ---- RBP-0x10 = 0x7FFFFFFFDD00
buff 1 byte padding buff 1字节填充
------------------------------------------------------ RBP - 0x11 = 0x7FFFFFFFDCFF -------------------------------------------------- ---- RBP-0x11 = 0x7FFFFFFFDCFF
buff 浅黄色
------------------------------------------------------ RBP - 0x20 = 0x7FFFFFFFDCF0 -------------------------------------------------- ---- RBP-0x20 = 0x7FFFFFFFDCF0
Value of EDI EDI的价值
------------------------------------------------------ RBP - 0x24 = 0x7FFFFFFFDCEC -------------------------------------------------- ---- RBP-0x24 = 0x7FFFFFFFDCEC
Value of RSI RSI的价值
------------------------------------------------------ RBP - 0x30 = 0x7FFFFFFFDCE0 Top of the Stack -------------------------------------------------- ---- RBP-0x30 = 0x7FFFFFFFDCE0 堆栈顶部

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM