The Official Radare2 Book — страница 53 из 64

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 


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.

IOLI 0x03

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 


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 */

/* ./crackme0x03 @ 0x804846e */

#include 


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