Objective: Recover the real flag by bypassing the anti-debugging and SMC (Self-Modifying Code) protections in the binary.
1. Initial Inspection and the Decoy
During the initial inspection, I ran the strings command to identify readable text and gather the first clues.
strings crackme2
The output did not contain a flag in the standard format (FLAG{...}), but several lines stood out:
/proc/%d/comm
strace
ltrace
radare2
2. Dynamic Analysis and the GDB Illusion
Because the binary was stripped and the Linux dynamic loader used IFUNC optimizations, conventional strcmp breakpoint analysis did not work. I therefore focused directly on the program’s memory management and used stack analysis.
Knowing that local variables in C are allocated consecutively on the stack, I placed a catchpoint on the write system call to stop the program when it printed a message.
(gdb) catch syscall write
Catchpoint 1 (syscall 'write' [1])
(gdb) run
#Gelen ilk write çağrılarını (Karşılama mesajları) 'continue' ile geçtim.
=== Trackbook Crackme 2 ===
Lutfen flag giriniz: AAAAAAAA
I entered AAAAAAAA as test data. GDB intercepted the write syscall while the program was preparing to print its message. Because the process had not yet exited, the stack frame for main was still live in memory. To avoid crossing into unmapped pages, I searched for my input within a precisely bounded 4,000-byte region beginning at the active Stack Pointer ($rsp):
(gdb) find (char*)$rsp, (char*)$rsp + 4000, "AAAAAAAA"
0x7fffffffd840
1 pattern found.
My input was located at 0x7fffffffd840. Further inspection indicated that the program likely stored user input in a 64-byte buffer such as char input[64], because the bytes following the eight-character value (AAAAAAAA) were filled with null bytes (\0).
(gdb) x/20s 0x7fffffffd840
0x7fffffffd840: "AAAAAAAA"
0x7fffffffd849: ""
0x7fffffffd84a: ""
0x7fffffffd84b: ""
0x7fffffffd84c: ""
0x7fffffffd84d: ""
0x7fffffffd84e: ""
0x7fffffffd84f: ""
0x7fffffffd850: ""
0x7fffffffd851: ""
0x7fffffffd852: ""
0x7fffffffd853: ""
0x7fffffffd854: ""
0x7fffffffd855: ""
0x7fffffffd856: ""
0x7fffffffd857: ""
0x7fffffffd858: ""
0x7fffffffd859: ""
0x7fffffffd85a: ""
0x7fffffffd85b: ""
Assuming that the encrypted value compared against the input would be placed immediately after this buffer, I added 64 bytes (hexadecimal 0x40) to the address of my input and read the resulting location as a string:
(gdb) x/10s 0x7fffffffd880
0x7fffffffd880: "FLAG{Tebrikler_Dogru_Flag}"
0x7fffffffd89b: ""
0x7fffffffd89c: ""
0x7fffffffd89d: ""
0x7fffffffd89e: ""
0x7fffffffd89f: ""
0x7fffffffd8a0: ""
0x7fffffffd8a1: ""
0x7fffffffd8a2: ""
0x7fffffffd8a3: ""
The memory analysis and buffer calculation appeared correct. Immediately after the 64-byte boundary of my input variable, I found the program’s runtime-decoded FLAG{Tebrikler_Dogru_Flag} string. This appeared to recover from memory a flag that was completely hidden during static analysis while bypassing the IFUNC behavior. I terminated the program and tested the flag through the normal execution path:
Figure 1: Fake flag
The program returned a YouTube link to Badi Ekrem’s well-known roof-tile-breaking scene from Hababam Sınıfı. The path that seemed breakable had instead broken my leg. This showed that the debugger had been detected and that the program had deliberately generated a fake flag, meaning even the value read directly from memory was deceptive. I moved to static reverse engineering in Ghidra to understand the actual mechanism.
Figure 2: Badi Ekrem
3. Ghidra and Algorithm Analysis
Decompiling the program in Ghidra revealed two major protection mechanisms that changed its execution flow.
Protection 1: Parent Process Detection
The is_debugger_present() function, which referenced the strings found during the initial analysis, read /proc/[pid]/comm to determine whether the program had been launched by an analysis tool such as gdb, strace, or ltrace. When it detected a debugger, the function returned 1 and silently allowed execution to continue rather than crashing.
//is_debugger_present() fonksiyonu (FUN_001011f9) cagiriliyor
uVar1 = FUN_001011f9();
//Donen sonuc (debugger var/yok durumu) dinamik shellcode'a parametre olarak veriliyor
iVar2 = (*__addr)(uVar1);
Protection 2: In-Memory Execution & Self-Modifying Code (SMC)
Later in main, the program allocated an executable memory region with mmap and wrote an encrypted hexadecimal array named enc_shellcode into it after decrypting the bytes with the XOR key 0x55:
__addr = mmap((void *)0x0,6,7,0x22,-1,0);
if (__addr == (code *)0xffffffffffffffff) {
uVar5 = 1;
}
else {
for (local_1bc = 0; local_1bc < 6; local_1bc = local_1bc + 1) {
__addr[(int)local_1bc] = (code)((&DAT_00104078)[(int)local_1bc] ^ 0x55);
}
4. Shellcode Analysis and Recovery of the Real Key
I extracted the enc_shellcode array in Ghidra: DC AD D6 95 64 96.
After XORing the sequence with 0x55, I passed the resulting machine code through a disassembler to obtain its assembly representation:
- XOR Result (Hex):
89 F8 83 C0 31 C3 - Assembly:
mov eax, edi ;Parametreyi (Debugger durumunu) EAX registerına al
add eax, 0x31 ;EAX'in uzerine 0x31 ekle
ret ;Degeri dondur
SMC Algorithm:
- With no debugger (
edi = 0), the function returns0 + 0x31 = 0x31. This is the real execution path. - With a debugger present (
edi = 1), it returns1 + 0x31 = 0x32. This is the Badi Ekrem path.
This mathematically confirmed that, during dynamic analysis in GDB, the program used the 0x32 key to decrypt a fake flag in memory and lead the analysis into an illusion.
5. String Obfuscation and Flag Recovery
After establishing that the real execution path used the 0x31 key, I located the actual encrypted array (enc_real_flag) checked inside the flag_key == 0x31 block in Ghidra.
I recovered the real flag by applying XOR with the 0x31 key to the static hexadecimal sequence in CyberChef:
- Input Hex:
7C 6B 67 68 4A 73 50 55 58 6E 74 5A 43 54 5C 4C - XOR Key:
31(Hex)
Figure 3: Decrypting the real flag in CyberChef
Decrypted Real Flag: MZVY{Badi_Ekrem}
6. Verification
To verify the recovered flag, I ran the program normally without a debugger.
Figure 4: Flag verification
The program accepted the real flag, decrypted the congratulatory message that had been encrypted with 0x55, and printed Yaparsin anam yaparsin yavrucugum :) together with a YouTube link to an İnek Şaban scene. The flag was recovered after successfully bypassing both the dynamic-analysis trap and the SMC protection.
Figure 5: İnek Şaban