CrackingLessons: Challenges #01 - #20 Writeup

This is a series of writeups for the 20 reverse engineering “crackme” challenges posted on crackinglessons.com. You can find the original binaries and challenges here. The challenges are written C/C++, delphi, VB, p-code, Assembly, and C# and cover a variety of topics such as removing obfuscation, unpacking, patching, creating keygens, loaders, removing nag screens, extending 30-day trials, reverse engineering serial key number algorithms, and more. Each challenge writeup will include a brief overview and high level solution, followed by a technical walkthrough of reversing the binary and solving the challenge.

Challenge #01

This challenge includes a basic Windows GUI dialog box that accepts user input, the “serial number” value is hardcoded as a string within the binary, reverse engineering the dialog sequence allows you to identify the string which allows you to bypass the serial number check.

image

The first main function starts by calling CreateDialogParamA which returns a HWND window handle to the execute dialog box, the fourth function parameter (lpDialogFunc) is a pointer to the dialog box procedure for the challenge. This includes a pointer to the check_for_serial function. This dialog box is displayed via a call to Show Window and

image

check_for_serial includes a dialog box that prompts the user for a “serial number” as input, the input is read by the GetDlgItemTextA function, the first parameter is a handle to the dialog box and the identified of the control for the text is defined as 1000. The lpString parameter is a buffer for the user provided string.

image

The lpString variable is checked against the statically defined string cr4ckingL3ssons within the do while loop. If it’s equal to the final_key variable, the check_status variable is set to 0, otherwise the check_status variable is set to 1.

image

The check_status variable is checked to determine if the provided input matches the binaries “serial number”. If it matches, a message box stating “Well done” is displayed, otherwise the user is prompted with an error message stating they should try again.

image

After identifying the hardcoded “serial number” value, we can enter it and reach the “success” execution path.

image

Challenge #02

This challenge includes a Windows GUI binary that states it’s in an “unregistered” state, the challenges objective is to check the registration state without patching the binary. Reverse engineering the binary reveals that at runtime it reads in a .txt file called keyfile.txt and changes the registration state to include any data from within the file. If you create the file within the same directory and add a name to the .txt file, the registration state of the file changes and name is included as the registar.

image

The main function starts by creating a new dialog prompt with a pointer to FUN_00401110 which is the function that includes the About messagebox prompt. main will call CreateFileA to obtain a HANDLE to a file within the same directory that’s called keyfile.txt. If the handle exists a subsequent call to ReadFileEx is made which reads the context of the .txt file into the 31 byte DAT_004142e4 buffer. If data exists within the file, it’s concatenated to a hardcoded “Registered to:” string and the string result is set as a dialog text item.

image

If the user selects the About prompt button on the dialog it displays the registration state along with the data from within the keyfile.txt file.

image

The solution is to create a file named keyfile.txt within the same directory that includes a name. The binary will read the file and change the registration state.

image

Challenge #03

This challenge includes two “nag screens” that need to be removed, there is one messagebox before the dialog and one after you close it. The status for the binary is also “unregistered” by default, you need to control the execution flow to “register” it and you also need to either debug the binary or patch it statically to remove both “nag screens”

image

The main function starts by establishing the dialog prompt (FUN_004010b0) and then displays the first “nag screen” via a call to MessageBoxA if the variable FUN_004010b0 equals 0. To bypass this first check we can either directly patch it with Ghidra or manipulate the JNZ operation in a debugger.

image

We can debug the binary using x32dbg and when the FUN_004010b0 check occurs we can step to the JNZ operation and change the ZF flag from 1 to 0 to redirect the control from which allows us to skip the first nag screen.

image

For registration and the second “nag screen” there is a similar execution flow. We can bypass the second nag screen via patching as well as the registration. For registration we can manipulate _DAT_004142a0 to equal 0 which hits the “REGISTERED” status message.

image

Challenge #04

This challenge involves extending a 30 day trial period for the provided application. The application has a flawed method for calculating the remaining days. The binary subtracts the current date from a static value of 30, we can simply change the value to extend the remaining days.

image

The DaysRemainingInt variable is responsible for holding the remaining days for the “trial”. It’s calculated by obtaining the systems day value from the SYSTEMTIME structure through a call to GetLocalTime. The SYSTEMTIME structure includes values for the year, month, day of the week, day, etc.

typedef struct _SYSTEMTIME {
  WORD wYear;
  WORD wMonth;
  WORD wDayOfWeek;
  WORD wDay;
  WORD wHour;
  WORD wMinute;
  WORD wSecond;
  WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;

The remaining days are calculated by subtracting the .wDay member from the value of 30. If the DaysRemainingInt goes below 1, the “trial” expires.

image

We can solve this by patching the static 30 value, increasing it allows us to extend the “trial”

image

Challenge #05

This challenge includes two inputs, a string input (firstname) and a serial number, inspecting the serial number checking algorithm reveals it uses the systems time to calculate the necessary key. The user string is appended to the year, month, and day values obtained from a call to GetLocalTime via the SYSTEMTIME structure. After building the serial key, it’s printed to a debugger via OutputDebugStringA. The key value that’s built is checked against the user input, to solve the challenge you need to predict what the key value would be, so you can debug the binary, enter nothing for the key, and obtain the system date value.

image

image

image

Challenge #06

Challenge #07

This challenge requires a basic understanding of the x86 instruction set and how conditional statements work in Assembly. For this challenge there are two paths, either display “Unregistered” or “Registered” in the display Window. By default before displaying the window, the EAX register is populated with 2. And following it is a test and JE. To bypass jumping to the “Unregistered” path you need to set the ZF back to 0 prior to the JE operation

image

Challenge #08

Challenge #09

Challenge #10

Challenge #11

Challenge #11

Challenge #12

Challenge #13

Challenge #14

Challenge #15

Challenge #16

Challenge #17

Challenge #18

For this challenge a Windows 32-bit .NET GUI application is provided. The application accepts a name and a serial number as input. Reverse engineering the binary with DNSPY reveals that 3/4 values for the serial number are hardcoded within the code, the name provided by the user is included as the second value concatenated with the other 3 values to make of the full serial number (value1 + user provided name + value2 + value3)

image

Running the application reveals a GUI that includes two input fields for the user, a name and a serial key.

image

PeStudio shows that the application is a 32-bit Windows binary written in C#, so reverse engineering it in DNSPY is the way to go.

image

There is a function within Form1 named btnCheck_Click which is called after the user enters the two input values and clicks the CHECK button on the GUI application.

The function is responsible for performing the following:

  1. The function takes the user-provided name input and and converts it to uppercase and stores it in the text string variable
  2. The name value provided by the user needs to be under 4 characters, the binary will strip anything past 4, if it’s not an error messagebox is displayed
  3. The value variable is populated using text3 + user provided "username" input + text4 + text5.
  4. The text2 variable (serial key) is checked against the value variable, if the Equals function returns true, a messagebox displaying “Correct Serial Key” is displayed, otherwise it states that the wrong serial key was given

image

The quickest way to identify what text3, text4, and text5 are populated with is to debug the binary, set a breakpoint on the btnCheck_Click function and step through the variables while the binary is executing.

We can see that the variables are populated with the form values relating to the “Product ID”, the provided string “random” is converted to uppercase, and “RAND” is the only string value that’s used.

image

The variables are concatanted and the final serial number is built, knowing this, we can exit the debugging session, and enter a name along with the final serial key (since we can control the provided name)

image

Providing random as the name and X398RAND33CEA639 as the final serial key allows us to hit the correct execution path

image

Challenge #19

Challenge #20

Updated: