So for my class project I am given a binary and what I have to do is bypass the security authentication. Now, once you change the binary to bypass the authentication, you have to create a C program that will replicate the "same" binary. Now, I have been struggling to understand what this area of the assembly code dump that I obtained does.
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
I'm not very familiar with assembly, but I got most of it figured out. This is the original/entire assembly dump that I got using GDB.
0x08048a23 <function8+0>: push %ebp
0x08048a24 <function8+1>: mov %esp,%ebp
0x08048a26 <function8+3>: sub $0x28,%esp
0x08048a29 <function8+6>: movl $0xd6a1a,-0x18(%ebp)
0x08048a30 <function8+13>: mov 0x8(%ebp),%eax
0x08048a33 <function8+16>: mov %eax,-0x14(%ebp)
0x08048a36 <function8+19>: mov 0xc(%ebp),%eax
0x08048a39 <function8+22>: mov %eax,-0x10(%ebp)
0x08048a3c <function8+25>: movl $0x0,-0xc(%ebp)
0x08048a43 <function8+32>: mov -0xc(%ebp),%eax
0x08048a46 <function8+35>: mov %eax,%edx
0x08048a48 <function8+37>: sar $0x1f,%edx
0x08048a4b <function8+40>: idivl -0x18(%ebp)
0x08048a4e <function8+43>: imul -0x14(%ebp),%eax
0x08048a52 <function8+47>: imul -0x10(%ebp),%eax
0x08048a56 <function8+51>: mov %eax,-0xc(%ebp)
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
0x08048a6b <function8+72>: mov %eax,(%esp)
0x08048a6e <function8+75>: call 0x8048434 <strcmp#plt>
0x08048a73 <function8+80>: test %eax,%eax
0x08048a75 <function8+82>: jne 0x8048a8d <function8+106>
0x08048a77 <function8+84>: mov $0x8048cdc,%eax
0x08048a7c <function8+89>: mov -0xc(%ebp),%edx
0x08048a7f <function8+92>: mov %edx,0x4(%esp)
0x08048a83 <function8+96>: mov %eax,(%esp)
0x08048a86 <function8+99>: call 0x8048414 <printf#plt>
0x08048a8b <function8+104>: jmp 0x8048a99 <function8+118>
0x08048a8d <function8+106>: movl $0x8048cfa,(%esp)
0x08048a94 <function8+113>: call 0x8048424 <puts#plt>
0x08048a99 <function8+118>: mov -0xc(%ebp),%eax
0x08048a9c <function8+121>: leave
0x08048a9d <function8+122>: ret
And so far I have managed to convert it to this in C:
int function8(one, two){
int a = 879130;
int b = one;
int c = two;
int d = 0;
d = (d / a * b * c);
if(strcmp(b, (d + 4)) == 0){
printf("You may enter using token %d", d);
}
else{
puts("You may not enter.");
}
return d;
}
int main(){
int one, two = 0;
function8(one, two);
}
I am know that
0x08048a59 <function8+54>: mov 0x8049e50,%eax
and
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
are pointing to a particular address and value respectively (correct me if I'm wrong), but don't know if I have to call it or not. If so, how do I call that particular address?
Any help would be appreciated!
Just in case you guys need it, this is my current output when I create a binary using GCC and then run GDB to get the dump:
0x08048434 <function8+0>: push %ebp
0x08048435 <function8+1>: mov %esp,%ebp
0x08048437 <function8+3>: sub $0x28,%esp
0x0804843a <function8+6>: movl $0xd6a1a,-0x18(%ebp)
0x08048441 <function8+13>: mov 0x8(%ebp),%eax
0x08048444 <function8+16>: mov %eax,-0x14(%ebp)
0x08048447 <function8+19>: mov 0xc(%ebp),%eax
0x0804844a <function8+22>: mov %eax,-0x10(%ebp)
0x0804844d <function8+25>: movl $0x0,-0xc(%ebp)
0x08048454 <function8+32>: mov -0xc(%ebp),%eax
0x08048457 <function8+35>: mov %eax,%edx
0x08048459 <function8+37>: sar $0x1f,%edx
0x0804845c <function8+40>: idivl -0x18(%ebp)
0x0804845f <function8+43>: imul -0x14(%ebp),%eax
0x08048463 <function8+47>: imul -0x10(%ebp),%eax
0x08048467 <function8+51>: mov %eax,-0xc(%ebp)
0x0804846a <function8+54>: mov -0xc(%ebp),%eax
0x0804846d <function8+57>: add $0x4,%eax
0x08048470 <function8+60>: mov %eax,0x4(%esp)
0x08048474 <function8+64>: mov -0x14(%ebp),%eax
0x08048477 <function8+67>: mov %eax,(%esp)
0x0804847a <function8+70>: call 0x8048364 <strcmp#plt>
0x0804847f <function8+75>: test %eax,%eax
0x08048481 <function8+77>: jne 0x8048499 <function8+101>
0x08048483 <function8+79>: mov $0x80485a0,%eax
0x08048488 <function8+84>: mov -0xc(%ebp),%edx
0x0804848b <function8+87>: mov %edx,0x4(%esp)
0x0804848f <function8+91>: mov %eax,(%esp)
0x08048492 <function8+94>: call 0x8048344 <printf#plt>
0x08048497 <function8+99>: jmp 0x80484a5 <function8+113>
0x08048499 <function8+101>: movl $0x80485bd,(%esp)
0x080484a0 <function8+108>: call 0x8048354 <puts#plt>
0x080484a5 <function8+113>: mov -0xc(%ebp),%eax
0x080484a8 <function8+116>: leave
0x080484a9 <function8+117>: ret
strcmp compares two strings that are passed in as pointers. The code here :
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
0x08048a6b <function8+72>: mov %eax,(%esp)
0x08048a6e <function8+75>: call 0x8048434 <strcmp#plt>
is passing two pointers to strcmp, both of which are static/global data (not local, like you have in your C code). One is at 0x8048cd4 (that's the address of the string.. it's probably something in quotes : "example"). The other is a pointer load + 4 that's dereferenced. I'd recommend : (1) look at those addresses to see what's stored in them, and (2) if you're confused by the assembly pointer chasing, try writing simple C programs that call strcmp and seeing the resulting assembly.
good luck.
This is the part who is calling the strcmp as drivingon9 pointed.
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
0x08048a6b <function8+72>: mov %eax,(%esp)
0x08048a6e <function8+75>: call 0x8048434 <strcmp#plt>
The result value of the strcmp will be stored in the register EAX.
Thats why we have a test eax,eax in the code bellow:
0x0804847f <function8+75>: test %eax,%eax
0x08048481 <function8+77>: jne 0x8048499 <function8+101>
0x08048483 <function8+79>: mov $0x80485a0,%eax
0x08048488 <function8+84>: mov -0xc(%ebp),%edx
The test eax, eax tests if eax register is equal to 0.
i'm not sure what part of the code will let you do what you want, but you can try change the line
0x08048481 <function8+77>: jne 0x8048499 <function8+101>
and change the instruction to a je(JUMP IF EQUAL) or a incondicional jump(JMP)
Related
I've got an NX enabled, canary enabled x64 ELF and can only view the assembly, not the source code but I do know its written in c. When run, it only accepts command line args and returns nothing. Inside of the main function there's only one function call of note, to evil;
0x000000000040060e <+0>: push rbp
0x000000000040060f <+1>: mov rbp,rsp
0x0000000000400612 <+4>: sub rsp,0x10
0x0000000000400616 <+8>: mov DWORD PTR [rbp-0x4],edi
0x0000000000400619 <+11>: mov QWORD PTR [rbp-0x10],rsi
0x000000000040061d <+15>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400621 <+19>: add rax,0x8
0x0000000000400625 <+23>: mov rax,QWORD PTR [rax]
0x0000000000400628 <+26>: mov rdi,rax
0x000000000040062b <+29>: call 0x4005a7 <evil>
0x0000000000400630 <+34>: mov eax,0x0
0x0000000000400635 <+39>: leave
0x0000000000400636 <+40>: ret
and inside evil, it pulls the command line args and checks... something... against 0xdeadbeef and if they match, passes a "you win!" message, then verifies the canary and if either of those fail, it runs call __stack_chk_fail#plt, which indirects through a GOT entry which is in writeable memory somewhere.
0x00000000004005a7 <+0>: push rbp
0x00000000004005a8 <+1>: mov rbp,rsp
0x00000000004005ab <+4>: sub rsp,0x70
0x00000000004005af <+8>: mov QWORD PTR [rbp-0x68],rdi
0x00000000004005b3 <+12>: mov rax,QWORD PTR fs:0x28
0x00000000004005bc <+21>: mov QWORD PTR [rbp-0x8],rax
0x00000000004005c0 <+25>: xor eax,eax
0x00000000004005c2 <+27>: mov DWORD PTR [rbp-0x54],0x0
0x00000000004005c9 <+34>: mov rdx,QWORD PTR [rbp-0x68]
0x00000000004005cd <+38>: lea rax,[rbp-0x50]
0x00000000004005d1 <+42>: mov rsi,rdx
0x00000000004005d4 <+45>: mov rdi,rax
0x00000000004005d7 <+48>: mov eax,0x0
0x00000000004005dc <+53>: call 0x4004b0 <sprintf#plt>
0x00000000004005e1 <+58>: mov eax,DWORD PTR [rbp-0x54]
0x00000000004005e4 <+61>: cmp eax,0xdeadbeef
0x00000000004005e9 <+66>: jne 0x4005f7 <evil+80>
0x00000000004005eb <+68>: lea rdi,[rip+0xd6] # 0x4006c8
0x00000000004005f2 <+75>: call 0x400490 <puts#plt>
0x00000000004005f7 <+80>: nop
0x00000000004005f8 <+81>: mov rax,QWORD PTR [rbp-0x8]
0x00000000004005fc <+85>: xor rax,QWORD PTR fs:0x28
0x0000000000400605 <+94>: je 0x40060c <evil+101>
0x0000000000400607 <+96>: call 0x4004a0 <__stack_chk_fail#plt>
0x000000000040060c <+101>: leave
0x000000000040060d <+102>: ret
In ghidra and with cyclic strings I'm able to verify that the buffer is 72 characters. I've found a bunch of old info from liveoverflow that's about 5 years old now with the exact same problem (protostar format0), except his buffer is 64. For some reason, this buffer mismatch is causing me all sorts of problems I believe.
I've tried hundreds of inputs to achieve the winning statement;
I've tried overwriting the buffer of 72 with 72 A's followed by variations of 0xdeadbeef such as little endian, strings, hex, etc
I've played around with the buffer and offset, so for example putting 0xdeadbeef and then the buffer after, or putting 72 A's with a nop sled of 8 or so after it then 0xdeadbeef
I've tried following liveoverflow's method of overwriting __stack_chk_fail's GOT entry completely, via a format-string vulnerability like %1640d which you can see here, but either have the wrong numbers or am misunderstanding how it works/if it will work on my binary and machine
None of these have given me the winning statement, and I'd really like to understand the why and how and the assembly reasoning behind it.
edit
I've noticed that if I pass python -c 'print ("%x."*5)' as an argument, my stack fills up with ffffe374.f7fa3658.4006b0.f7fcbb60.0, which repeats if I up the number from 5. When checking these with x 0x4006b0, 0x4006b0 is the only value that reuturns anything of use - 0x4006b0 <__libc_csu_fini>: "\363", <incomplete sequence \303>. I've tried creating a rop chain using this and looking up ret2csu guides but I've had no luck so far.
Why is (i&1) | ((i&mostSig)?onesComp:0), where all three variables are integers, faster than i&1 on its own when compiled on GCC without optimisation?
The first expression obviously has to evaluate i&1 and then OR it with ((i&shift)?oneortwo:0), so I would expect it to be faster than i%2 but slower than i&1, and that is the case on Clang (3.8 and newer):(mine is (i&1) | ((i&mostSig)?onesComp:0), broken i&1 (because it would break on a system that uses one's complement), and naive i%2)
But on GCC (5.5 - 8.1) my expression is marginally faster than the other two, all without optimisations:(As above)
The full code for the first expression is:
int i = -3; // Number to test
int onesComp = !((-1) & 1); // Check if machine uses One's complement
int mostSig = 1<<(sizeof(i)*8-1); // Represents the most significant bit (Should be unsigned, but serves for demonstration)
int odd = (i&1) | ((i&mostSig)?onesComp:0);// The bit being benchmarked
where the last line runs in a loop when benchmarking.
I don't think GCC is optimising the code despite the lack of -O flags, because all three expressions produce different assembly, and when I compile the code with -O3 all three perform exactly the same.
I have obviously run the benchmarks multiple times, and whilst the performance gap varies a bit, my expression is always the fastest, and i%2 is always the slowest.
EDIT: Here's the assembly GCC 8.1 produced for (i&1) | ((i&mostSig)?onesComp:0) using Google Benchmark:
push %rbp
mov %rsp,%rbp
sub $0x90,%rsp
mov %rdi,-0x88(%rbp)
movl $0x0,-0x54(%rbp)
movl $0xfffffffd,-0x4(%rbp)
movl $0x0,-0x8(%rbp)
movl $0x80000000,-0xc(%rbp)
mov -0x88(%rbp),%rax
mov %rax,-0x18(%rbp)
mov -0x18(%rbp),%rax
mov %rax,-0x28(%rbp)
mov -0x28(%rbp),%rax
mov %rax,-0x30(%rbp)
mov -0x30(%rbp),%rax
movzbl 0x3c(%rax),%eax
test %al,%al
je 4075ae <mine(benchmark::State&)+0x5c>
mov $0x0,%eax
jmp 4075b6 <mine(benchmark::State&)+0x64>
mov -0x30(%rbp),%rax
mov 0x78(%rax),%rax
mov %rax,-0x40(%rbp)
mov -0x30(%rbp),%rax
mov %rax,-0x38(%rbp)
mov -0x40(%rbp),%rax
mov -0x38(%rbp),%rdx
mov %rax,-0x70(%rbp)
mov %rdx,-0x68(%rbp)
mov -0x18(%rbp),%rax
mov %rax,-0x20(%rbp)
mov -0x20(%rbp),%rax
mov %rax,%rdi
callq 416b60 <benchmark::State::StartKeepRunning()>
movq $0x0,-0x50(%rbp)
movq $0x0,-0x48(%rbp)
mov -0x50(%rbp),%rax
mov -0x48(%rbp),%rdx
mov %rax,-0x80(%rbp)
mov %rdx,-0x78(%rbp)
mov -0x70(%rbp),%rax
test %rax,%rax
setne %al
movzbl %al,%eax
test %rax,%rax
je 40761f <mine(benchmark::State&)+0xcd>
mov $0x1,%eax
jmp 407630 <mine(benchmark::State&)+0xde>
mov -0x68(%rbp),%rax
mov %rax,%rdi
callq 4168e0 <benchmark::State::FinishKeepRunning()>
mov $0x0,%eax
test %al,%al
je 407693 <mine(benchmark::State&)+0x141>
mov -0x4(%rbp),%eax
and $0x1,%eax
mov %eax,%edx
mov -0x4(%rbp),%eax
and -0xc(%rbp),%eax
test %eax,%eax
je 40764b <mine(benchmark::State&)+0xf9>
mov -0x8(%rbp),%eax
jmp 407650 <mine(benchmark::State&)+0xfe>
mov $0x0,%eax
or %edx,%eax
mov %eax,-0x54(%rbp)
mov -0x54(%rbp),%eax
mov -0x70(%rbp),%rax
test %rax,%rax
jne 40767a <mine(benchmark::State&)+0x128>
mov $0x426ca0,%ecx
mov $0x274,%edx
mov $0x426c40,%esi
mov $0x426c69,%edi
callq 404240 <__assert_fail#plt>
mov -0x70(%rbp),%rax
sub $0x1,%rax
mov %rax,-0x70(%rbp)
jmpq 407606 <mine(benchmark::State&)+0xb4>
mov %rax,%rdi
callq 404790 <_Unwind_Resume#plt>
nop
leaveq
retq
And this is for i&1:
push %rbp
mov %rsp,%rbp
sub $0x90,%rsp
mov %rdi,-0x88(%rbp)
movl $0x0,-0x54(%rbp)
movl $0xfffffffd,-0x4(%rbp)
mov -0x88(%rbp),%rax
mov %rax,-0x10(%rbp)
mov -0x10(%rbp),%rax
mov %rax,-0x20(%rbp)
mov -0x20(%rbp),%rax
mov %rax,-0x28(%rbp)
mov -0x28(%rbp),%rax
movzbl 0x3c(%rax),%eax
test %al,%al
je 4076e4 <broken(benchmark::State&)+0x4e>
mov $0x0,%eax
jmp 4076ec <broken(benchmark::State&)+0x56>
mov -0x28(%rbp),%rax
mov 0x78(%rax),%rax
mov %rax,-0x40(%rbp)
mov -0x28(%rbp),%rax
mov %rax,-0x38(%rbp)
mov -0x40(%rbp),%rax
mov -0x38(%rbp),%rdx
mov %rax,-0x70(%rbp)
mov %rdx,-0x68(%rbp)
mov -0x10(%rbp),%rax
mov %rax,-0x18(%rbp)
mov -0x18(%rbp),%rax
mov %rax,%rdi
callq 416b60 <benchmark::State::StartKeepRunning()>
movq $0x0,-0x50(%rbp)
movq $0x0,-0x48(%rbp)
mov -0x50(%rbp),%rax
mov -0x48(%rbp),%rdx
mov %rax,-0x80(%rbp)
mov %rdx,-0x78(%rbp)
mov -0x70(%rbp),%rax
test %rax,%rax
setne %al
movzbl %al,%eax
test %rax,%rax
je 407755 <broken(benchmark::State&)+0xbf>
mov $0x1,%eax
jmp 407766 <broken(benchmark::State&)+0xd0>
mov -0x68(%rbp),%rax
mov %rax,%rdi
callq 4168e0 <benchmark::State::FinishKeepRunning()>
mov $0x0,%eax
test %al,%al
je 4077ae <broken(benchmark::State&)+0x118>
mov -0x4(%rbp),%eax
and $0x1,%eax
mov %eax,-0x54(%rbp)
mov -0x54(%rbp),%eax
mov -0x70(%rbp),%rax
test %rax,%rax
jne 407798 <broken(benchmark::State&)+0x102>
mov $0x426ca0,%ecx
mov $0x274,%edx
mov $0x426c40,%esi
mov $0x426c69,%edi
callq 404240 <__assert_fail#plt>
mov -0x70(%rbp),%rax
sub $0x1,%rax
mov %rax,-0x70(%rbp)
jmp 40773c <broken(benchmark::State&)+0xa6>
mov %rax,%rdi
callq 404790 <_Unwind_Resume#plt>
nop
leaveq
retq
Im trying to figure out what format sscanf function is asking for in assembly. I think it wants two inputs but im not sure if those should be both decimal or string etc.
Ive tried x/s and x/d 0x400c30 but i got \377%z4. Where can i look so it says %d or %s etc? Thanks in advance!
Dump of assembler code for function phase_4:
0x000000000040101e <+0>: sub $0x18,%rsp
0x0000000000401022 <+4>: lea 0xc(%rsp),%rcx
0x0000000000401027 <+9>: lea 0x8(%rsp),%rdx
0x000000000040102c <+14>: mov $0x4027cd,%esi
0x0000000000401031 <+19>: mov $0x0,%eax
0x0000000000401036 <+24>: callq 0x400c30 <__isoc99_sscanf#plt> //???
0x000000000040103b <+29>: cmp $0x2,%eax
0x000000000040103e <+32>: jne 0x40104c <phase_4+46>
0x0000000000401040 <+34>: mov 0xc(%rsp),%eax
0x0000000000401044 <+38>: sub $0x2,%eax
0x0000000000401047 <+41>: cmp $0x2,%eax
0x000000000040104a <+44>: jbe 0x401051 <phase_4+51>
0x000000000040104c <+46>: callq 0x401554 <explode_bomb>
0x0000000000401051 <+51>: mov 0xc(%rsp),%esi
0x0000000000401055 <+55>: mov $0x7,%edi
0x000000000040105a <+60>: callq 0x400fe6 <func4>
0x000000000040105f <+65>: cmp 0x8(%rsp),%eax
0x0000000000401063 <+69>: je 0x40106a <phase_4+76>
0x0000000000401065 <+71>: callq 0x401554 <explode_bomb>
0x000000000040106a <+76>: add $0x18,%rsp
0x000000000040106e <+80>: retq
I'm assuming x86 here. The AT&T syntax is unfamiliar to me, but sscanf's second argument, the format string, goes in the rsi register. The string you're looking for, seems to reside at address 4027cd.
0x000000000040102c <+14>: mov $0x4027cd,%esi
0x0000000000401031 <+19>: mov $0x0,%eax
0x0000000000401036 <+24>: callq 0x400c30 <__isoc99_sscanf#plt> //???
Try to examine that address instead
I'm looking at how the memory is laid out on the stack, but I don't understand why it looks like there are 12 bytes worth of space for storing each of the variables. Here is a simple C program that prints out the location of various variables:
#include <stdio.h>
#include <stdlib.h>
int test (long p1, long p2){
int l1 = 9999;
int l2 = 99993333;
printf("%p p1\n", &p1);
printf("%p p2\n", &p2);
printf("%p l1\n", &l1);
printf("%p l1\n", &l2);
}
int main(int argc, const char** argv)
{
register void* stack asm("esp");
int x = 22;
int y = 1000;
printf("%p stack\n", stack);
printf("%p argv\n", &argv);
printf("%p argc\n", &argc);
printf("%p l1\n", &x);
printf("%p l2\n", &y);
test(1, 888);
return 0;
}
When run, here is the output:
~/gc$ ./a.out
0x7fff5496b200 stack
0x7fff5496b200 argv
0x7fff5496b20c argc
0x7fff5496b218 l1
0x7fff5496b21c l2
0x7fff5496b1d8 p1
0x7fff5496b1d0 p2
0x7fff5496b1e8 l1
0x7fff5496b1ec l1
Why is there 12 space between the address of argv and argc, and 12 space between l1 and l2? I expected 8 for the longs and pointers, and for the int parameter of main, I would understand either 4 or 8 characters, but I don't see any reason it should be 12.
Someone mentioned that the assembly code would be useful, so I got that as well:
Dump of assembler code for function main:
0x0000000000400614 <+0>: push %rbp
0x0000000000400615 <+1>: mov %rsp,%rbp
0x0000000000400618 <+4>: sub $0x20,%rsp
0x000000000040061c <+8>: mov %edi,-0x14(%rbp)
0x000000000040061f <+11>: mov %rsi,-0x20(%rbp)
0x0000000000400623 <+15>: movl $0x16,-0x8(%rbp)
0x000000000040062a <+22>: movl $0x3e8,-0x4(%rbp)
0x0000000000400631 <+29>: mov %rsp,%rax
0x0000000000400634 <+32>: mov %rax,%rsi
0x0000000000400637 <+35>: mov $0x40079c,%edi
0x000000000040063c <+40>: mov $0x0,%eax
0x0000000000400641 <+45>: callq 0x400410 <printf#plt>
0x0000000000400646 <+50>: lea -0x20(%rbp),%rax
0x000000000040064a <+54>: mov %rax,%rsi
0x000000000040064d <+57>: mov $0x4007a6,%edi
0x0000000000400652 <+62>: mov $0x0,%eax
0x0000000000400657 <+67>: callq 0x400410 <printf#plt>
0x000000000040065c <+72>: lea -0x14(%rbp),%rax
0x0000000000400660 <+76>: mov %rax,%rsi
0x0000000000400663 <+79>: mov $0x4007af,%edi
0x0000000000400668 <+84>: mov $0x0,%eax
0x000000000040066d <+89>: callq 0x400410 <printf#plt>
0x0000000000400672 <+94>: lea -0x8(%rbp),%rax
0x0000000000400676 <+98>: mov %rax,%rsi
0x0000000000400679 <+101>: mov $0x400780,%edi
0x000000000040067e <+106>: mov $0x0,%eax
0x0000000000400683 <+111>: callq 0x400410 <printf#plt>
0x0000000000400688 <+116>: lea -0x4(%rbp),%rax
0x000000000040068c <+120>: mov %rax,%rsi
0x000000000040068f <+123>: mov $0x400787,%edi
0x0000000000400694 <+128>: mov $0x0,%eax
0x0000000000400699 <+133>: callq 0x400410 <printf#plt>
0x000000000040069e <+138>: mov $0x14d,%ecx
0x00000000004006a3 <+143>: mov $0x1589e,%edx
0x00000000004006a8 <+148>: mov $0x378,%esi
0x00000000004006ad <+153>: mov $0x1,%edi
0x00000000004006b2 <+158>: callq 0x40052c <test>
0x00000000004006b7 <+163>: mov $0x0,%eax
0x00000000004006bc <+168>: leaveq
0x00000000004006bd <+169>: retq
End of assembler dump.
(gdb) disassemble test
Dump of assembler code for function test:
0x000000000040052c <+0>: push %rbp
0x000000000040052d <+1>: mov %rsp,%rbp
0x0000000000400530 <+4>: sub $0x40,%rsp
0x0000000000400534 <+8>: mov %rdi,-0x28(%rbp)
0x0000000000400538 <+12>: mov %rsi,-0x30(%rbp)
0x000000000040053c <+16>: mov %rdx,-0x38(%rbp)
0x0000000000400540 <+20>: mov %rcx,-0x40(%rbp)
0x0000000000400544 <+24>: movl $0x270f,-0x18(%rbp)
0x000000000040054b <+31>: movq $0x5f5c6f5,-0x10(%rbp)
0x0000000000400553 <+39>: movl $0x63,-0x14(%rbp)
0x000000000040055a <+46>: movq $0x371,-0x8(%rbp)
0x0000000000400562 <+54>: lea -0x28(%rbp),%rax
0x0000000000400566 <+58>: mov %rax,%rsi
0x0000000000400569 <+61>: mov $0x400764,%edi
0x000000000040056e <+66>: mov $0x0,%eax
0x0000000000400573 <+71>: callq 0x400410 <printf#plt>
0x0000000000400578 <+76>: lea -0x30(%rbp),%rax
0x000000000040057c <+80>: mov %rax,%rsi
0x000000000040057f <+83>: mov $0x40076b,%edi
0x0000000000400584 <+88>: mov $0x0,%eax
0x0000000000400589 <+93>: callq 0x400410 <printf#plt>
0x000000000040058e <+98>: lea -0x38(%rbp),%rax
0x0000000000400592 <+102>: mov %rax,%rsi
0x0000000000400595 <+105>: mov $0x400772,%edi
0x000000000040059a <+110>: mov $0x0,%eax
0x000000000040059f <+115>: callq 0x400410 <printf#plt>
0x00000000004005a4 <+120>: lea -0x40(%rbp),%rax
0x00000000004005a8 <+124>: mov %rax,%rsi
0x00000000004005ab <+127>: mov $0x400779,%edi
0x00000000004005b0 <+132>: mov $0x0,%eax
0x00000000004005b5 <+137>: callq 0x400410 <printf#plt>
0x00000000004005ba <+142>: lea -0x18(%rbp),%rax
0x00000000004005be <+146>: mov %rax,%rsi
0x00000000004005c1 <+149>: mov $0x400780,%edi
0x00000000004005c6 <+154>: mov $0x0,%eax
0x00000000004005cb <+159>: callq 0x400410 <printf#plt>
0x00000000004005d0 <+164>: lea -0x10(%rbp),%rax
0x00000000004005d4 <+168>: mov %rax,%rsi
0x00000000004005d7 <+171>: mov $0x400787,%edi
0x00000000004005dc <+176>: mov $0x0,%eax
0x00000000004005e1 <+181>: callq 0x400410 <printf#plt>
0x00000000004005e6 <+186>: lea -0x14(%rbp),%rax
0x00000000004005ea <+190>: mov %rax,%rsi
0x00000000004005ed <+193>: mov $0x40078e,%edi
0x00000000004005f2 <+198>: mov $0x0,%eax
0x00000000004005f7 <+203>: callq 0x400410 <printf#plt>
0x00000000004005fc <+208>: lea -0x8(%rbp),%rax
0x0000000000400600 <+212>: mov %rax,%rsi
0x0000000000400603 <+215>: mov $0x400795,%edi
0x0000000000400608 <+220>: mov $0x0,%eax
0x000000000040060d <+225>: callq 0x400410 <printf#plt>
0x0000000000400612 <+230>: leaveq
0x0000000000400613 <+231>: retq
End of assembler dump.
You are using a 64-bit system (based on the size of the pointers printed), which means you're probably using x86-64.
Certain arguments to functions in the x86-64 ABI do not have addresses because they are passed in registers. However, according to the C standard, you are allowed to take their addresses. So, when you write &argc, the compiler reserves space for it on the stack and returns that address.
So it is just another local variable. The compiler is free to put argc anywhere on the stack. This behavior is not mandatory, it's just the way that your compiler works.
As for the reason why 12 happens coincidentally on this particular occasion to be the spacing, remember that the stack grows downwards on x86-64. So if you push argc onto the stack the stack pointer will go down 4 bytes, and if you push argv onto the stack it will first go down another 4 bytes to be aligned correctly, then it will go down 8 bytes after argv is pushed. Of course the compiler is free to do other things, like put argv and argc in arbitrary other locations on the stack.
Demonstration
C code:
void otherfunc(int *ptr);
int func(int value)
{
otherfunc(&value);
return 0;
}
Assembly code:
func:
subq $24, %rsp ; Allocate 24 bytes on the stack
movl %edi, 12(%rsp) ; Store 'value' on the stack
leaq 12(%rsp), %rdi ; Calculate the address of 'value'
call otherfunc ; Call 'otherfunc'
xorl %eax, %eax ; Return value 0
addq $24, %rsp ; Deallocate stack
ret ; Return
Remember that %rsp is the stack pointer, %edi / %rdi is the first parameter to a function, and %eax is the return value of a function.
I have been trying to skip an instruction by changing the return address through stack smashing. The following code skips a++ in main and prints an output of "1 3". I have executed this code on a 32-bit intel machine.
#include<stdio.h>
void fun(int a,int b) {
// buffer
char buf[8];
char *p;
p = (char *)buf+24;
*p=*p+5;
return;
}
int main() {
int a=1,b=2;
fun(a,b);
a++;
b++;
printf("%d %d",a,b);
}
I am unable to understand why return address is stored at a displacement of 24 bytes from starting address of buf. I have tried executing the same code on a different 32-bit intel machine and I had to use a displacement of 20 bytes instead of 24 bytes. I have put my understanding in the following figure. I am not sure about what fills the gap represented by "?" in the figure. Does gcc put any canary value there or am I missing something ?
Link to figure: http://www.cse.iitb.ac.in/~shashankr/stack.png
Smashing the stack example3.c confusion asked the same question but could not explain the reason for displacement in general.
The following figure gives a view of the stack obtained by placing a breakpoint in function.
(source: shashankr at www.cse.iitb.ac.in)
The following is the assembly code for main and fun:
Dump of assembler (fun):
0x08048434 <+0>: push %ebp
0x08048435 <+1>: mov %esp,%ebp
0x08048437 <+3>: sub $0x18,%esp
0x0804843a <+6>: mov %gs:0x14,%eax
0x08048440 <+12>: mov %eax,-0xc(%ebp)
0x08048443 <+15>: xor %eax,%eax
0x08048445 <+17>: lea -0x14(%ebp),%eax
0x08048448 <+20>: add $0x18,%eax
0x0804844b <+23>: mov %eax,-0x18(%ebp)
0x0804844e <+26>: mov -0x18(%ebp),%eax
0x08048451 <+29>: movzbl (%eax),%eax
0x08048454 <+32>: add $0x5,%eax
0x08048457 <+35>: mov %eax,%edx
0x08048459 <+37>: mov -0x18(%ebp),%eax
0x0804845c <+40>: mov %dl,(%eax)
0x0804845e <+42>: mov -0xc(%ebp),%eax
0x08048461 <+45>: xor %gs:0x14,%eax
0x08048468 <+52>: je 0x804846f <fun+59>
0x0804846a <+54>: call 0x8048350 <__stack_chk_fail#plt>
0x0804846f <+59>: leave
0x08048470 <+60>: ret
Dump of assembler (main)
0x08048471 <+0>: push %ebp
0x08048472 <+1>: mov %esp,%ebp
0x08048474 <+3>: and $0xfffffff0,%esp
0x08048477 <+6>: sub $0x20,%esp
0x0804847a <+9>: movl $0x1,0x18(%esp)
0x08048482 <+17>: movl $0x2,0x1c(%esp)
0x0804848a <+25>: mov 0x1c(%esp),%eax
0x0804848e <+29>: mov %eax,0x4(%esp)
0x08048492 <+33>: mov 0x18(%esp),%eax
0x08048496 <+37>: mov %eax,(%esp)
0x08048499 <+40>: call 0x8048434 <fun>
0x0804849e <+45>: addl $0x1,0x18(%esp)
0x080484a3 <+50>: addl $0x1,0x1c(%esp)
0x080484a8 <+55>: mov $0x80485a0,%eax
0x080484ad <+60>: mov 0x1c(%esp),%edx
0x080484b1 <+64>: mov %edx,0x8(%esp)
0x080484b5 <+68>: mov 0x18(%esp),%edx
0x080484b9 <+72>: mov %edx,0x4(%esp)
0x080484bd <+76>: mov %eax,(%esp)
0x080484c0 <+79>: call 0x8048340 <printf#plt>
0x080484c5 <+84>: leave
0x080484c6 <+85>: ret
I believe the answer is nothing. Are you having different gcc versions? Anyway a compiler is allowed to allocate a bit more stack than necessary. Perhaps it's the initial "guess" based on the number of variables, but which isn't reduced by optimization stages, which are allowed to move any variable to a register. Or it's some reservoir to save ecx,ebp or other registers in case the subroutine needs to.
There's anyway one fixed address variable to overcome the problem: a.
Return address = &a[-1].