Reverse Engineering or just Reversing is looking at a program and trying to figure out how it works. Often than trying to change the program to do what you want it to do. In the real world, this is a useful skill to have, especially in malware analysis. Because you could see what the program does, to find out if it's malicious and how it infects the victim.

In this post, I will explain how to reverse a very simple program using Ghidra. I will use 3 different basic techniques to reverse a program, to give an introduction to the different techniques. If you find it interesting I suggest looking at more programs yourself to see how they work and looking online for more resources to learn about.

The program

We will write a simple program to reverse later. This program will just ask for a password, and then check if it is correct. If it is correct, it will print out a message, if not, it will print out a different message. I will do this in C because it's a simple and common language to use for creating these programs. I will call this program reverse.c.

C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main() {
    char guess[100];  // max 100 characters

    // Get user input into guess
    printf("What's the password? ");
    scanf("%s", guess);
    
    // Compare user input to correct password
    // strcmp() will return 0 if the strings are equal
    if (strcmp(guess, "Sup3rStr0ngP4ssw0rd123!") == 0) {
        printf("ACCESS GRANTED\
");
        exit(0);
    } else {
        printf("Wrong password!\
");
        exit(1);
    }
}

Then we can compile the program using gcc as follows:

Shell

$ gcc program.c -o program

After compiling we should get the file program in the same directory. We can run the program using ./program. It will ask for a password, and if we input anything other than the correct password, it will give the wrong password message. And if we of course know the correct password and put it in, it will give us "ACCESS GRANTED".

Shell

$ ./program
What's the password? anything
Wrong password!

$ ./program
What's the password? Sup3rStr0ngP4ssw0rd123!
ACCESS GRANTED

Imagine we don't have access to the program.c file, only to the new compiled program file. Now our goal is to get the ACCESS GRANTED message, without knowing the correct password beforehand. We will use reversing to do this.

1. Looking for strings

It's very common for programs to have strings in them. Anything you put in quotes (") will be a string. Sometimes the creator of the program will put the password in a string (like in the example program) meaning we can look for strings and possibly find it there.

In Linux, there is a simple strings command to find all strings in a given file. But note that programs often have lots of other strings in them that get added from compiling. Things like libraries and other standard code.

Shell

$ strings program
...
u+UH
[]A\A]A^A_
What's the password?
Sup3rStr0ngP4ssw0rd123!
ACCESS GRANTED
Wrong password!
:*3$"
GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
...

In this example, we can see a few interesting strings we recognize. We can see the question, an ACCESS GRANTED message, and the wrong password message. There is also a string that looks like a password ("Sup3rStr0ngP4ssw0rd123!"). We can try this in the program to see if it is indeed the password.

Shell

$ ./program
What's the password? Sup3rStr0ngP4ssw0rd123!
ACCESS GRANTED

ACCESS GRANTED! We found the password by looking at the strings in the file!

2. Decompiling with Ghidra

The approach with strings worked in this example, but it won't always work. Often the password is hidden in some way to make it not show up as a simple string. That's why we can use a tool like Ghidra to 'decompile' the program. Meaning we take the compiled program and turn it into something close to the original source code. Then we can look at what the code exactly does and see if we can find the password.

Get Ghidra running

First, download and install the latest version of Ghidra (https://ghidra-sre.org/). Then when you open Ghidra you'll be met with a screen like this:

You'll first have to make a project before you can import files. Do this by going to File->New Project or press Ctrl+N. Because you are doing this yourself, just choose 'Non-Shared Project', and give it a name. Then go to File->Import File or press I and select the file program that we compiled earlier. It will ask for a few options, but by default, it should be fine. So just click OK until you're back to the starting screen.

Now that we have imported the file, we can open and analyze it by double-clicking on it. This will open a new window with the file open. And because we just imported it, it will notify us that the program has not been analyzed yet. So just click Yes and Analyse to let it analyze the file.

After loading you should see a panel on the left called 'Symbol Tree'. In here expand the 'Functions' folder by clicking the + sign next to it. Here you can see all the functions Ghidra was able to find. One important function that is in almost every program is the main function. This is the function that is called when the program starts. So we can click on it to open it in the code viewer. In the center panel called 'Listing', you can see the Assembly instructions that the program executes. If you're comfortable with Assembly, you can try to understand what the program does using this code. The rightmost panel is very useful as it shows an attempt by Ghidra to decompile the program back to the C source code. This code is often more easily readable than the Assembly code.

Analysing the decompiled C code

Looking at this main function, we can see that it resembles our C code from earlier pretty well:

C

void main(void)

{
  int iVar1;
  long in_FS_OFFSET;
  char local_78 [104];
  undefined8 local_10;
  
  local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
  printf("What\'s the password? ");
  __isoc99_scanf(&DAT_0010201a,local_78);
  iVar1 = strcmp(local_78,"Sup3rStr0ngP4ssw0rd123!");
  if (iVar1 == 0) {
    puts("ACCESS GRANTED");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts("Wrong password!");
                    /* WARNING: Subroutine does not return */
  exit(1);
}

We can see the "What's the password?" question, the scanf() function, and most importantly the strcmp() function. This function takes two string arguments and returns 0 if the strings are equal. The code then checks if this return value is indeed 0, and gives ACCESS GRANTED. But because Ghidra did the work for us, we can see the correct password as an argument to the strcmp() function! Now we can again try this password in the program.

Shell

$ ./program
What's the password? Sup3rStr0ngP4ssw0rd123!
ACCESS GRANTED

ACCESS GRANTED! We found the password and even the program logic by decompiling the program with Ghidra!

3. Changing instructions with Ghidra

A different way we could solve this challenge is instead of trying to find the correct password, to change the instructions of the program to give the ACCESS GRANTED message, without even providing the correct password. This is called 'Patching'.

Installing the SavePatch.py script

Out of the box, Ghidra is not really able to do this well (or at least I had some problems). But with a handy script for Ghidra, we can import, we can easily change instructions and save the new program.

We'll use a script called SavePatch.py from https://github.com/schlafwandler/ghidra_SavePatch. To install it, we need to place it in one of the folders that Ghidra is searching for scripts. To find all these folders, go to Window->Script Manager, then on the top bar, you find a list icon called 'Manage Script Directories' (3rd from right). When you click this icon, another window will pop up with a list of all the folders where Ghidra can find scripts. Either choose an enabled folder in this list or add your own with the + button. Then make sure your SavePatch.py file is in one of those folders.

When you're done with adding the folder, you can close the window. Then still in the Script Manager click the green 'Refresh Script List' button to reload the scripts, and it should find the SavePatch.py script. To check if it's working, search something like 'patch' in the Filter bar on the bottom. If you find the script, you're good to go and you can close the Script Manager.

Patching assembly instructions

Now we'll start with actually patching the instructions. Open the main function just like in the previous technique. Then look at the Decompile panel on the right. We'll try to change the if statement to the opposite of what it is now so that the program actually gives the ACCESS GRANTED message when we put in the wrong password.

To do this, we can click on the word 'if' in the Decompile panel. This will take us to the correct location in the Listing panel, which shows Assembly as you may remember. Here you can see that the if statement is actually JNZ LAB_0010123a in Assembly. This JNZ means 'Jump if Not Zero'. To do the inverse, we would need to change this to JZ which means 'Jump if Zero'.

Now place your cursor on the JNZ LAB_0010123a instruction, then right-click and choose 'Patch Instruction', or press Ctrl+Shift+G. You might get some warning about the Assembler Rating but you can just click OK. Now you should see the instruction editable. Go to the first field that says JNZ and change it to JZ (74 16). Now you see that the assembly code has changed, and the Decompile panel as well. The previous if (iVar1 == 0) statement is now if (iVar1 != 0).

We've now changed the instruction to give the ACCESS GRANTED message, and we can save the program. We will need to use the SavePatch.py script from earlier to do this. So first select a small area around the patched instruction (this will be the part that gets replaced in the program) and then click on the Script Manager icon again. In here, look for the SavePatch.py script, then right-click it and choose 'Run Script'. You should see a new window with the script running. It will load for a bit and then ask for a place to save it. I suggest giving it a different name (something like program_patched), to make sure you preserve the original. Just click 'Save changes' when you selected a location and name. The new program is saved now, and you can close the Script Manager again.

Now that we have our new patched program, we can run it. We made it so that when we supply a wrong password, the program gives the ACCESS GRANTED message instead of the wrong password message. So we can just input anything to get past the check.

Shell

$ ./program_patched
What's the password? anything
ACCESS GRANTED

ACCESS GRANTED! We bypassed the password check by inverting the if statement, meaning we can input any password to get past the check!

Conclusion

Ghidra is a very nice tool to learn for reverse engineering. Its decompiler and patching ability are very powerful and easy to use. If you would like to learn more about reversing, I suggest looking at some tools like GDB to debug the program while it is running.

Other than that, have fun and happy hacking!