Setup
After watching stacksmashing’s Ghidra tutorial, I downloaded Ghidra for myself and found this very easy crackme on crackmes.one for my very first practice.
I realized the Linux binaries wouldn’t run on macOS, so I fired up a tiny CLI Ubuntu VM for testing.
I didn’t have to work directly with the x86 assembly since Ghidra decompiled into C, but getting better with x86 is my goal in the near future.
High Level
The program asks for a name and then a serial number. It determines the serial number by doing computations on the length of the name and ascii values of the letters in the name. I was able to re-implement the same functions that the binary performed to generate serial that would be accepted. The program just checks that the inputted serial matches the serial generated by the program.
Disassembly
After Ghidra analysis, I looked at the entry function which called __libc_start_main(...)
which had the main function as a parameter.
Main:
undefined8 main(void)
{
int namelen_computation;
long in_FS_OFFSET;
int serial;
char *name_string;
long offset_plus_40;
offset_plus_40 = *(long *)(in_FS_OFFSET + 0x28);
puts("Enter your name");
__isoc99_scanf(&DAT_00102060,name_string);
namelen_computation = namelen_compute(name_string);
puts("Enter the serial");
__isoc99_scanf();
credcheck(namelen_computation,serial);
if (offset_plus_40 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
Great, we found some strings that were printed when I ran the binary. I renamed some functions after realizing what they did (like namelen_compute
or credcheck
).
namelen_compute
This function was responsible for computing the serial which was then later checked by credcheck
.
It looked like this:
int namelen_compute(char *name)
{
int namelen;
size_t namelen_size;
namelen_size = strlen(name);
namelen = (int)namelen_size;
/* if namelen is odd */
if ((namelen - 1U & 1) == 0) {
namelen = namelen + -1;
/* check if namelen-1 is larger than 2 (so namelen is 3 or more chars) */
if (2 < namelen) {
/* multiply ascii value of 3rd letter with 9230, divide by 2 and add 1 */
namelen = (name[2] * 0x240e) / 2 + 1;
}
}
else {
/* if even, -1 from namelen and multiply by 645629 */
namelen = (namelen + -1) * 0x9d9fd;
if (namelen < 0) {
namelen = namelen + 3;
}
/* bit shift left 2, multiply by 13 and divide by 7 */
namelen = ((namelen >> 2) * 0xd) / 7;
}
return namelen;
}
I commented what exactly the code was doing for each step. It was trivial to rewrite this in python.
keygen.py
import sys
if(len(sys.argv) != 2):
print("Usage: python3 keygen.py <name>")
name = sys.argv[1]
nameLen = len(name)
if(nameLen % 2 == 1): # if odd
nameLen -= 1
if(nameLen>2):
nameLen = ord(name[2])*4615+1 # 4615 = 0x240e/2
print("Serial: " + str(nameLen))
else: # if even
nameLen = (nameLen -1 ) * 645629
if(nameLen<0):
nameLen += 3
nameLen = int((nameLen >> 2) * 13 / 7)
print("Serial: " + str(nameLen))
Result
Luckily, the program accepts :)
andrewmagid@ubuntu:~/Documents/pwn/hare121_keygen$ ./crackme
Enter your name
andrew
Enter the serial
1498781
yay you have the correct creds :)
Takeaway
This was my first time working with a disassembler/analyzer like Ghidra and doing a crackme. The problem was very easy (as per the difficulty) but helped me better understand the tools used along the way.