What do you need to disassemble programs
For reverse engineering from scratch, three components will suffice: Ghidra, Java Development Kit and virtual machine. Read more in our article on binary vulnerability analysis.
Ghidra – free disassembler (translates machine code into assembly instructions) and decompiler (restores the semblance of source code in C). Downloaded from the official NSA repository on GitHub. Before IDA Free, Ghidra has one killer advantage for learning to learn fromftern engineering – a full decompiler for x86, x64, ARM and MIPS without restrictions. Instead of reading mnemonics like MOV, CMP, JNZ you see pseudo-C with variables, conditions, and cycles. First, for the eyes.
JDK 21 or newer Ghidra is written in Java. On Linux installation in one command: apt install openjdk-21-jdk. On Windows is an installer with adoptium.net.
Virtual Machine – even training crackme should be launched in isolation. VirtualBox is free and sufficient. Create a VM with Windows 10/11, put Ghidra inside and take a snapshot of “Clean State” before running any binary. Switch the network adapter to Host-Only after installing the software – this isolates VM from the main network. Paranoia? Maybe. But the habit will be useful when instead of the training crackme on the table will be suspicious .exe from the wild.
Why even learn the analysis of binary files? Three directions: malware analysis (understand what a suspicious file does), search for vulnerabilities in closed software (when there are no sources and will not be) and CTF-competition. Crackme is a training ground where the same techniques are worked out without legal risks and without a threat to the work infrastructure.
Import crackme and autoanalysis launch in Ghidra
For the first crack in Ghidra, sets from open platforms are suitable. Classics - IOLI CrackMe (a set of binary with increasing complexity, mentioned in the Shogun Lab training series "Here Be Dragons"). Level crackme0x00 – ideal entry point: one verification function, one password, no obfuction.
Step 1. Open Ghidra and create a project: File → New Project → Non-Shared Project. A name is anything meaningful, such as “CrackMe_Practice.” The project is a container inside which Ghidra stores all the results of the analysis.
Step 2. Import the binary. Drag .exe or ELF file in the project window, or through File → Import File. Ghidra will show the file card: architecture (x86 or x64), format (PE for Windows, ELF for Linux), the intended compiler. This is not just an information plate – the architecture defines a set of registers and call convention agreements that you will encounter in the listing.
Step 3. A double click on the file in the project opens Ghidra will ask if to run automatic analysis. Press the yes. Default settings for simple crackme are suitable. The analysis takes a few seconds to a couple of minutes.
After analysis in front of you three windows to remember:
Symbol Tree (left panel) - character tree: functions, imports, exports. If the binary is collected with debug symbols, here will be meaningful names: main, checkPassword, validate. If without - Ghidra will generate technical names like FUN_00401000. Don’t be alarmed: even automatic names can be renamed in the analysis process.
Listing (center) - disassembled code: addresses, bytes, assembly instructions. At first, this window is more frightening than it helps. Normally, we will hardly use it directly.
Decompiler (right panel) is a decompiled pseudo-C. Look here. Ghidra turns a set of instructions into readable code. An important detail: if you select a string in the decompiler, Ghidra will highlight the relevant instructions in Listing. The connection is two-way, and over time it will help to learn the assembler naturally - through a comparison with an already understandable C-code. I remembered more mnemonics in a week than a month reading the textbook on x86.
How to find a password in the Ghidra program: the Defined Strings method
This is the central technique for solving simple crackme and the starting point for learning reverse engineering. The logic is simple: the program that outputs the text “Wrong password” or “Invalid!” stores these lines inside the binary. Found a line - found the code that causes it. And next to the output code “wrong” is checking the password.
Do it once. Open the line window: Window → Defined Strings. Ghidra will show all the text strings found in the binary. In the training crackme here will be the results lines: “Enter password”, “Password OK”, “Invalid Password!”, “Try Again!”. They are looking for them with your eyes.
Do two. Found a line related to the check - for example "Invalid Password!". Right click → References → Show References to Address. Ghidra will show all the places in the code where this string is used. Usually one or two entrys.
Do three. A double click on the link will transfer you to the function that decides whether or not to be correct. The Decompiler window on the right will show the pseudo-C code of this function. You will see the condition if, the challenge strcmp or a similar check and, at a distance of one glance, the password itself or the mechanism for its generation.
The reception works for the vast majority of training crackme. The reason is banal: the authors encrypt the password, but almost never encrypt the notification lines. The message “Try Again!” is a thread that leads directly to the verification logic.
Code decompilation: read password check
Let’s say Defined Strings has led you to the verification feature. The Ghidra decompiler will show something similar:
void main(void) {
char input[32];
printf("Enter password: ");
scanf("%s", input);
if (strcmp(input, "s3cr3t_k3y") == 0) {
puts("Correct!");
} else {
puts("Invalid Password!");
}
}
strcmp – standard line comparison function. Returns 0, if the lines match. One side of the comparison is input (user input), the other is a shard-skink password. The answer is literally in the decompiled code.
In practice, Ghidra does not always generate such a pure conclusion. Variables will be called local_28, param_1, iVar2. The comparison function can be displayed as FUN_00401120 instead of strcmp. Here's what to do about it:
Renaming variables. Right click on the variable in the decompiler → Rename Variable. Name local_28 How user_input, iVar2 How comparison_result. Ghidra will update names everywhere, including the Listing window. It's not cosmetics - in five minutes you will forget what is local_28, eh user_input understood immediately.
Identification of functions. Instead of strcmp see FUN_00401120? Click on the function name, see its decompiled code. Have you seen a cycle of two lines of byte comparison? It's custom strcmp. Rename the function through L (Label) in custom_strcmp and continue the analysis.
Crosslinks. Highlight the line in the decompiler — Ghidra will highlight the appropriate assembler in Listing. Over time, you will begin to notice: “if (iVar2 == 0) are a couple of instructions TEST EAX, EAX + JNZ." This is how the assembler learns without cramming - through the context of real analysis of binary files.
Static analysis of binary: three patterns of inspection
Simple strcmp with open text – the “crackme for the first day” level. Real-world tasks and even medium-scope learning crackme use three sustainable patterns.
A hard-sewn line
The easiest option. The password lies in the binary as a text constant. Function strcmp (or memcmp for binary data) compares the user input to this constant. It is detected through Defined Strings or directly in the decompiler. Most of the “beginner” level on crackmes.one use this particular. Stupid, but it works – and is more common than you would like.
XOR-obfuscation of the password
The author of the crackme stores the password in encrypted form and decrypts when checking the XOR operation (bit excluding OR) with a fixed key. In the Ghidra decompiler, it looks like a cycle that runs through an array of bytes and applies ^ (XOR) to each element. As described in the FatMike's CrackMe#1 (fewstreet.com) parseup, such a cycle can XOR's custom input with a constant array, and then compare the result with the benchmark:
for (i = 0; i < length; i++) {
xor_output = input ^ xor_key;
}
result = check_hash(xor_output);
To recover your password, you need to find both arrays — the encrypted benchmark and the key — and perform the reverse operation. Because XOR is reversible (A ^ B ^ B = A), Enough XOR's the key-to-key benchmark. Both arrays are usually seen in the decompiler as global variables. Beautiful mathematics, and breaks in a minute, if you know where to look.
Hash comparison
The password is not stored at all - neither in the open nor in the encrypted. The program calculates the hash (CRC32, MD5, SHA-256) from the user input and compares with the hard-skinned value. In the Ghidra decompiler, look for a function call that receives the string and returns the number or array of bytes. Characteristic marker of tabular implementation of CRC-32 — constant 0xEDB88320 (reflected-poline used in zlib and most standard implementations). Bitouts can contain direct polynomial 0x04C11DB7 or not having an obvious constant at all. You saw one of these constants in the body of the function – before you CRC32, and the password will have to be selected, not removed directly. Here already without a brutforce or rainbow tables it is necessary.
Obfuscation and Packers: When Defined Strings Doesn't Help
Opened crackme, went to Defined Strings - and there empty. Not a single meaningful line. Ghidra found only one function – entry. Meet: Binary packed with a packer.
The most common packer is UPX. It compresses the binary sections and adds a unboxer that restores the original code in memory when running. In terms of MITRE ATT&CK, this technique is Software Packing (T1027.002) – the same technique is used by malware authors to bypass static analysis.
How to recognize UPX in Ghidra? Open Program Tree (left top panel). If instead of standard sections .text, .data, .rdata see UPX0 and UPX1 - Binary is packed. This observation is confirmed in the FatMike's CrackMe#1: Ghidra disassembly did not find functions except for entry point until the binary was unpacked.
UPX unpacking is trivial: upx -d crackme.exe in the command line. After that, import the unpacked file in Ghidra again and run the analysis — strings and functions will appear. If the author crackme used a modified UPX (swept the signature), the full-time unboxing would not work — but for training tasks, such a rarity.
Another technique that occurs in a crackme of medium complexity is debugging protection (Debugger Evasion, T1622 by MITRE ATT&CK). The program causes IsDebuggerPresent() on Windows or ptrace(PTRACE_TRACEME, ...) on Linux. Debugger detected - the program is completed or gives a false result. (Note: MITRE T1622 is primarily documented for Windows; on Linux, similar techniques are applied in practice, but are formally less covered by tests.) In the decompiler, such checks are visible as the calls of these functions at the beginning main. As shown in the material FreeCodeCamp about the ELF-crackme solution, the check can be bypassed by patching - replacing the instruction of the conditional transition (JNZ → JMP) directly in Ghidra via right click → Patch Instruction. According to the documents, you can not just take and change the instructions. In practice, one click.
Frequent mistakes in learning reverse engineering
Confusion between virtual address and file offset (file offset). Address 0x00401000 in Listing - the virtual address at which the code will be uploaded to memory. It does NOT match the position of these bytes in the file on the disk. If you open a binary in the hex editor and search for bytes at the address from Ghidra - you will not find. For translation, use Window → Bytes in Ghidra, where both values are shown. I lost an hour on it myself until I figured it out.
Trying to read an assembler without a decompiler. Beginners often believe that the “real” reverse engineer reads only the assembler. In practice, the decompiler is the main tool even for experienced analysts. The assembler is needed to clarify the details that the decompiler interpreted inaccurately: the order of arguments, the side effects of the instructions, the self-excreting code. But the starting point is always the decompiler. Purism does not help here, but brakes.
Ignoring the renaming of variables. Ghidra allows you to rename variables, functions and labels. Beginners do not use it, and after ten minutes of analysis are lost in local_14, local_18, local_1c. Rename each variable as soon as you understand its purpose. This is not an option – it is a mandatory stage of static analysis of binary.
Stripped-binary analysis without searching the main. If there is no function in Symbol Tree main – binary is assembled without debug symbols (stripped). Look for a challenge __libc_start_main (for ELF) is one of his arguments and there is an address main. Ghidra usually highlights this call in the function entry. For PE files look for mainCRTStartup or start with the entry point and trace the calls deep.
Checklist: from Ghidra launch to found password
Create a project in Ghidra, import a binary.
Run autoanalysis with default settings.
Check Program Tree: If Sections UPX0/UPX1 – unpacking through upx -d and import again.
Open Window → Defined Strings, look for the results lines: “Correct”, “Wrong”, “Invalid”, “Try Again”.
By found line → References → Show References to Address → go to the verification function.
In the Decompiler window, read the pseudo-C code of the function. Search strcmp, memcmp, cycles with XOR, hash functions calls.
Rename variables and functions for readability.
Extract password: from argument strcmp, from the XOR key and benchmark or selection to the hash.
Check: run crackme in VM and enter the found password.
For reverse engineering from scratch, three components will suffice: Ghidra, Java Development Kit and virtual machine. Read more in our article on binary vulnerability analysis.
Ghidra – free disassembler (translates machine code into assembly instructions) and decompiler (restores the semblance of source code in C). Downloaded from the official NSA repository on GitHub. Before IDA Free, Ghidra has one killer advantage for learning to learn fromftern engineering – a full decompiler for x86, x64, ARM and MIPS without restrictions. Instead of reading mnemonics like MOV, CMP, JNZ you see pseudo-C with variables, conditions, and cycles. First, for the eyes.
JDK 21 or newer Ghidra is written in Java. On Linux installation in one command: apt install openjdk-21-jdk. On Windows is an installer with adoptium.net.
Virtual Machine – even training crackme should be launched in isolation. VirtualBox is free and sufficient. Create a VM with Windows 10/11, put Ghidra inside and take a snapshot of “Clean State” before running any binary. Switch the network adapter to Host-Only after installing the software – this isolates VM from the main network. Paranoia? Maybe. But the habit will be useful when instead of the training crackme on the table will be suspicious .exe from the wild.
Why even learn the analysis of binary files? Three directions: malware analysis (understand what a suspicious file does), search for vulnerabilities in closed software (when there are no sources and will not be) and CTF-competition. Crackme is a training ground where the same techniques are worked out without legal risks and without a threat to the work infrastructure.
Import crackme and autoanalysis launch in Ghidra
For the first crack in Ghidra, sets from open platforms are suitable. Classics - IOLI CrackMe (a set of binary with increasing complexity, mentioned in the Shogun Lab training series "Here Be Dragons"). Level crackme0x00 – ideal entry point: one verification function, one password, no obfuction.
Step 1. Open Ghidra and create a project: File → New Project → Non-Shared Project. A name is anything meaningful, such as “CrackMe_Practice.” The project is a container inside which Ghidra stores all the results of the analysis.
Step 2. Import the binary. Drag .exe or ELF file in the project window, or through File → Import File. Ghidra will show the file card: architecture (x86 or x64), format (PE for Windows, ELF for Linux), the intended compiler. This is not just an information plate – the architecture defines a set of registers and call convention agreements that you will encounter in the listing.
Step 3. A double click on the file in the project opens Ghidra will ask if to run automatic analysis. Press the yes. Default settings for simple crackme are suitable. The analysis takes a few seconds to a couple of minutes.
After analysis in front of you three windows to remember:
Symbol Tree (left panel) - character tree: functions, imports, exports. If the binary is collected with debug symbols, here will be meaningful names: main, checkPassword, validate. If without - Ghidra will generate technical names like FUN_00401000. Don’t be alarmed: even automatic names can be renamed in the analysis process.
Listing (center) - disassembled code: addresses, bytes, assembly instructions. At first, this window is more frightening than it helps. Normally, we will hardly use it directly.
Decompiler (right panel) is a decompiled pseudo-C. Look here. Ghidra turns a set of instructions into readable code. An important detail: if you select a string in the decompiler, Ghidra will highlight the relevant instructions in Listing. The connection is two-way, and over time it will help to learn the assembler naturally - through a comparison with an already understandable C-code. I remembered more mnemonics in a week than a month reading the textbook on x86.
How to find a password in the Ghidra program: the Defined Strings method
This is the central technique for solving simple crackme and the starting point for learning reverse engineering. The logic is simple: the program that outputs the text “Wrong password” or “Invalid!” stores these lines inside the binary. Found a line - found the code that causes it. And next to the output code “wrong” is checking the password.
Do it once. Open the line window: Window → Defined Strings. Ghidra will show all the text strings found in the binary. In the training crackme here will be the results lines: “Enter password”, “Password OK”, “Invalid Password!”, “Try Again!”. They are looking for them with your eyes.
Do two. Found a line related to the check - for example "Invalid Password!". Right click → References → Show References to Address. Ghidra will show all the places in the code where this string is used. Usually one or two entrys.
Do three. A double click on the link will transfer you to the function that decides whether or not to be correct. The Decompiler window on the right will show the pseudo-C code of this function. You will see the condition if, the challenge strcmp or a similar check and, at a distance of one glance, the password itself or the mechanism for its generation.
The reception works for the vast majority of training crackme. The reason is banal: the authors encrypt the password, but almost never encrypt the notification lines. The message “Try Again!” is a thread that leads directly to the verification logic.
Code decompilation: read password check
Let’s say Defined Strings has led you to the verification feature. The Ghidra decompiler will show something similar:
void main(void) {
char input[32];
printf("Enter password: ");
scanf("%s", input);
if (strcmp(input, "s3cr3t_k3y") == 0) {
puts("Correct!");
} else {
puts("Invalid Password!");
}
}
strcmp – standard line comparison function. Returns 0, if the lines match. One side of the comparison is input (user input), the other is a shard-skink password. The answer is literally in the decompiled code.
In practice, Ghidra does not always generate such a pure conclusion. Variables will be called local_28, param_1, iVar2. The comparison function can be displayed as FUN_00401120 instead of strcmp. Here's what to do about it:
Renaming variables. Right click on the variable in the decompiler → Rename Variable. Name local_28 How user_input, iVar2 How comparison_result. Ghidra will update names everywhere, including the Listing window. It's not cosmetics - in five minutes you will forget what is local_28, eh user_input understood immediately.
Identification of functions. Instead of strcmp see FUN_00401120? Click on the function name, see its decompiled code. Have you seen a cycle of two lines of byte comparison? It's custom strcmp. Rename the function through L (Label) in custom_strcmp and continue the analysis.
Crosslinks. Highlight the line in the decompiler — Ghidra will highlight the appropriate assembler in Listing. Over time, you will begin to notice: “if (iVar2 == 0) are a couple of instructions TEST EAX, EAX + JNZ." This is how the assembler learns without cramming - through the context of real analysis of binary files.
Static analysis of binary: three patterns of inspection
Simple strcmp with open text – the “crackme for the first day” level. Real-world tasks and even medium-scope learning crackme use three sustainable patterns.
A hard-sewn line
The easiest option. The password lies in the binary as a text constant. Function strcmp (or memcmp for binary data) compares the user input to this constant. It is detected through Defined Strings or directly in the decompiler. Most of the “beginner” level on crackmes.one use this particular. Stupid, but it works – and is more common than you would like.
XOR-obfuscation of the password
The author of the crackme stores the password in encrypted form and decrypts when checking the XOR operation (bit excluding OR) with a fixed key. In the Ghidra decompiler, it looks like a cycle that runs through an array of bytes and applies ^ (XOR) to each element. As described in the FatMike's CrackMe#1 (fewstreet.com) parseup, such a cycle can XOR's custom input with a constant array, and then compare the result with the benchmark:
for (i = 0; i < length; i++) {
xor_output = input ^ xor_key;
}
result = check_hash(xor_output);
To recover your password, you need to find both arrays — the encrypted benchmark and the key — and perform the reverse operation. Because XOR is reversible (A ^ B ^ B = A), Enough XOR's the key-to-key benchmark. Both arrays are usually seen in the decompiler as global variables. Beautiful mathematics, and breaks in a minute, if you know where to look.
Hash comparison
The password is not stored at all - neither in the open nor in the encrypted. The program calculates the hash (CRC32, MD5, SHA-256) from the user input and compares with the hard-skinned value. In the Ghidra decompiler, look for a function call that receives the string and returns the number or array of bytes. Characteristic marker of tabular implementation of CRC-32 — constant 0xEDB88320 (reflected-poline used in zlib and most standard implementations). Bitouts can contain direct polynomial 0x04C11DB7 or not having an obvious constant at all. You saw one of these constants in the body of the function – before you CRC32, and the password will have to be selected, not removed directly. Here already without a brutforce or rainbow tables it is necessary.
Obfuscation and Packers: When Defined Strings Doesn't Help
Opened crackme, went to Defined Strings - and there empty. Not a single meaningful line. Ghidra found only one function – entry. Meet: Binary packed with a packer.
The most common packer is UPX. It compresses the binary sections and adds a unboxer that restores the original code in memory when running. In terms of MITRE ATT&CK, this technique is Software Packing (T1027.002) – the same technique is used by malware authors to bypass static analysis.
How to recognize UPX in Ghidra? Open Program Tree (left top panel). If instead of standard sections .text, .data, .rdata see UPX0 and UPX1 - Binary is packed. This observation is confirmed in the FatMike's CrackMe#1: Ghidra disassembly did not find functions except for entry point until the binary was unpacked.
UPX unpacking is trivial: upx -d crackme.exe in the command line. After that, import the unpacked file in Ghidra again and run the analysis — strings and functions will appear. If the author crackme used a modified UPX (swept the signature), the full-time unboxing would not work — but for training tasks, such a rarity.
Another technique that occurs in a crackme of medium complexity is debugging protection (Debugger Evasion, T1622 by MITRE ATT&CK). The program causes IsDebuggerPresent() on Windows or ptrace(PTRACE_TRACEME, ...) on Linux. Debugger detected - the program is completed or gives a false result. (Note: MITRE T1622 is primarily documented for Windows; on Linux, similar techniques are applied in practice, but are formally less covered by tests.) In the decompiler, such checks are visible as the calls of these functions at the beginning main. As shown in the material FreeCodeCamp about the ELF-crackme solution, the check can be bypassed by patching - replacing the instruction of the conditional transition (JNZ → JMP) directly in Ghidra via right click → Patch Instruction. According to the documents, you can not just take and change the instructions. In practice, one click.
Frequent mistakes in learning reverse engineering
Confusion between virtual address and file offset (file offset). Address 0x00401000 in Listing - the virtual address at which the code will be uploaded to memory. It does NOT match the position of these bytes in the file on the disk. If you open a binary in the hex editor and search for bytes at the address from Ghidra - you will not find. For translation, use Window → Bytes in Ghidra, where both values are shown. I lost an hour on it myself until I figured it out.
Trying to read an assembler without a decompiler. Beginners often believe that the “real” reverse engineer reads only the assembler. In practice, the decompiler is the main tool even for experienced analysts. The assembler is needed to clarify the details that the decompiler interpreted inaccurately: the order of arguments, the side effects of the instructions, the self-excreting code. But the starting point is always the decompiler. Purism does not help here, but brakes.
Ignoring the renaming of variables. Ghidra allows you to rename variables, functions and labels. Beginners do not use it, and after ten minutes of analysis are lost in local_14, local_18, local_1c. Rename each variable as soon as you understand its purpose. This is not an option – it is a mandatory stage of static analysis of binary.
Stripped-binary analysis without searching the main. If there is no function in Symbol Tree main – binary is assembled without debug symbols (stripped). Look for a challenge __libc_start_main (for ELF) is one of his arguments and there is an address main. Ghidra usually highlights this call in the function entry. For PE files look for mainCRTStartup or start with the entry point and trace the calls deep.
Checklist: from Ghidra launch to found password
Create a project in Ghidra, import a binary.
Run autoanalysis with default settings.
Check Program Tree: If Sections UPX0/UPX1 – unpacking through upx -d and import again.
Open Window → Defined Strings, look for the results lines: “Correct”, “Wrong”, “Invalid”, “Try Again”.
By found line → References → Show References to Address → go to the verification function.
In the Decompiler window, read the pseudo-C code of the function. Search strcmp, memcmp, cycles with XOR, hash functions calls.
Rename variables and functions for readability.
Extract password: from argument strcmp, from the XOR key and benchmark or selection to the hash.
Check: run crackme in VM and enter the found password.