0

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!!!

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • 4
    If you are new to C, why would you want to do this weird stuff that will only work under certain conditions on specific hardware and is by no means covered by C language standard? – Gerhardh Oct 13 '20 at 16:39
  • 3
    Just as a side note: If your intention is to learn C, then you shouldn't be learning it by experimenting with what the official ISO C standard considers [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior). These are hardware and operating system specific implementation details. Such details are of no interest to the common C programmer. They should only be of interest to you if you plan to learn assembly language. However, if you are determined to learn assembly language, I suggest you first learn C before learning assembly language. – Andreas Wenzel Oct 13 '20 at 16:45
  • If you really want to understand what is going on, you could run your program in a debugger in a mode which displays the individual assembly instructions and allows you to run one instruction at a time, while monitoring the contents of the stack. However, this would require you to understand the individual assembly instructions. Also, understanding the [x64 calling convention](https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI) would probably be useful. – Andreas Wenzel Oct 13 '20 at 21:10
  • @AndreasWenzel The debugger mode you were talking about sounds very cool. Do you have a debugger to recommend? – elithecool Oct 14 '20 at 11:44
  • @elithecool: Although I am unfamiliar with it myself, as I mainly use the Windows platform, the most popular debugger on Linux seems to be [GDB](https://www.gnu.org/software/gdb/). However, that debugger is text-based. If you are looking for a graphical front-end or simply a front-end which fits better into your IDE, you might want to take a look at [this list](http://sourceware.org/gdb/wiki/GDB%20Front%20Ends) of GDB front-ends. Also this link may help: [Show current assembly instruction in GDB](https://stackoverflow.com/questions/1902901/show-current-assembly-instruction-in-gdb) – Andreas Wenzel Oct 14 '20 at 15:25

0 Answers0