Crackme Writeup 1
Vasa's Very Easy CrackMe
Vasa's Very Easy CrackMe is a beginner-friendly reverse engineering challenge. Written in C/C++ for the Windows x86-64 platform with a difficulty rating of 1.4, this should be fairly easy and give insight on how to navigate compiled code, understand program logic, and potentially bypass simple checks. Typically, the program asks for a password, and your task is to either find the correct password by analysing the code or modify the program to accept any password (by patching or injecting code).
For this challenge, I will be using x64dbg, an efficient, lightweight reverse engineering tool with powerful features. Since the challenge is designed for the x86-64 platform, I will be using the x64 version. First, we need to find the entry point of the executable. We can do this by selecting the "Break at Entry" option from the Debug menu (Debug > Break at Entry). x64dbg will pause at the entry point before executing any code. It uses typical prologue instructions (push rbp), (mov rbp, rsp), which are common at the start of functions, especially for main or initializer functions.
We can now examine the instructions using the available graph in x64dbg. Graph is an important tool to help visualize the control flow of a function or block of code. In the disassembly view, right-click on the code area and select Graph View from the context menu.
By examining the instruction we can deduct that the program requires a username and password from the user, concatenate both input which are then checked against some condition we do not understand yet. A deeper dive through the block of instructions and we can get a better understanding.

We spot an interesting value 0x32446B666A7830 being moved into the rax register. The value is 8 bytes long and is stored in little-endian format, meaning the bytes are reversed. When reversed, it becomes 30 78 6A 66 6B 44 32. Interpreting this as an ASCII string, it translates to 0xjfkD2. This is likely a hardcoded password that will be used for comparison later in the program.
00007FF7A06C1478 | E8 BB140000 | call <JMP.&puts> ; Call puts to print a string
00007FF7A06C147D | 48:8D45 D0 | lea rax,qword ptr ss:[rbp-30] ; Load the address [rbp-30] into rax
00007FF7A06C1481 | 48:89C2 | mov rdx,rax ; Move rax into rdx (prepare for a function call)
00007FF7A06C1484 | 48:8D05 862B0000 | lea rax,qword ptr ds:[7FF7A06C4011] ; Load address [7FF7A06C4011] into rax
00007FF7A06C148B | 48:89C1 | mov rcx,rax ; Move rax into rcx (prepare for a function call)
00007FF7A06C148E | E8 6D110000 | call crackme.7FF7A06C2600 ; Call the function at address crackme.7FF7A06C2600This section begins by calling puts to prompt the user for a username. The address of the buffer for storing the username at [rbp-30] (30 is hexadecimal, meaning the username is located 48 bytes below the base pointer) is then loaded into rdx . Next, the address of a format string at 7FF7A06C4011, used for reading input, is loaded into rcx. A function call is then made to crackme.7FF7A06C2600, which appears to handle reading the username input from the user. By double-clicking on this line in x64dbg, we can examine the function to confirm its purpose. This process is repeated to prompt for and read the password input from the user.
Function call at address 7FF7A06C2600 includes two standard library function calls, __acrt_iob_func and __stdio_common_vfscanf. The first function, __acrt_iob_func, accesses standard input/output streams such as stdin, stdout, and stderr. The second function, __stdio_common_vfscanf, handles formatted input operations, similar to scanf or fscanf, for reading data from these streams. Both functions are part of the C runtime library, with __stdio_common_vfscanf specifically managing formatted input from standard input .

By inspecting the rdx register we can see the hardcoded password (0xjfkD2) in clear text .
00007FF7A06C14B8 | 48:8D55 D0 | lea rdx,qword ptr ss:[rbp-30] ; Load the address [rbp-30] into rdx
00007FF7A06C14BC | 48:8D45 90 | lea rax,qword ptr ss:[rbp-70] ; Load the address [rbp-70] into rax
00007FF7A06C14C0 | 48:89C1 | mov rcx,rax ; Move rax into rcx (prepare for function call)
00007FF7A06C14C3 | E8 18140000 | call <JMP.&strcpy> ; Call strcpy to copy a string from source to destinationThe instructions set here loads the address of the username, stored at [rbp-30] (48 bytes below the base pointer), into the rdx register. It then loads the address of a destination buffer, located at [rbp-70] (112 bytes below the base pointer), into the rax register. The value of rax is moved into the rcx register. Finally, the strcpy function is called, which copies the username from the location pointed to by rdx (the source) into the buffer pointed to by rcx (the destination).
00007FF7A06C14C8 | 48:8D55 F8 | lea rdx,qword ptr ss:[rbp-8] ; Load the address [rbp-8] into rdx (destination for strcat)
00007FF7A06C14CC | 48:8D45 90 | lea rax,qword ptr ss:[rbp-70] ; Load the address [rbp-70] into rax (source string for strcat)
00007FF7A06C14D0 | 48:89C1 | mov rcx,rax ; Move rax into rcx (prepare for function call)
00007FF7A06C14D3 | E8 F8130000 | call <JMP.&strcat> ; Call strcat to concatenate source string to destination stringThis section loads the address of the hardcoded password buffer, located at [rbp-8], into the rdx register, loads the address of the user-entered username buffer, located at [rbp-70], into the rax register, moves the address in rax into the rcx register to prepare for the function call, and finally calls the strcat function to concatenate the username (pointed to by rcx) to the hardcoded password (pointed to by rdx).

As we proceed further into the instructions, we observe that the username entered by the user and the stored password have been concatenated and are now present in the rax register.
00007FF7A06C14D8 | 48:8D55 90 | lea rdx,qword ptr ss:[rbp-70] ; Load the address of the string at [rbp-70] into rdx (second string for strcmp)
00007FF7A06C14DC | 48:8D45 B0 | lea rax,qword ptr ss:[rbp-50] ; Load the address of the string at [rbp-50] into rax (first string for strcmp)
00007FF7A06C14E0 | 48:89C1 | mov rcx,rax ; Move the address in rax into rcx (prepare for function call)
00007FF7A06C14E3 | E8 F0130000 | call <JMP.&strcmp> ; Call strcmp to compare the two strings
00007FF7A06C14E8 | 85C0 | test eax,eax ; Test the result of strcmp (eax) against zero
00007FF7A06C14EA | 75 11 | jne crackme.7FF7A06C14FD ; If the strings are not equal (result is non-zero), jump to crackme.7FF7A06C14FDThe subsequent block of instructions is responsible for comparing the user-input password with the hardcoded password using the strcmp function. The address of the user-input password located at [rbp-70] is loaded into the rdx register, while the address of the hardcoded password, located at [rbp-50], is loaded into the rax register.
A check is performed to determine if the result of the comparison is zero by executing the instruction test eax, eax. This operation examines the Zero Flag (ZF) in the CPU's flags register, indicating that the strings match if the flag is set. If the strings do not match, the program will jump to display a failure message.
Now that we understand how the program works and know the password, we can enter any username of our choice followed by the string we found in the rax register (0xjfkD2) to bypass the password check.
We can simply enter our username, and then use our username combined with the password we found earlier as the password to successfully complete the challenge. But wait, is there another way to complete this challenge? I'm glad you asked!
We can simply modify the instruction jne crackme.7FF7A06C14FD to je crackme.7FF7A06C14FD. By changing the instruction to je (Jump if Equal), the logic is reversed. Now, if the two strings are equal, it jumps to crackme.7FF7A06C14FD. If they are not equal, it will continue to the next instruction without jumping.
With this modification, the patched version of the executable bypasses the string comparison entirely, allowing the user to enter any value without performing a validation check.
We have successfully bypassed the password check and can now enter any password of our choice. This marks the completion of this crackme challenge. I hope the process was not overwhelming and that you found the information useful. Now, the real question is how do you uncover a password if it is encoded or encrypted? Welcome to the world of obfuscation!

Last updated
