💾 Archived View for radare.org › book › crackmes › ioli › ioli_0x03.gmi captured on 2024-08-18 at 17:14:32. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-11-04)
-=-=-=-=-=-=-
crackme 0x03, let's skip the string check part and analyze it directly.
[0x08048360]> aaa [0x08048360]> pdd@sym.main /* r2dec pseudo code output */ /* ./crackme0x03 @ 0x8048498 */ #include <stdint.h> int32_t main (void) { int32_t var_ch; int32_t var_8h; int32_t var_4h; int32_t var_sp_4h; eax = 0; eax += 0xf; eax += 0xf; eax >>= 4; eax <<= 4; printf ("IOLI Crackme Level 0x03\n"); printf ("Password: "); eax = &var_4h; scanf (0x8048634, eax); var_8h = 0x5a; var_ch = 0x1ec; edx = 0x1ec; eax = &var_8h; *(eax) += edx; eax = var_8h; eax *= var_8h; var_ch = eax; eax = var_4h; test (eax, eax); eax = 0; return eax; }
It looks straightforward except the function `test(eax, eax)`. This is unusual to call a function with same two parameters , so I speculate that the decompiler has gone wrong. we can check it in disassembly.
[0x08048360]> pdf@sym.main ... 0x080484fc 8945f4 mov dword [var_ch], eax 0x080484ff 8b45f4 mov eax, dword [var_ch] 0x08048502 89442404 mov dword [var_sp_4h], eax ; uint32_t arg_ch 0x08048506 8b45fc mov eax, dword [var_4h] 0x08048509 890424 mov dword [esp], eax ; int32_t arg_8h 0x0804850c e85dffffff call sym.test ...
Here comes the`sym.test`, called with two parameters. One is var_4h (our input from `scanf()`). The other is var_ch. The value of var_ch (as the parameter of `test()`) can be calculated like it did in crackme_0x02. It's 0x52b24. Try it!
./crackme0x03 IOLI Crackme Level 0x03 Password: 338724 Password OK!!! :)
Take a look at `sym.test`. It's a two path conditional jump which compares two parameters and then do shift. We can guess that shift is most likely the decryption part (shift cipher, e.g. Caesar cipher).
/* r2dec pseudo code output */ /* ./crackme0x03 @ 0x804846e */ #include <stdint.h> int32_t test (int32_t arg_8h, uint32_t arg_ch) { eax = arg_8h; if (eax != arg_ch) { shift ("Lqydolg#Sdvvzrug$"); } else { shift ("Sdvvzrug#RN$$#=,"); } return eax; }
can also reverse `shift()` to satisfy curiosity.
[0x08048360]> pdf@sym.shift ; CODE (CALL) XREF 0x08048491 (sym.test) ; CODE (CALL) XREF 0x08048483 (sym.test) / function: sym.shift (90) | 0x08048414 sym.shift: | 0x08048414 55 push ebp | 0x08048415 89e5 mov ebp, esp | 0x08048417 81ec98000000 sub esp, 0x98 | 0x0804841d c7458400000000 mov dword [ebp-0x7c], 0x0 ; this seems to be a counter | . ; CODE (JMP) XREF 0x0804844e (sym.shift) / loc: loc.08048424 (74) | . 0x08048424 loc.08048424: 0x08048424 8b4508 mov eax, [ebp+0x8] ; ebp+0x8 = strlen(chain) | | 0x08048427 890424 mov [esp], eax | | 0x0804842a e811ffffff call dword imp.strlen | | ; imp.strlen() | | 0x0804842f 394584 cmp [ebp-0x7c], eax | |,=< 0x08048432 731c jae loc.08048450 | || 0x08048434 8d4588 lea eax, [ebp-0x78] | || 0x08048437 89c2 mov edx, eax | || 0x08048439 035584 add edx, [ebp-0x7c] | || 0x0804843c 8b4584 mov eax, [ebp-0x7c] | || 0x0804843f 034508 add eax, [ebp+0x8] | || 0x08048442 0fb600 movzx eax, byte [eax] | || 0x08048445 2c03 sub al, 0x3 | || 0x08048447 8802 mov [edx], al | || 0x08048449 8d4584 lea eax, [ebp-0x7c] | || 0x0804844c ff00 inc dword [eax] | `==< 0x0804844e ebd4 jmp loc.08048424 | | ; CODE (JMP) XREF 0x08048432 (sym.shift) / loc: loc.08048450 (30) | | 0x08048450 loc.08048450: | `-> 0x08048450 8d4588 lea eax, [ebp-0x78] | 0x08048453 034584 add eax, [ebp-0x7c] | 0x08048456 c60000 mov byte [eax], 0x0 | 0x08048459 8d4588 lea eax, [ebp-0x78] | 0x0804845c 89442404 mov [esp+0x4], eax | 0x08048460 c70424e8850408 mov dword [esp], 0x80485e8 | 0x08048467 e8e4feffff call dword imp.printf | ; imp.printf() | 0x0804846c c9 leave \ 0x0804846d c3 ret ;
you can read the assembly code and find the decryption is actually a "sub al, 0x3". we can write a python script for it:
print(''.join([chr(ord(i)-0x3) for i in 'SdvvzrugRN$