Came across this canary technique from http://blog.quarkslab.com/clang-hardening-cheat-sheet.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <stdio.h> #include <string.h> #include <stdint.h> static char c; void f() { size_t top = SIZE_MAX; for (int i = 0; i < 5; ++i) { printf("%p\n", (void*)((size_t*)&top)[i]); } char b[8]; strcpy(b, &c); }
int main() { size_t x = 0; c = '0'; f(); void *bp; asm ("mov %%rbp, %0;" : "=r" (bp) : : ); printf("bp is %p\n", bp); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| $ clang -fstack-protector test.c && ./a.out 0xffffffffffffffff 0x1 0xdeba1e5fb9c19200 0x7ffd419467a0 0x400694 bp is 0x7ffd419467a0 $ objdump -d a.out | grep -C 2 400694 40068e: 30 40068f: e8 4c ff ff ff callq 4005e0 <f> 400694: 48 bf 44 07 40 00 00 movabs $0x400744,%rdi 40069b: 00 00 00 40069e: 48 89 e8 mov %rbp,%rax
|
The memory layout looks like (address goes from low to high):
1 2 3 4 5
| bp-24 : SIZE_MAX bp-16 : char b[8] bp-8 : canary bp : old bp bp+8 : return address
|
The return address is confirmed by the objdump
above. Similarly, we could do the same analysis for the case without stack protector.
1 2 3 4 5 6 7
| $ clang test.c && ./a.out 0xffffffffffffffff 0x7ffe7dc66df0 0x400604 0x400630 # content from previous stack frame, so ignored 0x400470 bp is 0x7ffe7dc66df0
|
The memory layout looks like (address goes from low to high):
1 2 3
| bp-8 : SIZE_MAX bp : old bp bp+8 : return address
|
1 2 3 4 5 6
| $ objdump -d a.out | grep -C 2 400604 4005fe: 30 4005ff: e8 6c ff ff ff callq 400570 <f> 400604: 48 bf b4 06 40 00 00 movabs $0x4006b4,%rdi 40060b: 00 00 00 40060e: 48 89 e8 mov %rbp,%rax
|