November 6, 2020

Reverse Engineering & Cracking – Part I

Reverse Engineering & Cracking – Part I

In this, first part of the series “Reverse Engineering & Cracking” we will look at a simple license check and test if we can bypass the license check.

We use the following tools:

To keep things simple in the first part, we write a small program with a simple license check. For this we define the following “pseudo” program flow:

  • Check if exactly one parameter was given. Otherwise the program ends with an error.
  • Copy 4 bytes from the parameter and treat it as “license”.
  • Generate a 4 byte long “random” license key
  • Check if the parameter equals the license key of the program. If yes output “license accepted”. Otherwise “wrong license”.

The C sourcecode looks like this and can be found on GitHub here:

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

int getRandomNumber() {
    return (uint8_t) ((rand() % 25) + 65);
}

int main(int argc, char *argv[]) {

    if(argc != 2) {
        return EXIT_FAILURE;
    }
    

    time_t t;
    srand((unsigned) time(&t));

    printf("Check if license key is valid \n");
    char *license_arg = malloc(sizeof(char) * 4);
    strncpy(license_arg, argv[1], 4);

    char *random_license = malloc(sizeof(char) * 4);

    for(int i = 0; i < 4; i++) {
        char random_char = (char) getRandomNumber();
        strcat(random_license, &random_char);
    }

    int res = strcmp(random_license, license_arg);

    if (res == 0) {
        printf("license accepted \n");
    } else {
        printf("wrong license \n");
    }

    return EXIT_SUCCESS;
}
  1. Preparing the program

We compile the program with the following call via gcc. “ggc license.c -o license -Wall”. Attention: If you should compile the program e.g. on Linux and not macOS you have to change “uint8_t” to “u_int8_t” in line 7! After that we should have a working executable.

We start the program once without parameters and once with to test our logic:

  • without parameters we should get an error
  • with parameter the message “wrong license

2. Analysis with GDB

Now we start gdb with our program. To do this, execute the following command: “gdb license“.

With the command “info functions” we get the functions of the program displayed. Surprise: we have a “main” function. Note: There are some programming languages like “ada” which do not have a “main” function. Read more: https://en.wikibooks.org/wiki/Ada_Programming/Basic.

With the command “disass main” we can display a disassembly of the main function. Let’s have a look at the output. The exciting part is at address “0x000000010000003ea9”. There the ASM instruction “JNE” is called. This means: The comparison at adress “0x0000000100003ea5” sets the zero flag (ZF) to 1 (boolean = true) if both values are equal, otherwise to 0. JNE (“jump not equal”) “jumps” to adress “0x100003ec2”, i.e. to the end of the program if ZF is 0. And thus both values are not equal.

3. Attack vectors

As we now understand how our logic works, we could “crack” the program with the following attacks. (If you have more ideas, please write me!):

  • 1. set the value in EAX to 0x0 before the CMP instruction gets called. And thus force a “True
  • 2. manipulate the ZF after running the CMP instruction and set it to 1

4. Manipulate EAX Register

Let’s take a look at where CMP is called. At address “0x0000000100003ea5” CMP is called before our JNE instruction. But before that, at address “0x0000000100003ea2” the value from “eax” is copied to an address in memory. To manipulate the value there, we set a breakpoint and stop the program at that position. With the command “break *0x0000000100003ea2” we set a breakpoint at a specific memory address.

Then we start the program with a parameter using the command: “run FFFFFFFF“. The program should now stop at address “0x0000000100003ea2”.

With the command “info register $eax” we can look at the current value of EAX. Anything but 0, let’s just set the value to 0 with the following command: “set $eax=0x0“. This changes the value of EAX before the CMP instruction is executed. With the command “ni” we execute the next instruction. We do this until we see the message “license accepted”. Bingo: cracked!

Summarized:

  • “mov DWORD PTR [rbp-0x34],eax” (Breakpoint)
  • set $eax=0x0
  • “cmp DWORD PTR [rbp-0x34],0x0”
  • ZF = 0 (da eax und rbp-0x34] = 0x0)
  • JNE
  • license accepted

5. Manipulate ZF

We start gdb again with “license” as parameter and this time we set a breakpoint at the JNE instruction. To do this, set a breakpoint at address “0x0000000100003ea9”. With the command “info register $eflags” we can look at the flags, ZF is not set. With the command “set ($eflags)=0x246” we now set the ZF flag.

With the command “ni” we execute the next instruction. We do this until we see the message “license accepted”. Bingo: cracked vol. 2!

6. Summary

In this first simple example we have seen how the basic procedure is. In the next part we either take a crackme from a CTF or a wargame or write a more complex example and look at the NOP attack if necessary.

I hope you had fun.

close

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *