I've been reading "Smashing the Stack for Fun and Profit" and seem to be encountering a problem similar to what others have run into the past; however I cannot figure out why my code is still not working.
What I'm trying to do:
Consider the code below:
void function1(int a, int b, int c){
char buffer1[8];
// char buffer2[10];
int *ret;
ret = (int *) buffer1 + 24;
(*ret) += 7;
}
void main() {
int x;
x = 0;
function1(1,2,3);
x = 1;
printf("%d\n", x);
}
The goal of the example is to overwrite the return address of *function1 * and skip the line x = 1 in main. So, the program should output 0 instead of 1. However, my output is still 1. So I'm not sure where I'm going wrong.
Using gdb to calculate the return address and the offsets
From an objdump of the assembly:
00000000000006db <main>:
6db: 55 push %rbp
6dc: 48 89 e5 mov %rsp,%rbp
6df: 48 83 ec 10 sub $0x10,%rsp
6e3: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
6ea: ba 03 00 00 00 mov $0x3,%edx
6ef: be 02 00 00 00 mov $0x2,%esi
6f4: bf 01 00 00 00 mov $0x1,%edi
6f9: e8 b2 ff ff ff callq 6b0 <function1>
6fe: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%rbp)
705: 8b 45 fc mov -0x4(%rbp),%eax
708: 89 c6 mov %eax,%esi
70a: 48 8d 3d 93 00 00 00 lea 0x93(%rip),%rdi # 7a4 <_IO_stdin_used+0x4>
711: b8 00 00 00 00 mov $0x0,%eax
716: e8 45 fe ff ff callq 560 <printf@plt>
71b: 90 nop
71c: c9 leaveq
71d: c3 retq
71e: 66 90 xchg %ax,%ax
Since I'm trying to skip x = 1;
, 0x705 - 0x6fe = 0x7, which I'm confident is correct.
For determining the offset between buffer1 and the return address on the call stack, I inspect gdb:
Breakpoint 1, function1 (a=1, b=2, c=3) at example3.c:12
12 ret = (int *) buffer1 + 24;
(gdb) p $rbp
$1 = (void *) 0x7fffffffea80
(gdb) p &buffer1
$2 = (char (*)[8]) 0x7fffffffea70
The distance between buffer1 and the return address should then be: 0x7fffffffea80 - 0x7fffffffea70 + 8 = 24
I've also verified that the word size is 8 (using arch
I know it's x86_64).
My guess is the return address offset might be different due to something like a stack canary, but how would I find the actual return address then?
EDIT:
The disassembly for function1:
00000000000006b0 <function1>:
6b0: 55 push %rbp
6b1: 48 89 e5 mov %rsp,%rbp
6b4: 89 7d ec mov %edi,-0x14(%rbp)
6b7: 89 75 e8 mov %esi,-0x18(%rbp)
6ba: 89 55 e4 mov %edx,-0x1c(%rbp)
6bd: 48 8d 45 f0 lea -0x10(%rbp),%rax
6c1: 48 83 c0 44 add $0x44,%rax
6c5: 48 89 45 f8 mov %rax,-0x8(%rbp)
6c9: 48 8b 45 f8 mov -0x8(%rbp),%rax
6cd: 8b 00 mov (%rax),%eax
6cf: 8d 50 07 lea 0x7(%rax),%edx
6d2: 48 8b 45 f8 mov -0x8(%rbp),%rax
6d6: 89 10 mov %edx,(%rax)
6d8: 90 nop
6d9: 5d pop %rbp
6da: c3 retq
Not sure if this is your main problem, but I'm looking at
ret = (int *) buffer1 + 24;
Cast has higher precedence than binary +
, so this is ((int *)buffer1) + 24
. The effect is that ret
is set to an address which is 96 bytes higher than that of buffer1
, since sizeof(int) == 4
.
You probably meant to write
ret = (int *)(buffer1 + 24);
Also, the return address is 64 bits, and int
is 32 bits. So you probably wanted to declare ret
as unsigned long *
instead of int *
, and change the cast accordingly, so that your *ret += 7
is a 64-bit add. It's unlikely to actually make a difference because of alignments, but still it will look more correct.