Artificial truth

The more you see, the less you believe.

[archives] [latest] | [homepage] | [atom/rss]

Defeating IOLI with radare2 in 2017
Fri 08 September 2017 — download

Four years ago, I wrote a blogpost about how to defeat the IOLI crackmes serie. After giving an unplanned workshop at the r2con 2017 (because the main room was too small to handle the unexpected number of attendees, I ended up giving a workshop with xvilka), without any slides not preparation, after waking up at 3am (it was fun), I realised that I'm not using radare2 like I did 4 years ago, and that radare2 itself has changed quite a lot, hence this refreshed blogpost.

A funny note about the IOLI crackmes is that they were written by a radare2 contributor:

I wrote IOLI crackmes to teach my wife a bit of reversing around 10 years ago, I'm amazed they are still useful nowadays...

This might explain why some keys are suspiciously looking like a birthday date ;)

crackme0x0

$ r2 -A ./crackme0x00
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
 -- Disassemble?! No Disassemble Johnny No. 5!!!
[0x0804842e]> pdf @ main
;-- main:
 (fcn) main 127
 main ();
     ; var int local_18h @ ebp-0x18
     ; var int local_4h @ esp+0x4
        ; DATA XREF from 0x08048377 (entry0)
     0x08048414  55             push ebp
     0x08048415  89e5           mov ebp, esp
     0x08048417  83ec28         sub esp, 0x28 ; '('
     0x0804841a  83e4f0         and esp, 0xfffffff0
     0x0804841d  b800000000     mov eax, 0
     0x08048422  83c00f         add eax, 0xf
     0x08048425  83c00f         add eax, 0xf
     0x08048428  c1e804         shr eax, 4
     0x0804842b  c1e004         shl eax, 4
     0x0804842e  29c4           sub esp, eax
     0x08048430  c70424688504.  mov dword [esp], str.IOLI_Crackme_Level_0x00_n ; [0x8048568:4]=0x494c4f49 ; "IOLI Crackme Level 0x00\n" ; const char * format
     0x08048437  e804ffffff     call sym.imp.printf ; int printf(const char *format)
     0x0804843c  c70424818504.  mov dword [esp], str.Password: ; [0x8048581:4]=0x73736150 ; "Password: " ; const char * format
     0x08048443  e8f8feffff     call sym.imp.printf ; int printf(const char *format)
     0x08048448  8d45e8         lea eax, [ebp - 0x18]
     0x0804844b  89442404       mov dword [esp + 4], eax
     0x0804844f  c704248c8504.  mov dword [esp], 0x804858c ; [0x804858c:4]=0x32007325 ; const char * format
     0x08048456  e8d5feffff     call sym.imp.scanf ; int scanf(const char *format)
     0x0804845b  8d45e8         lea eax, [ebp - 0x18]
     0x0804845e  c74424048f85.  mov dword [esp + 4], str.250382 ; [0x804858f:4]=0x33303532 ; "250382" ; const char *s2
     0x08048466  890424         mov dword [esp], eax ; const char * s2
     0x08048469  e8e2feffff     call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
     0x0804846e  85c0           test eax, eax
 ┌─< 0x08048470  740e           je 0x8048480
    0x08048472  c70424968504.  mov dword [esp], str.Invalid_Password__n ; [0x8048596:4]=0x61766e49 ; "Invalid Password!\n" ; const char * format
    0x08048479  e8c2feffff     call sym.imp.printf ; int printf(const char *format)
│┌──< 0x0804847e  eb0c           jmp 0x804848c
││└─> 0x08048480  c70424a98504.  mov dword [esp], str.Password_OK_:__n ; [0x80485a9:4]=0x73736150 ; "Password OK :)\n" ; const char * format
││    0x08048487  e8b4feffff     call sym.imp.printf ; int printf(const char *format)
││    ; JMP XREF from 0x0804847e (main)
│└──> 0x0804848c  b800000000     mov eax, 0
     0x08048491  c9             leave
     0x08048492  c3             ret
[0x0804842e]>

Radare2 is smart enough to show the argument of the strcmp function at 0x08048469, meaning that the flag is likely 250382:

$ ./crackme0x00 
IOLI Crackme Level 0x00
Password: 250382
Password OK :)

crackme0x01

jvoisin@kaa 18:02 /tmp/IOLI-crackme/bin-linux r2 ./crackme0x01 -A
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
 -- Greetings, human.
 [0x08048330]> pdf @ main
;-- main:
 (fcn) main 113
 main ();
      ; var int local_4h @ ebp-0x4
      ; var int local_4h_2 @ esp+0x4
         ; DATA XREF from 0x08048347 (entry0)
      0x080483e4  55             push ebp
      0x080483e5  89e5           mov ebp, esp
      0x080483e7  83ec18         sub esp, 0x18
      0x080483ea  83e4f0         and esp, 0xfffffff0
      0x080483ed  b800000000     mov eax, 0
      0x080483f2  83c00f         add eax, 0xf
      0x080483f5  83c00f         add eax, 0xf
      0x080483f8  c1e804         shr eax, 4
      0x080483fb  c1e004         shl eax, 4
      0x080483fe  29c4           sub esp, eax
      0x08048400  c70424288504.  mov dword [esp], str.IOLI_Crackme_Level_0x01_n ; [0x8048528:4]=0x494c4f49 ; "IOLI Crackme Level 0x01\n"
      0x08048407  e810ffffff     call sym.imp.printf ; int printf(const char *format)
      0x0804840c  c70424418504.  mov dword [esp], str.Password: ; [0x8048541:4]=0x73736150 ; "Password: "
      0x08048413  e804ffffff     call sym.imp.printf ; int printf(const char *format)
      0x08048418  8d45fc         lea eax, [local_4h]
      0x0804841b  89442404       mov dword [local_4h_2], eax
      0x0804841f  c704244c8504.  mov dword [esp], 0x804854c  ; [0x804854c:4]=0x49006425
      0x08048426  e8e1feffff     call sym.imp.scanf ; int scanf(const char *format)
      0x0804842b  817dfc9a1400.  cmp dword [local_4h], 0x149a ; [0x149a:4]=-1
  ┌─< 0x08048432  740e           je 0x8048442
     0x08048434  c704244f8504.  mov dword [esp], str.Invalid_Password__n ; [0x804854f:4]=0x61766e49 ; "Invalid Password!\n"
     0x0804843b  e8dcfeffff     call sym.imp.printf ; int printf(const char *format)
 ┌──< 0x08048440  eb0c           jmp 0x804844e
 ││      ; JMP XREF from 0x08048432 (main)
 │└─> 0x08048442  c70424628504.  mov dword [esp], str.Password_OK_:__n ; [0x8048562:4]=0x73736150 ; "Password OK :)\n"
     0x08048449  e8cefeffff     call sym.imp.printf ; int printf(const char *format)
        ; JMP XREF from 0x08048440 (main)
 └──> 0x0804844e  b800000000     mov eax, 0
      0x08048453  c9             leave
      0x08048454  c3             ret
[0x08048330]> ps @ 0x804854c
%d
[0x08048330]> 

The binary is reading a number (%d format in scanf), then comparing it against 0x149a. You can change the base of the immediate in the listing with the command ahi d @ 0x0804842b to get a nice listing, or simply use the calculator (?) to get every common base:

[0x0804842b]> ? 0x149a
5274 0x149a 012232 5.2K 0000:049a 5274 "\x9a\x14" 0001010010011010 5274.0 5274.000000f 5274.000000
[0x08048330]> ahi d @ 0x0804842b
[0x08048330]> pdf @ main
;-- main:
 (fcn) main 113
 main ();
      ; var int local_4h @ ebp-0x4
      ; var int local_4h_2 @ esp+0x4
         ; DATA XREF from 0x08048347 (entry0)
      0x080483e4  55             push ebp
      0x080483e5  89e5           mov ebp, esp
      0x080483e7  83ec18         sub esp, 0x18
      0x080483ea  83e4f0         and esp, 0xfffffff0
      0x080483ed  b800000000     mov eax, 0
      0x080483f2  83c00f         add eax, 0xf
      0x080483f5  83c00f         add eax, 0xf
      0x080483f8  c1e804         shr eax, 4
      0x080483fb  c1e004         shl eax, 4
      0x080483fe  29c4           sub esp, eax
      0x08048400  c70424288504.  mov dword [esp], str.IOLI_Crackme_Level_0x01_n ; [0x8048528:4]=0x494c4f49 ; "IOLI Crackme Level 0x01\n"
      0x08048407  e810ffffff     call sym.imp.printf ; int printf(const char *format)
      0x0804840c  c70424418504.  mov dword [esp], str.Password: ; [0x8048541:4]=0x73736150 ; "Password: "
      0x08048413  e804ffffff     call sym.imp.printf ; int printf(const char *format)
      0x08048418  8d45fc         lea eax, [local_4h]
      0x0804841b  89442404       mov dword [local_4h_2], eax
      0x0804841f  c704244c8504.  mov dword [esp], 0x804854c ; [0x804854c:4]=0x49006425
      0x08048426  e8e1feffff     call sym.imp.scanf           ; int scanf(const char *format)
      0x0804842b  817dfc9a1400.  cmp dword [local_4h], 5274 ; [0x149a:4]=-1
  ┌─< 0x08048432  740e           je 0x8048442
     0x08048434  c704244f8504.  mov dword [esp], str.Invalid_Password__n ; [0x804854f:4]=0x61766e49 ; "Invalid Password!\n"
     0x0804843b  e8dcfeffff     call sym.imp.printf ; int printf(const char *format)
 ┌──< 0x08048440  eb0c           jmp 0x804844e
 ││      ; JMP XREF from 0x08048432 (main)
 │└─> 0x08048442  c70424628504.  mov dword [esp], str.Password_OK_:__n ; [0x8048562:4]=0x73736150 ; "Password OK :)\n"
     0x08048449  e8cefeffff     call sym.imp.printf ; int printf(const char *format)
        ; JMP XREF from 0x08048440 (main)
 └──> 0x0804844e  b800000000     mov eax, 0
      0x08048453  c9             leave
      0x08048454  c3             ret

Checking the flag:

$ ./crackme0x01 
IOLI Crackme Level 0x01
Password: 5274
Password OK :)
$

crackme0x2

jvoisin@kaa 18:13 /tmp/IOLI-crackme/bin-linux r2 -A ./crackme0x02
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
 -- Execute commands on a temporary offset by appending '@ offset' to your
 command.
[0x0804842b]> pdf @ main
;-- main:
 (fcn) main 144
   main ();
     ; var int local_ch @ ebp-0xc
     ; var int local_8h @ ebp-0x8
     ; var int local_4h @ ebp-0x4
     ; var int local_4h_2 @ esp+0x4
        ; DATA XREF from 0x08048347 (entry0)
     0x080483e4  55             push ebp
     0x080483e5  89e5           mov ebp, esp
     0x080483e7  83ec18         sub esp, 0x18
     0x080483ea  83e4f0         and esp, 0xfffffff0
     0x080483ed  b800000000     mov eax, 0
     0x080483f2  83c00f         add eax, 0xf
     0x080483f5  83c00f         add eax, 0xf
     0x080483f8  c1e804         shr eax, 4
     0x080483fb  c1e004         shl eax, 4
     0x080483fe  29c4           sub esp, eax
     0x08048400  c70424488504.  mov dword [esp], str.IOLI_Crackme_Level_0x02_n ; [0x8048548:4]=0x494c4f49 ; "IOLI Crackme Level 0x02\n"
     0x08048407  e810ffffff     call sym.imp.printf ; int printf(const char *format)
     0x0804840c  c70424618504.  mov dword [esp], str.Password: ; [0x8048561:4]=0x73736150 ; "Password: "
     0x08048413  e804ffffff     call sym.imp.printf ; int printf(const char *format)
     0x08048418  8d45fc         lea eax, [ebp - 4]
     0x0804841b  89442404       mov dword [esp + 4], eax
     0x0804841f  c704246c8504.  mov dword [esp], 0x804856c  ; [0x804856c:4]=0x50006425
     0x08048426  e8e1feffff     call sym.imp.scanf ; int scanf(const char *format)
     0x0804842b  c745f85a0000.  mov dword [ebp - 8], 0x5a   ; 'Z'
     0x08048432  c745f4ec0100.  mov dword [ebp - 0xc], 0x1ec
     0x08048439  8b55f4         mov edx, dword [ebp - 0xc]
     0x0804843c  8d45f8         lea eax, [ebp - 8]
     0x0804843f  0110           add dword [eax], edx
     0x08048441  8b45f8         mov eax, dword [ebp - 8]
     0x08048444  0faf45f8       imul eax, dword [ebp - 8]
     0x08048448  8945f4         mov dword [ebp - 0xc], eax
     0x0804844b  8b45fc         mov eax, dword [ebp - 4]
     0x0804844e  3b45f4         cmp eax, dword [ebp - 0xc]
 ┌─< 0x08048451  750e           jne 0x8048461
    0x08048453  c704246f8504.  mov dword [esp], str.Password_OK_:__n ; [0x804856f:4]=0x73736150 ; "Password OK :)\n"
    0x0804845a  e8bdfeffff     call sym.imp.printf ; int printf(const char *format)
│┌──< 0x0804845f  eb0c           jmp 0x804846d
│││      ; JMP XREF from 0x08048451 (main)
││└─> 0x08048461  c704247f8504.  mov dword [esp], str.Invalid_Password__n ; [0x804857f:4]=0x61766e49 ; "Invalid Password!\n"
││    0x08048468  e8affeffff     call sym.imp.printf ; int printf(const char *format)
││       ; JMP XREF from 0x0804845f (main)
│└──> 0x0804846d  b800000000     mov eax, 0
     0x08048472  c9             leave
           0x08048473      c3             ret
[0x0804842b]> 

The interesting thing is happening between 0x0804842b and 0x0804844b, where the value that we're entering is compared against a computed one, without any side-effect, so we can simply emulate this part with ESIL instead of resorting to pen and paper:

[0x08048330]> s 0x0804842b
[0x0804842b]> aeim
[0x0804842b]> aeip
[0x0804842b]> aesu 0x0804844e

You can check that it's emulating things correctly by switching to the Visual mode, and stepping with F8 if you're used to Ollydbg or s for a more radare2-ish way.

We can use the powerful pf to print the data formatted directly (d for an integer, like with printf(3)), instead of converting it with ahi:

[0x0804842b]> pf d @ ebp - 0xc
0x00177ff4 = 338724
[0x0804842b]> 
$ ./crackme0x02 
IOLI Crackme Level 0x02
Password: 338724
Password OK :)
$

crackme0x03

[0x080484df]> pdf @ sym.main
;-- main:
 (fcn) sym.main 128
              ; DATA XREF from 0x08048377 (entry0)
           0x08048498      55             push ebp
           0x08048499      89e5           mov ebp, esp
           0x0804849b      83ec18         sub esp, 0x18
           0x0804849e      83e4f0         and esp, 0xfffffff0
           0x080484a1      b800000000     mov eax, 0
           0x080484a6      83c00f         add eax, 0xf
           0x080484a9      83c00f         add eax, 0xf
           0x080484ac      c1e804         shr eax, 4
           0x080484af      c1e004         shl eax, 4
           0x080484b2      29c4           sub esp, eax
           0x080484b4      c70424108604.  mov dword [esp], str.IOLI_Crackme_Level_0x03_n ; [0x8048610:4]=0x494c4f49 ; "IOLI Crackme Level 0x03\n"
           0x080484bb      e890feffff     call sym.imp.printf ; int printf(const char *format)
           0x080484c0      c70424298604.  mov dword [esp], str.Password: ; [0x8048629:4]=0x73736150 ; "Password: "
           0x080484c7      e884feffff     call sym.imp.printf ; int printf(const char *format)
           0x080484cc      8d45fc         lea eax, [local_4h]
           0x080484cf      89442404       mov dword [local_4h_2], eax
           0x080484d3      c70424348604.  mov dword [esp], 0x8048634  ; [0x8048634:4]=0x6425
           0x080484da      e851feffff     call sym.imp.scanf ; int scanf(const char *format)
           0x080484df      c745f85a0000.  mov dword [local_8h], 0x5a  ; 'Z'
           0x080484e6      c745f4ec0100.  mov dword [local_ch], 0x1ec
           0x080484ed      8b55f4         mov edx, dword [local_ch]
           0x080484f0      8d45f8         lea eax, [local_8h]
           0x080484f3      0110           add dword [eax], edx
           0x080484f5      8b45f8         mov eax, dword [local_8h]
           0x080484f8      0faf45f8       imul eax, dword [local_8h]
           0x080484fc      8945f4         mov dword [local_ch], eax
           0x080484ff      8b45f4         mov eax, dword [local_ch]
           0x08048502      89442404       mov dword [local_4h_2], eax
           0x08048506      8b45fc         mov eax, dword [local_4h]
           0x08048509      890424         mov dword [esp], eax
           0x0804850c      e85dffffff     call sym.test
           0x08048511      b800000000     mov eax, 0
           0x08048516      c9             leave
           0x08048517      c3             ret
[0x080484df]> pdf @ sym.test
 (fcn) sym.test 42
              ; CALL XREF from 0x0804850c (sym.main)
           0x0804846e      55             push ebp
           0x0804846f      89e5           mov ebp, esp
           0x08048471      83ec08         sub esp, 8
           0x08048474      8b4508         mov eax, dword [arg_8h] ; [0x8:4]=-1 ; 8
           0x08048477      3b450c         cmp eax, dword [arg_ch] ; [0xc:4]=-1 ; 12
       ┌─< 0x0804847a      740e           je 0x804848a
          0x0804847c      c70424ec8504.  mov dword [esp], str.Lqydolg_Sdvvzrug_ ; [0x80485ec:4]=0x6479714c ; "Lqydolg#Sdvvzrug$"
          0x08048483      e88cffffff     call sym.shift
      ┌──< 0x08048488      eb0c           jmp 0x8048496
      ││      ; JMP XREF from 0x0804847a (sym.test)
      │└─> 0x0804848a      c70424fe8504.  mov dword [esp], str.Sdvvzrug_RN______ ; [0x80485fe:4]=0x76766453 ; "Sdvvzrug#RN$$$#=,"
          0x08048491      e87effffff     call sym.shift
             ; JMP XREF from 0x08048488 (sym.test)
      └──> 0x08048496      c9             leave
           0x08048497      c3             ret
[0x080484df]> 

Again, a block of assembly without side-effect, and then a function that suspiciously looks like a goodboy/badboy one, with the sym.shift symbol looking like a decrypt'n'print function: it's taking an encrypted-looking string, and is called in both branches of the function. Same procedure than for the previous crackme, ESIL:

[0x08048360]> s 0x080484df
[0x080484df]> aeim
[0x080484df]> aeip
[0x080484df]> aesu 0x08048506
[0x080484df]> dr=
oeax 0x00000000      eax 0x00052b24      ebx 0x00000000      ecx 0x00000000
 edx 0x000001ec      esi 0x00000000      edi 0x00000000      esp 0x00178000
 ebp 0x00178000      eip 0x08048506      eflags             
[0x080484df]> ? eax
338724 0x52b24 01225444 330.8K 5000:0b24 338724 "$+\x05" 000001010010101100100100 338724.0 338724.000000f 338724.000000
$ ./crackme0x03       
IOLI Crackme Level 0x03
Password: 338724
Password OK!!! :)
$

crackme0x04

For this one, we'll use the visual mode: analyse the binary by launching radare2 with -A, seek to main with s main, enter ascii-graph mode with VV (you can switch to visual mode with V, and then hit V again (or space) to get into grpah mode) then jump to the sym.check symbol by pressing gc (since those are the bracketed letters next to it).

You can adjust the zoom level with the +/- keys, move around with hjkl like in vim, select nodes with tab and shift-tab, and of course, since you're in radare2, get help with ?.

                      ┌────────────────────┐
                       [0x8048484] ;[ga]  │
                      └────────────────────┘
                               v
                               
                               .───────────────────────.
                      ┌────────────────────┐           
                        0x8048498 ;[gd]   │           │
                      └────────────────────┘           
                              f t                      
                  ┌───────────┘ └──────┐               
                                                     
          ┌────────────────────┐ ┌────────────────────┐│
            0x80484a8 ;[gg]   │ │  0x80484fb ;[gc]   ││
          └────────────────────┘ └────────────────────┘│
                  f t                                  
        ┌─────────┘ └─────┐                            
                                                     
┌────────────────────┐┌────────────────────┐           
  0x80484dc ;[gj]   ││  0x80484f4 ;[gf]   │           │
└────────────────────┘└────────────────────┘           
                          └────────────────────────────┘

Likely a badboy in [gc] (you can see the Password incorrect! string if you zoom enough, with the + key.), then we have two checks, one in [gd], and the other in [gg]:

[0x08048484]> pdb @ 0x8048498
    0x08048498  8b4508      mov eax, dword [arg_8h]     ; [0x8:4]=-1 ; 8
    0x0804849b  890424      mov dword [esp], eax
    0x0804849e  e8e1feffff  call sym.imp.strlen         ; size_t strlen(const char *s)
    0x080484a3  3945f4      cmp dword [local_ch], eax   ; [0x13:4]=-1 ; 19
┌─< 0x080484a6  7353        jae 0x80484fb
[0x08048484]> 

This check is likely to prevent the loop from reading behind the entered string.

[0x080483d0]> pdb @ 0x80484a8
    0x080484a8  8b45f4         mov eax, dword [local_ch]
    0x080484ab  034508         add eax, dword [arg_8h]
    0x080484ae  0fb600         movzx eax, byte [eax]
    0x080484b1  8845f3         mov byte [local_dh], al
    0x080484b4  8d45fc         lea eax, [local_4h]
    0x080484b7  89442408       mov dword [local_8h_2], eax
    0x080484bb  c74424043886.  mov dword [local_4h_2], 0x8048638 ; [0x8048638:4]=0x50006425
    0x080484c3  8d45f3         lea eax, [local_dh]
    0x080484c6  890424         mov dword [esp], eax
    0x080484c9  e8d6feffff     call sym.imp.sscanf         ; int sscanf(const char *s, const char *format,   ...)
    0x080484ce  8b55fc         mov edx, dword [local_4h]
    0x080484d1  8d45f8         lea eax, [local_8h]
    0x080484d4  0110           add dword [eax], edx
    0x080484d6  837df80f       cmp dword [local_8h], 0xf   ; [0xf:4]=-1 ; 15
┌─< 0x080484da  7518           jne 0x80484f4
[0x080483d0]> 

A call to sscanf recognized by radare2, with %d as format (ps @ 0x8048638 to check), with the result added to local_8h, that is later compared to 0xf. You can verify that local_8h isn't modified anywhere else by highlighting it by typing / local_8h in visual mode. So our key simply has to be numeric, and its sum must be 0xf, aka 16:

$ ./crackme0x04    
IOLI Crackme Level 0x04
Password: 1111111111111111   
Password OK!
$

crackme0x05

The sym.main and sym.check functions are the same, except for the presence of a sym.parell function in the sym.check one:

              ┌───────────────────────────────────┐
               [0x8048484] ;[gc]                 │
                                                 
               (fcn) sym.parell 68               
               push ebp                          
               mov ebp, esp                      
               sub esp, 0x18                     
               lea eax, [local_4h]               
               mov dword [local_8h], eax         
               mov dword [local_4h_2], 0x8048668 
               mov eax, dword [arg_8h]           
               mov dword [esp], eax              
               call sym.imp.sscanf;[ga]          │
               mov eax, dword [local_4h]         
               and eax, 1                        
               test eax, eax                     
               jne 0x80484c6;[gb]                │
              └───────────────────────────────────┘
                             f t
        ┌────────────────────┘ └──────────────┐
                                             
┌─────────────────────────────────────┐ ┌──────────────────┐
  0x80484ae ;[gf]                    │ │  0x80484c6 ;[gb] │
    ; "Password OK!\n"               │ │ leave            │
 mov dword [esp], str.Password_OK__n   ret              
 call sym.imp.printf;[gd]            │ └──────────────────┘
 mov dword [esp], 0                  
 call sym.imp.exit;[ge]              │
└─────────────────────────────────────┘

Nothing funky, a simple and eax, 1 check, to see if the key is divisible by 2:

$ ./crackme0x05
IOLI Crackme Level 0x05
Password: 55222
Password OK!
$

crackme0x06

The 6th crackme is similar to the previous one, except the presence of a sym.dummy function that looks a bit ugly:

                              ┌────────────────────┐
                               [0x804851a] ;[gd]  │
                              └────────────────────┘
                                      f t
                              ┌───────┘ └──────────────┐
                                                      
                      ┌──────────────────┐             
                        0x8048550 ;[ge] │             │
                      └──────────────────┘             
                              v                        
                                                      
                               .─────────.            
                      ┌──────────────────┐│            
                        0x8048557 ;[gf] ││            │
                      └──────────────────┘│            
                              f t                     
                      ┌───────┘ └─────────│──────────┐ 
                                                    
              ┌────────────────────┐       ┌────────────────────┐
                0x804855d ;[gh]   │      │ │  0x8048586 ;[gc]   │
              └────────────────────┘       └────────────────────┘
                      f t                 
        ┌─────────────┘                  
                                        
┌────────────────────┐                   
  0x8048567 ;[gk]   │  │                 │
└────────────────────┘                   
                  ┌────────────────────┐  
                    0x804857f ;[gg]   │  │
                  └────────────────────┘  
                        └─────────────────┘

We can get a summary of what the function is doing:

[0x080484b4]> pds
0x080484ee str.LOLO
0x080484fc call sym.imp.strncmp
0x08048535 call sym.imp.sscanf
0x08048547 call sym.dummy
0x08048567 str.Password_OK__n
0x0804856e call sym.imp.printf
0x0804857a call sym.imp.exit
[0x080484b4]> 

Odds are that it'll compare an provided string against the LOLO one. But we're already providing a key, so where is crackme getting this string from?

$ r2 -d ./crackme0x06
Process with PID 11804 started...
= attach 11804 11804
bin.baddr 0x08048000
Using 0x8048000
asm.bits 32
 -- Experts agree, security holes suck, and we fixed some of them!
[0xf76dfa20]> db sym.imp.strncmp
[0xf76dfa20]> dc
IOLI Crackme Level 0x06
Password: 55222
hit breakpoint at: 80484fc
[0x080484fc]> pd 1
            ;-- eip:
            0x080484fc b    e8d7feffff     call sym.imp.strncmp
[0x080484fc]> drr
   eax 0xffec6693  eax stack R W 0x5f474458 (XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0) --> ascii
   ebx 0x00000000  ecx
   ecx 0x00000000  ecx
   edx 0xffec647c  edx stack R W 0xffec6693 --> eax stack R W 0x5f474458 (XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0) --> ascii
   esi 0x00000001  esi
   edi 0xf76ad000  (/lib/i386-linux-gnu/libc-2.24.so) edi library R W 0x1b5db0
   esp 0xffec62d0  esp stack R W 0xffec6693 --> eax stack R W 0x5f474458 (XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0) --> ascii
   ebp 0xffec62e8  ebp stack R W 0xffec6308 --> stack R W 0xffec6338 --> stack R W 0xffec63d8 --> stack R W 0x0 --> ecx
   eip 0x080484fc  (LOAD0) (/tmp/IOLI-crackme/bin-linux/crackme0x06) eip program R X 'call 0x80483d8' 'crackme0x06'
   xfs 0x00000000  ecx
   xgs 0x00000063  ascii
   xcs 0x00000023  ascii
   xss 0x0000002b  ascii
eflags         1I  eflags
  oeax 0xffffffff  oeax
[0x080484fc]> dc
hit breakpoint at: 80484fc
[0x080484fc]> drr
   eax 0xffec66c7  eax stack R W 0x5f474458 (XDG_CONFIG_DIRS=/etc/xdg/xdg-xubuntu:/etc/xdg:/etc/xdg) --> ascii
   ebx 0x00000000  ebx
   ecx 0x00000004  ecx
   edx 0xffec647c  edx stack R W 0xffec6693 --> stack R W 0x5f474458 (XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0) --> ascii
   esi 0x00000001  esi
   edi 0xf76ad000  (/lib/i386-linux-gnu/libc-2.24.so) edi library R W 0x1b5db0
   esp 0xffec62d0  esp stack R W 0xffec66c7 --> eax stack R W 0x5f474458 (XDG_CONFIG_DIRS=/etc/xdg/xdg-xubuntu:/etc/xdg:/etc/xdg) --> ascii
   ebp 0xffec62e8  ebp stack R W 0xffec6308 --> stack R W 0xffec6338 --> stack R W 0xffec63d8 --> stack R W 0x0 --> ebx
   eip 0x080484fc  (LOAD0) (/tmp/IOLI-crackme/bin-linux/crackme0x06) eip program R X 'call 0x80483d8' 'crackme0x06'
   xfs 0x00000000  ebx
   xgs 0x00000063  ascii
   xcs 0x00000023  ascii
   xss 0x0000002b  ascii
eflags         1I  eflags
  oeax 0xffffffff  oeax
[0x080484fc]> 

By putting a breakpoint on strncmp and dereferencing the pointers with the drr command, we can guess that it's looking in the environnement variables:

$ LOLO=1337 ./crackme0x06 
IOLI Crackme Level 0x06
Password: 55222
Password OK!
$

crackme0x07

Against, similar to the previous one, with some extra nodes in the sym.dummy function (the symboles are stripped, but you can recognise the function by the shape of their control-flow graphs, so it's ok.):

You can quickly get the dissasemblies of the nodes by getting in the minimap mode by mashing the - key until the graph is small enough, then move between the node with tab and shift-tab.

[ 0x804857f ]

   ; JMP XREF from 0x080485b5 (sub.sscanf_542)
   ; [0x9:4]=-1
   ; 9                       __8542__
cmp dword [local_8h], 9        f t
jg 0x80485b7;[gc]          ┌───┘ │
                                
                      __8578__   
                          v      
              ┌───────────┘      
               .────────.       
           <@@@@@@>             
              f t               
         ┌────┘ └────────│─────┐ 
                              
    __8585__                 _85b7__
        f t                 
         └─────────┐    
                       
     __858f__   __85b0__ 
        f t         └────┘
     ┌──┘ └───┐
             
  __8598__    
     v        
     └──────┐ 
             
         __85a4__

We can see that there is a comparison against the number 9, likely the maximal/minimal size of our key: if you hightlight the local_8h variable, you'll see that it's used: it's incremented by on on every loop passage, so yeah, likely the length of our string, and the jump after the condition is a jump if greater, so our key has to be less than 9 chars long:

$ LOLO= ./crackme0x07
IOLI Crackme Level 0x07
Password: 111111118
Password OK!
$ LOLO= ./crackme0x07
IOLI Crackme Level 0x07
Password: 1111111117
Password Incorrect!
$

crackme0x8 and crackme0x9

They are (almost?) the same than crackme0x07, I'm quite sure you'll figure how to solve them on your own.

[0x00000000]> ?E Good luck and have fun with r2
 .--.     .--------------------------------.
 | _|     |                                |
 | O O   <  Good luck and have fun with r2 |
 |  |  |  |                                |
 || | /   `--------------------------------'
 |`-'|
 `---'
[0x00000000]>