#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
答案 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 auth
,int
还需要16字节的堆栈槽来尊重gcc
的堆栈边界标志。使用gdb
调试程序时,我注意到int auth
地址为BSP-0x04
。 BSP-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 堆栈顶部