Cracking Yuri's Simple Keygen with Ghidra

For the first time, I decided to try reverse engineering Linux binaries. I have to say that I’m not an expert in this field, if you find something in this post that is not correct please point it out!

Description

There is an excellent site called crackmes.one and I had it in my bookmarks for a really long time, but finally the time has come. Today, I’m going to show you how to reverse engineer a simple ELF binary and write an “exploit” for it in Python. The binary is going to be a Serial Key checker and the “exploit” will be a key generator. The goal is to understand the algorithm and write that keygen.

Don’t check the comments if you are afraid of serious spoilers!

Here is the link to the challenge: https://crackmes.one/crackme/5c2acb8933c5d46a3882b8d4

Requirements

Apart from basic programming and little familiarity with C/C++ you don’t need anything. I won’t go into details with Assembly and more advanced concepts. However, you’ll need the following tools:

  • Latest Ghidra (v. 9.0.4)
    • Requires Java 11+
  • Linux machine

I’m personally going to use CLion as an IDE (free for students!), which is an excellent development environment. My host machine is MacBook Pro, but the binary is compiled for Linux, so probably you’re better off with a virtual machine or Docker.

Getting started

Go ahead and download the zipped package from the above-mentioned link. The password is going to be: crackmes.one You’ll get a Keygen.tar.gz file, which you have to extract to get the SimpleKeyGen binary.

Information gathering

I ran the simple file command on the binary to determine the architecture and some additional information.

t0thkr1s :: ~/Downloads » file SimpleKeyGen
SimpleKeyGen: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=4b657ffd4f43a704861b200c9f2235f8fe395e27, not stripped
t0thkr1s :: ~/Downloads »

The file is, in fact, a 64-bit ELF binary for Linux. I also run the strings command, which identified some hardcoded strings and the GCC compiler version.

t0thkr1s :: ~/Downloads » strings SimpleKeyGen
/lib64/ld-linux-x86-64.so.2
libc.so.6
exit
puts
printf
strlen
__cxa_finalize
__libc_start_main
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u3UH
[]A\A]A^A_
%s [SERIAL]
Good Serial
Bad Serial
;*3$"
GCC: (GNU) 8.2.1 20180831

--- strip ---

That’s all for now, let’s investigate further with Ghidra!

Ghidra to the rescue

I created a new project called SimpleKeyGen and added the binary using drag and drop. Double-click the file and you’re ready to go.

SimpleKeyGen in Ghidra.

On the Symbol Tree panel, you can find the available functions, labels, etc… I located the main function because it’s the entry point of the program. There is a truly cool feature about Ghidra. It contains a decompiler, which you can activate with Window - Decompile: main.

Decompile view in Ghidra.

As you can see, Ghidra did a pretty good job and restored the source code as much as possible. The program looks very simple. It calls a function with a parameter (first command-line argument) and checks the result, whether the serial key is valid or not. If the result equals zero, we found a correct serial key.

Checking the serial key

Select the checkSerial function from the Symbol Tree, Ghidra will immediately display it in the Decompile view.

The "checkSerial" function in Ghidra.

This function check if the provided parameter has a length of 16 (0x10 in hexadecimal). The parameter is obviously the serial key, so from now on I’m just referencing it as the serial. Then, it loops through the serial and increments the index by 2. There is a logical statement in the body, which is very important. It subtracts the index-th character in the serial and the index + 1-th character and checks, whether the result is -1 or not. If not, the serial wrong.

Recovering the source

At this point, you should have some kind of idea, how this program works. I reconstructed the program in CLion, so you can compile the code and modify it easily.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>

void usage(char *parameter) {
printf("%s [SERIAL]\n", parameter);
exit(-1);
}

int checkSerial(char *serial) {
size_t serial_length;
int result;
int index;


serial_length = strlen(serial);
if (serial_length == 0x10) {
index = 0;
while (serial_length = strlen(serial), index < serial_length) {
if (serial[index] - serial[index + 1] != -1) {
return 0xffffffff;
}
index = index + 2;
}
result = 0;
} else {
result = 0xffffffff;
}
return result;
}


int main(int argc, char **argv) {
std::cout << "Starting SimpleKeyGen..." << std::endl;
std::cout << "The supplied serial is: " << argv[1] << std::endl;

int result;
int code;

if (argc != 2) {
usage(*argv);
}

result = checkSerial(argv[1]);
if (result == 0) {
puts("Good Serial");
code = 0;
} else {
puts("Bad Serial");
code = 0xffffffff;
}
return code;
}

Generating serial keys

Time to write a keygen program. I could have done it C++, but I decided to improve my Python skills. The following script will generate 10 different serial key and print them out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def generate():
print("Generating 10 unique serial keys")

index = 'a'
for serial in range(10):
serial = ""
for i in range(8):
serial += str(index) + str(chr(ord(index) + 1))

index = chr(ord(index) + 1)
print(serial)


if __name__ == '__main__':
generate()

I think it’s not the perfect solution, but it’s good enough for the purpose. I’m starting from the letter ‘a’ and incrementing the ASCII code every time by 1. The first round gives this: abababababababab It a valid serial key because ‘a’(ASCII: 97) - ‘b’ (ASCII: 98) equals minus one. The easiest example would be 0101010101010101. 0 - 1 = -1 and this prevails for every character pair.

Feel free to experiment with the code!

Final words

I really hope that this post was helpful to you. If you have question/suggestions please reach out to me, I’d be happy to help. Have a nice day and happy hacking!

Before you go

If you found this article helpful, please share to help others with similar interest find it! + Feedback and donations are always welcome!