Reputation: 10848
#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". On x64, 29 bytes are required to do the same. 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
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. This is the assembly output for the x86 version:
.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:
.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
Upvotes: 2
Views: 544
Reputation: 2058
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. If -mpreferred-stack-boundary is not specified, the default is 4 (16 bytes or 128 bits).
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. 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.
So, the 15 bytes array would require a 16 bytes stack-slot to keep it inside the stack boundary.
The int auth
, assuming a 4 byte int
, would also require a 16 bytes stack-slot to respect gcc
s stack boundary flag. Debugging the program with gdb
, I noticed the int auth
address is BSP-0x04
. BSP-0x04
up to BSP-0x10
is padding so it fits the stack-slot. 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
.
Finally, I mentioned to you I found a address between the array and the int
while debugging. 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.
Below there is a representation of the stack of the x64 code program.
------------------------------------------------------
Saved RBP
------------------------------------------------------ New RBP = 0x7FFFFFFFDD10
auth
------------------------------------------------------ RBP - 0x04 = 0x7FFFFFFFDD0C
auth 12 bytes padding
------------------------------------------------------ RBP - 0x10 = 0x7FFFFFFFDD00
buff 1 byte padding
------------------------------------------------------ RBP - 0x11 = 0x7FFFFFFFDCFF
buff
------------------------------------------------------ RBP - 0x20 = 0x7FFFFFFFDCF0
Value of EDI
------------------------------------------------------ RBP - 0x24 = 0x7FFFFFFFDCEC
Value of RSI
------------------------------------------------------ RBP - 0x30 = 0x7FFFFFFFDCE0 Top of the Stack
Upvotes: 2