the_thing
18 April, 2018
the_thing is a crackme imported from crackmes.de, with a difficulty level of 3 in C/C++. The aim is to create a keygen to generate valid passwords.
A way to attack this challenge is to effectively work backwards. Before we do that, we need to label the inputs.
This is the code called after the printf()
that prints the string “Username:”.
This call is to the scanf()
function, so the pointer to the buffer stored in
ebp-0x118
is our username buffer.
ebp-0x118
=username
Similarly, the same thing happens when we input the serial.
ebp-0x218
=serial
Now we’re going to skip most of the code and move towards the bottom. We see a
split 0x8048837, where one path prints the success message, and the other
prints the failure. Clearly, the comparison against the password happens around
here. Indeed, moving up a bit, we see the strcmp()
function, and the
user-input serial
is one of the arguments. This must mean that the other
argument is our target serial value.
ebp-0x318
=target_serial
So how is this value generated? Going up a little futher, we see this is passed
to an sprintf()
call.
Here, a value in eax is passed to sprintf()
, with the format string “%d”, and
the result is stored in our target_serial
buffer. So our serial is a number,
and that number is stored in eax at the time of this call. Going up a tiny
bit further
Here, our username
function is pushed as an argument to a function, but which
function? The address is stored in 0x8049f38, and called from the register.
This function must be taking the username as an argument, performing some
operation, and returning a number that is stringified to form the serial.
But what is this function? For this, we can use a debugger. We find that the function is at the address 0x08048f18. Where did this address come from? For the purposes of developing a keygen, the answer actually isn’t important. Some disassemblers will not realise this area is a function, since it is never referenced by its actual address, however we can define one here ourselves. Let’s look at the contents of this function:
This is quite a simple function, and forms the entire key generation algorithm.
The first and only argument, the username
buffer is retrieved. The first
character is rotate left 32 bits. The second is rotated left by 16. The third
by 8, and the fourth by 4. You may notice that all but the last of these don’t
actually do anything, they give the same output as the input. Let’s try with
an example username, ‘~kirby’
rol(0x7E, 32) = 0x7E rol(0x6B, 16) = 0x6B rol(0x69, 8) = 0x69 rol(0x62, 4) = 0x27
This forms the 32-bit integer 0x27696B7E. In decimal this is 661220222, and this is our serial. This also means only the first four characters of the name is actually used or relevant. Building a keygen from this is therefore quite simple - just perform the operations, construct the integer, and stringise it. Our final keygen is as follows: