function main () {
// 4 basic blocks
loc_0x80483e4:
//DATA XREF from entry0 @ 0x8048347
push ebp
ebp = esp
esp -= 0x18
esp &= 0xfffffff0
eax = 0
eax += 0xf //15
eax += 0xf //15
eax >>>= 4
eax <<<= 4
esp -= eax
dword [esp] = "IOLI Crackme Level 0x02\n" //[0x8048548:4]=0x494c4f49 ; str.IOLI_Crackme_Level_0x02 ; const char *format
int printf("IOLI Crackme Level 0x02\n")
dword [esp] = "Password: " //[0x8048561:4]=0x73736150 ; str.Password: ; const char *format
int printf("Password: ")
eax = var_4h
dword [var_sp_4h] = eax
dword [esp] = 0x804856c //[0x804856c:4]=0x50006425 ; const char *format
int scanf("%d")
//sym.imp.scanf ()
dword [var_8h] = 0x5a //'Z' ; 90
dword [var_ch] = 0x1ec //492
edx = dword [var_ch]
eax = var_8h //"Z"
dword [eax] += edx
eax = dword [var_8h]
eax = eax * dword [var_8h]
dword [var_ch] = eax
eax = dword [var_4h]
var = eax - dword [var_ch]
if (var) goto 0x8048461 //likely
{
loc_0x8048461:
//CODE XREF from main @ 0x8048451
dword [esp] = s"Invalid Password!\n"//[0x804857f:4]=0x61766e49 ; str.Invalid_Password ; const char *format
int printf("Invalid ")
do
{
loc_0x804846d:
//CODE XREF from main @ 0x804845f
eax = 0
leave //(pstr 0x0804857f) "Invalid Password!\n" ebp ; str.Invalid_Password
return
} while (?);
} while (?);
}
return;
}
The pdc command is unreliable especially in processing loops (while, for, etc.). So I prefer to use the r2dec plugin in r2 repo to generate the pseudo C code. you can install it easily:
r2pm install r2dec
decompile main() with the following command (like F5 in IDA):
[0x08048330]> pdd@main
/* r2dec pseudo code output */
/* ./crackme0x02 @ 0x80483e4 */
#include <stdint.h>
int32_t main (void) {
uint32_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 0x02\n");
printf ("Password: ");
eax = &var_4h;
*((esp + 4)) = eax;
scanf (0x804856c);
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;
if (eax == var_ch) {
printf ("Password OK :)\n");
} else {
printf ("Invalid Password!\n");
}
eax = 0;
return eax;
}
It's more human-readable now. To check the string in 0x804856c, we can:
• seek
• print string
[0x08048330]> s 0x804856c
[0x0804856c]> ps
%d
it's exactly the format string of scanf(). But r2dec does not recognize the second argument (eax) which is a pointer. it points to var_4h and means out input will store in var_4h.
we can easily write out pseudo code here.
var_ch = (var_8h + var_ch)^2;
if (var_ch == our_input)
printf("Password OK :)\n");
given the initial status that var_8h is 0x5a, var_ch is 0x1ec, we have var_ch = 338724 (0x52b24):
$ rax2 '=10' '(0x5a+0x1ec)*(0x5a+0x1ec)'
338724
$ ./crackme0x02
IOLI Crackme Level 0x02
Password: 338724
Password OK :)
and we finish the crackme0x02.
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 thesym.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 */