I am relatively new to c, and I've been messing around with the virtual memory of a process to learn some basic concepts. I followed a tutorial from Holberton school.
(reference: https://blog.holbertonschool.com/hack-virtual-memory-stack-registers-assembly-code/)
Based on the last part of this blog, I wrote a c program that changes the return address of a function in the stack to an address of my own function named 'bye' and then exits the program.
However, I want to go above and beyond so that 'bye' will return back to main where the original function (func1) was called instead of exiting.
I seem to able to do this, but the stack pointers (rsp and rbp) strangely get offset by 8 bytes in comparison to what they would have been if func1 just returned to main.
Why is this happening? And how do I fix this?
Here is the source code:
#include <stdlib.h>
#include <stdio.h>
void bye(void) __attribute__((noinline));
void func1(void) __attribute__((noinline));
void func2(void);
void * ret_jump; /*a pointer to a label so that the label can be global*/
void bye(){
register long rsp asm("rsp");
register long rbp asm("rbp");
printf("bye, rsp: %lx\n", rsp);
printf("bye, rbp : %lx\n", rbp);
printf("[*] You have been tricked!!\n");
/*goto instead of return to not mess with the stack*/
goto *ret_jump;
}
/*Original function that should return to main but returns to bye*/
void func1(void){
register long rsp asm("rsp");
register long rbp asm("rbp");
printf("func1, rsp: %lx\n", rsp);
printf("func1, rbp : %lx\n", rbp);
int a;
int b;
int c;
a = 64;
b = 32;
c = a * b;
printf("a = %d, b = %d, c = %d\n", a, b, c);
printf("Value of 'a' using rbp: %d\n", *((int*)((char*)rbp - 0xc)));
printf("Value of 'b' using rbp: %d\n", *((int*)((char*)rbp - 0x8)));
printf("Value of 'c' using rbp: %d\n", *((int*)((char*)rbp - 0x4)));
printf("Previous value of rbp: %lx\n", *(unsigned long int*)rbp);
printf("Return address of func1 = %lx\n", *((unsigned long int*)((char*)rbp + 0x8)));
/*Change the return address of func1 (in main) to the address of the bye function*/
printf("func1, rsp: %lx\n", rsp);
printf("func1, rbp : %lx\n", rbp);
*((unsigned long int*)(((char*)rbp) + 0x8)) = (unsigned long int)bye;
printf("Previous value of rbp: %lx\n", *(unsigned long int*)rbp);
printf("Return address of func1 = %lx\n", *((unsigned long int*)((char*)rbp + 0x8)));
printf("func1, rsp: %lx\n", rsp);
printf("func1, rbp : %lx\n", rbp);
/*The stack pointers change after this return call.*/
return;
}
/*This function shows how the stack pointers are offset by 8 bytes from what they would have been if func1 returned to main*/
void func2(void){
register long rsp asm("rsp");
register long rbp asm("rbp");
printf("func2 , rsp: %lx\n", rsp);
printf("func2, rbp : %lx\n", rbp);
int a;
int b;
int c;
printf("a = %d, b = %d, c = %d\n", a, b, c);
printf("rsp: %lx\n", rsp);
printf("rbp : %lx\n", rbp);
}
int main(void){
ret_jump = &&ret_jmp;
register long rsp asm ("rsp");
register long rbp asm ("rbp");
printf("main, rbp = %lx\n", rbp);
printf("main, rsp = %lx\n", rsp);
func1();
ret_jmp:
printf("func1, rsp: %lx\n", rsp);
printf("func1, rbp : %lx\n", rbp);
func2();
return EXIT_SUCCESS;
}
Here is the output when run:
main, rbp = 7ffe96b075f0
main, rsp = 7ffe96b075f0
func1, rsp: 7ffe96b075d0
func1, rbp : 7ffe96b075e0
a = 64, b = 32, c = 2048
Value of 'a' using rbp: 64
Value of 'b' using rbp: 32
Value of 'c' using rbp: 2048
Previous value of rbp: 7ffe96b075f0
Return address of func1 = 55c0a7a0c93a
func1, rsp: 7ffe96b075d0
func1, rbp : 7ffe96b075e0
Previous value of rbp: 7ffe96b075f0
Return address of func1 = 55c0a7a0c68a
func1, rsp: 7ffe96b075d0
func1, rbp : 7ffe96b075e0
bye, rsp: 7ffe96b075e8
bye, rbp : 7ffe96b075e8
[*] You have been tricked!!
func1, rsp: 7ffe96b075e8
func1, rbp : 7ffe96b075e8
func2 , rsp: 7ffe96b075c8
func2, rbp : 7ffe96b075d8
a = 32766, b = -1482635904, c = 21952
rsp: 7ffe96b075c8
rbp : 7ffe96b075d8
My machine is running Ubuntu 16.04 with kernel 5.4.0-48-generic. And it's using glibc 2.27
Thanks in advance to anyone who can help!!!