Ejercicios Reversing Ioli Crackmes
Los IOLI Crackmes son unos retos/ejercicios basicos de ingenieria inversa en los que hay que averiguar la contraseña que desbloquea a un programa.
Aqui dejo un Write up con la Solucion a los ejercicios IOLI Crackmes.
Con Radare2. (mas adelante rlos repetire con cutter.)
Indice:
1) Solucion crackme0x00 con radare
2) Solucion crackme0x00-clasico-linux
3) Solucion crackme0x01 con radare
4) Solucion crackme0x02 con radare
5) Solucion crackme0x03 con radare
6) Solucion crackme0x04 con radare
7) Solucion crackme0x05 con radare
8) Solucion crackme0x06 con radare
https://dustri.org/b/defeating-ioli-with-radare2.html
crackme0x00 (radare)
jejo@medion:~/reversing/IOLI-crackme$ ./crackme0x00 IOLI Crackme Level 0x00 Password: ???? Invalid Password!
Busco strings -z
jejo@medion:~/reversing/IOLI-crackme$ rabin2 -z crackme0x00 [Strings] nth paddr vaddr len size section type string ――――――――――――――――――――――――――――――――――――――――――――――――――――――― 0 0x00000568 0x08048568 24 25 .rodata ascii IOLI Crackme Level 0x00\n 1 0x00000581 0x08048581 10 11 .rodata ascii Password: 2 0x0000058f 0x0804858f 6 7 .rodata ascii 250382 3 0x00000596 0x08048596 18 19 .rodata ascii Invalid Password!\n 4 0x000005a9 0x080485a9 15 16 .rodata ascii Password OK :)\n
Salida mas simple… ´-zqq´
jejo@medion:~/reversing/IOLI-crackme$ rabin2 -zqq crackme0x00 IOLI Crackme Level 0x00\n Password: 250382 Invalid Password!\n Password OK :)\n
El comando dentro de radare (interactivo) seria fs strings; f
, iz
, radare2 -c "fs strings; f " crackme0x00
jejo@em50l:~/reversing/IOLI-crackme$ radare2 -c "fs strings; f " crackme0x00 -Q 0x08048568 25 str.IOLI_Crackme_Level_0x00 0x08048581 11 str.Password: 0x0804858f 7 str.250382 0x08048596 19 str.Invalid_Password 0x080485a9 16 str.Password_OK_:
Probamos con 250382
jejo@medion:~/reversing/IOLI-crackme$ ./crackme0x00 IOLI Crackme Level 0x00 Password: 250382 Password OK :)
Truco aaaa; pdc @main
dejara la funcion Main en un codigo parecido a C
Si lo retoco. casi tengo el codigo.
jejo@medion:~/reversing/IOLI-crackme$ radare2 -c "aaaa; pdc @main" crackme0x00 -Q function main () { int printf("IOLI Crackme Level 0x00\n") int printf("Password: ") int scanf("%s") //sym.imp.scanf () dword [s2] = "250382" //[0x804858f:4]=0x33303532 ; str.250382 ; const char *s2 dword [esp] = eax //const char *s1 int strcmp("", "250382") var = eax & eax if (!var) goto 0x8048480 //unlikely { loc_0x8048480:
Bueno no es perfecto. digamos que solo ha averiguado las primera s lineas de codigo.. Pero no esta mal.
Se puede intuir que compara una cadena con 250382 int strcmp("", "250382")
crackme0x00 (clasico linux)
jejo@medion:~/Escritorio/reversing/IOLI-crackme$ ./crackme0x00 IOLI Crackme Level 0x00 Password: ???? Invalid Password!
jejo@medion:~/Escritorio/reversing/IOLI-crackme$ strings crackme0x00 /lib/ld-linux.so.2 __gmon_start__ libc.so.6 printf strcmp scanf _IO_stdin_used __libc_start_main GLIBC_2.0 PTRh IOLI Crackme Level 0x00 Password: 250382 Invalid Password! Password OK :) GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10) GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
Probamos con 250382
jejo@medion:~/Escritorio/reversing/IOLI-crackme$ ./crackme0x00 IOLI Crackme Level 0x00 Password: 250382 Password OK :)
crackme0x01 (radare)
r2 cackme0x01
abro el archivo.aaaa
analisis automatico con nombrado de funciones (autoname)afl
listado de funcionespdf @ main
desensamblado funcion main.- antes de la funcion scanf se le pasa a esp el parametro con el formato a leer del teclado.
con
ps @ 0x804854c
(print string) listo el contenido de dicha posicion. - veo que compara el dato leido con
0x149a
con? 0x149a
veo los posibles valores de este dato en sus distintos formatos.
jejo@em50l:~/reversing/IOLI-crackme$ r2 crackme0x01 -- If you want to open the file in read-write mode, invoke r2 with '-w' [0x08048330]> aaaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Check for objc references [x] Check for vtables [x] Type matching analysis for all functions (aaft) [x] Propagate noreturn information [x] Use -AA or aaaa to perform additional experimental analysis. [x] Finding function preludes [x] Enable constraint types analysis for variables [0x08048330]> afl 0x08048330 1 33 entry0 0x080482fc 1 6 sym.imp.__libc_start_main 0x08048380 6 47 sym.__do_global_dtors_aux 0x080483b0 4 50 sym.frame_dummy 0x080484e0 4 35 sym.__do_global_ctors_aux 0x080484d0 1 5 sym.__libc_csu_fini 0x08048504 1 26 sym._fini 0x08048460 4 99 sym.__libc_csu_init 0x080484d5 1 4 sym.__i686.get_pc_thunk.bx 0x080483e4 4 113 main 0x080482d4 1 23 sym._init 0x08048354 3 33 fcn.08048354 0x0804830c 1 6 sym.imp.scanf 0x0804831c 1 6 sym.imp.printf [0x08048330]> pdf @main ┌ 113: int main (int argc, char **argv, char **envp); │ ; var uint32_t var_4h @ ebp-0x4 │ ; var int32_t var_4h_2 @ esp+0x4 │ ; DATA XREF from entry0 @ 0x8048347 │ 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 ; 15 │ 0x080483f5 83c00f add eax, 0xf ; 15 │ 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 ; [0x8048528:4]=0x494c4f49 ; "IOLI Crackme Level 0x01\n" ; const char *format │ 0x08048407 e810ffffff call sym.imp.printf ; int printf(const char *format) │ 0x0804840c c70424418504. mov dword [esp], str.Password: ; [0x8048541:4]=0x73736150 ; "Password: " ; const char *format │ 0x08048413 e804ffffff call sym.imp.printf ; int printf(const char *format) │ 0x08048418 8d45fc lea eax, [var_4h] │ 0x0804841b 89442404 mov dword [var_4h_2], eax │ 0x0804841f c704244c8504. mov dword [esp], 0x804854c ; [0x804854c:4]=0x49006425 ; const char *format │ 0x08048426 e8e1feffff call sym.imp.scanf ; int scanf(const char *format) │ 0x0804842b 817dfc9a1400. cmp dword [var_4h], 0x149a │ ┌─< 0x08048432 740e je 0x8048442 │ │ 0x08048434 c704244f8504. mov dword [esp], str.Invalid_Password ; [0x804854f:4]=0x61766e49 ; "Invalid Password!\n" ; const char *format │ │ 0x0804843b e8dcfeffff call sym.imp.printf ; int printf(const char *format) │ ┌──< 0x08048440 eb0c jmp 0x804844e │ ││ ; CODE XREF from main @ 0x8048432 │ │└─> 0x08048442 c70424628504. mov dword [esp], str.Password_OK_: ; [0x8048562:4]=0x73736150 ; "Password OK :)\n" ; const char *format │ │ 0x08048449 e8cefeffff call sym.imp.printf ; int printf(const char *format) │ │ ; CODE XREF from main @ 0x8048440 │ └──> 0x0804844e b800000000 mov eax, 0 │ 0x08048453 c9 leave └ 0x08048454 c3 ret [0x08048330]> ps @ 0x804854c %d [0x08048330]> ? 0x149a int32 5274 uint32 5274 hex 0x149a octal 012232 unit 5.2K segment 0000:049a string "\x9a\x14" fvalue: 5274.0 float: 0.000000f double: 0.000000 binary 0b0001010010011010 trits 0t21020100 [0x08048330]>
Luego el password es 5274
jejo@em50l:~/reversing/IOLI-crackme$ ./crackme0x01 IOLI Crackme Level 0x01 Password: ? Invalid Password! jejo@em50l:~/reversing/IOLI-crackme$ ./crackme0x01 IOLI Crackme Level 0x01 Password: 5274 Password OK :)
crackme0x02 (radare)
r2 cackme0x01
abro el archivo y ejecutoaaaa
, analisis ,pdf@main
impresin y desensamblado funcion main.
jejo@medion:~/reversing/IOLI-crackme$ radare2 -c "aaaa;pdf@main" crackme0x02 [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Check for objc references [x] Check for vtables [x] Type matching analysis for all functions (aaft) [x] Propagate noreturn information [x] Use -AA or aaaa to perform additional experimental analysis. [x] Finding function preludes [x] Enable constraint types analysis for variables ┌ 144: int main (int argc, char **argv, char **envp); │ ; var uint32_t var_ch @ ebp-0xc │ ; var signed int var_8h @ ebp-0x8 │ ; var int32_t var_4h @ ebp-0x4 │ ; var int32_t var_4h_2 @ esp+0x4 │ ; DATA XREF from entry0 @ 0x8048347 │ 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 ; 15 │ 0x080483f5 83c00f add eax, 0xf ; 15 │ 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 ; [0x8048548:4]=0x494c4f49 ; "IOLI Crackme Level 0x02\n" ; const char *format │ 0x08048407 e810ffffff call sym.imp.printf ; int printf(const char *format) │ 0x0804840c c70424618504. mov dword [esp], str.Password: ; [0x8048561:4]=0x73736150 ; "Password: " ; const char *format │ 0x08048413 e804ffffff call sym.imp.printf ; int printf(const char *format) │ 0x08048418 8d45fc lea eax, [var_4h] │ 0x0804841b 89442404 mov dword [var_4h_2], eax │ 0x0804841f c704246c8504. mov dword [esp], 0x804856c ; [0x804856c:4]=0x50006425 ; const char *format │ 0x08048426 e8e1feffff call sym.imp.scanf ; int scanf(const char *format) │ 0x0804842b c745f85a0000. mov dword [var_8h], 0x5a ; 'Z' ; 90 │ 0x08048432 c745f4ec0100. mov dword [var_ch], 0x1ec ; 492 │ 0x08048439 8b55f4 mov edx, dword [var_ch] │ 0x0804843c 8d45f8 lea eax, [var_8h] │ 0x0804843f 0110 add dword [eax], edx │ 0x08048441 8b45f8 mov eax, dword [var_8h] │ 0x08048444 0faf45f8 imul eax, dword [var_8h] │ 0x08048448 8945f4 mov dword [var_ch], eax │ 0x0804844b 8b45fc mov eax, dword [var_4h] │ 0x0804844e 3b45f4 cmp eax, dword [var_ch] │ ┌─< 0x08048451 750e jne 0x8048461 │ │ 0x08048453 c704246f8504. mov dword [esp], str.Password_OK_: ; [0x804856f:4]=0x73736150 ; "Password OK :)\n" ; const char *format │ │ 0x0804845a e8bdfeffff call sym.imp.printf ; int printf(const char *format) │ ┌──< 0x0804845f eb0c jmp 0x804846d │ ││ ; CODE XREF from main @ 0x8048451 │ │└─> 0x08048461 c704247f8504. mov dword [esp], str.Invalid_Password ; [0x804857f:4]=0x61766e49 ; "Invalid Password!\n" ; const char *format │ │ 0x08048468 e8affeffff call sym.imp.printf ; int printf(const char *format) │ │ ; CODE XREF from main @ 0x804845f │ └──> 0x0804846d b800000000 mov eax, 0 │ 0x08048472 c9 leave └ 0x08048473 c3 ret
El codigo a mirar (en el que hace los calculos del password) esta entre 0x0804842b
justo despues de scanf
y 0x08048451
justo antes de jne (salto condicional)
Luego:
s 0x08048451
Posicionado en la direccion 0x08048451aeim ; aeip
inicializo la pila de la MV y el contador de programa a la direccion anterior.aesu 0x08048451
ejecuto hasta la direccion 0x08048451afvd
Visualizo variables argumentos locales. (variables internas a la fucnion (en pila))
[0x08048330]> s 0x0804842b [0x0804842b]> aeim ; aeip [0x0804842b]> aesu 0x08048451 [0x0804844e]> afvd type:signed int doesn't exist var var_4h = 0x00177ffc = 0 var var_8h = var var_ch = 0x00177ff4 = 338724 var var_4h_2 = 0x00178004 = 0
Probemos con: 338724
jejo@medion:~/Escritorio/reversing/IOLI-crackme$ ./crackme0x02 IOLI Crackme Level 0x02 Password: 338724 Password OK :)
crackme0x03 (radare)
radare2 -c "aaaa;pdf@main" crackme0x03
-c
es para pasarle comandos :aaaa
, analisis ,pdf@main
impresin y desensamblado funcion main.
jejo@em50l:~/reversing/IOLI-crackme$ radare2 -c "aaaa;pdf@main" crackme0x03 [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Check for objc references [x] Check for vtables [x] Type matching analysis for all functions (aaft) [x] Propagate noreturn information [x] Use -AA or aaaa to perform additional experimental analysis. [x] Finding function preludes [x] Enable constraint types analysis for variables ┌ 128: int main (int argc, char **argv, char **envp); │ ; var int32_t var_ch @ ebp-0xc │ ; var signed int var_8h @ ebp-0x8 │ ; var int32_t var_4h @ ebp-0x4 │ ; var int32_t var_4h_2 @ esp+0x4 │ ; DATA XREF from entry0 @ 0x8048377 │ 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 ; 15 │ 0x080484a9 83c00f add eax, 0xf ; 15 │ 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 ; [0x8048610:4]=0x494c4f49 ; "IOLI Crackme Level 0x03\n" ; const char *format │ 0x080484bb e890feffff call sym.imp.printf ; int printf(const char *format) │ 0x080484c0 c70424298604. mov dword [esp], str.Password: ; [0x8048629:4]=0x73736150 ; "Password: " ; const char *format │ 0x080484c7 e884feffff call sym.imp.printf ; int printf(const char *format) │ 0x080484cc 8d45fc lea eax, [var_4h] │ 0x080484cf 89442404 mov dword [var_4h_2], eax │ 0x080484d3 c70424348604. mov dword [esp], 0x8048634 ; [0x8048634:4]=0x6425 ; const char *format │ 0x080484da e851feffff call sym.imp.scanf ; int scanf(const char *format) │ 0x080484df c745f85a0000. mov dword [var_8h], 0x5a ; 'Z' ; 90 │ 0x080484e6 c745f4ec0100. mov dword [var_ch], 0x1ec ; 492 │ 0x080484ed 8b55f4 mov edx, dword [var_ch] │ 0x080484f0 8d45f8 lea eax, [var_8h] │ 0x080484f3 0110 add dword [eax], edx │ 0x080484f5 8b45f8 mov eax, dword [var_8h] │ 0x080484f8 0faf45f8 imul eax, dword [var_8h] │ 0x080484fc 8945f4 mov dword [var_ch], eax │ 0x080484ff 8b45f4 mov eax, dword [var_ch] │ 0x08048502 89442404 mov dword [var_4h_2], eax │ 0x08048506 8b45fc mov eax, dword [var_4h] │ 0x08048509 890424 mov dword [esp], eax │ 0x0804850c e85dffffff call sym.test │ 0x08048511 b800000000 mov eax, 0 │ 0x08048516 c9 leave └ 0x08048517 c3 ret [0x08048360]>
[0x08048360]> s 0x080484df [0x080484df]> aeim ; aeip [0x080484df]> aesu 0x0804850c [0x08048502]> afvd type:signed int doesn't exist var var_4h = 0x00177ffc = 0 var var_8h = var var_ch = 0x00177ff4 = 338724 var var_4h_2 = 0x00178004 = 338724
Probemos con 338724
jejo@em50l:~/reversing/IOLI-crackme$ ./crackme0x03 IOLI Crackme Level 0x03 Password: 338724 Password OK!!! :)
Juguemos un poco mas con el programa.
Esta vez el cambio esta en la impresion.
[0x08048502]> pdf @sym.test ┌ 42: sym.test (int32_t arg_8h, uint32_t arg_ch); │ ; arg int32_t arg_8h @ ebp+0x8 │ ; arg uint32_t arg_ch @ ebp+0xc │ ; CALL XREF from main @ 0x804850c │ 0x0804846e 55 push ebp │ 0x0804846f 89e5 mov ebp, esp │ 0x08048471 83ec08 sub esp, 8 │ 0x08048474 8b4508 mov eax, dword [arg_8h] │ 0x08048477 3b450c cmp eax, dword [arg_ch] │ ┌─< 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 │ ││ ; CODE XREF from sym.test @ 0x804847a │ │└─> 0x0804848a c70424fe8504. mov dword [esp], str.Sdvvzrug_RN ; [0x80485fe:4]=0x76766453 ; "Sdvvzrug#RN$$$#=," │ │ 0x08048491 e87effffff call sym.shift │ │ ; CODE XREF from sym.test @ 0x8048488 │ └──> 0x08048496 c9 leave └ 0x08048497 c3 ret [0x08048502]> q
Si intento simular el codigo de impresion. Parece que funciona
Simulo desde la direccion 0x0804847c hasta la 0x08048488
s 0x0804847c ; aeim ; aeip ; aesu 0x08048488
[0x0804847c]> s 0x0804847c ; aeim ; aeip ; aesu 0x08048488 ^C[+] ESIL emulation interrupted at 0x00ac60af
crackme0x04 (radare)
radare2 -c "aaaa;pdf@main" crackme0x04
-c
es para pasarle comandos :aaaa
, analisis ,pdf@main
impresin y desensamblado funcion main.
jejo@medion:~/Escritorio/reversing/IOLI-crackme$ radare2 -c "aaaa;pdf@main" crackme0x04 [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Check for objc references [x] Check for vtables [x] Type matching analysis for all functions (aaft) [x] Propagate noreturn information [x] Use -AA or aaaa to perform additional experimental analysis. [x] Finding function preludes [x] Enable constraint types analysis for variables ┌ 92: int main (int argc, char **argv, char **envp); │ ; var int32_t var_78h @ ebp-0x78 │ ; var int32_t var_4h @ esp+0x4 │ ; DATA XREF from entry0 @ 0x80483e7 │ 0x08048509 55 push ebp │ 0x0804850a 89e5 mov ebp, esp │ 0x0804850c 81ec88000000 sub esp, 0x88 │ 0x08048512 83e4f0 and esp, 0xfffffff0 │ 0x08048515 b800000000 mov eax, 0 │ 0x0804851a 83c00f add eax, 0xf ; 15 │ 0x0804851d 83c00f add eax, 0xf ; 15 │ 0x08048520 c1e804 shr eax, 4 │ 0x08048523 c1e004 shl eax, 4 │ 0x08048526 29c4 sub esp, eax │ 0x08048528 c704245e8604. mov dword [esp], str.IOLI_Crackme_Level_0x04 ; [0x804865e:4]=0x494c4f49 ; "IOLI Crackme Level 0x04\n" ; const char *format │ 0x0804852f e860feffff call sym.imp.printf ; int printf(const char *format) │ 0x08048534 c70424778604. mov dword [esp], str.Password: ; [0x8048677:4]=0x73736150 ; "Password: " ; const char *format │ 0x0804853b e854feffff call sym.imp.printf ; int printf(const char *format) │ 0x08048540 8d4588 lea eax, [var_78h] │ 0x08048543 89442404 mov dword [var_4h], eax │ 0x08048547 c70424828604. mov dword [esp], 0x8048682 ; [0x8048682:4]=0x7325 ; const char *format │ 0x0804854e e821feffff call sym.imp.scanf ; int scanf(const char *format) │ 0x08048553 8d4588 lea eax, [var_78h] │ 0x08048556 890424 mov dword [esp], eax │ 0x08048559 e826ffffff call sym.check │ 0x0804855e b800000000 mov eax, 0 │ 0x08048563 c9 leave └ 0x08048564 c3 ret [0x080483d0]>
Antes de nada veamos que tipo de cadena se introduce por la funcion scanf
[0x080483d0]> ps @0x8048682 %s
Esta vez es una cadena de texto s tipo string
Esta vez Vamos a practicar con la nueva interfaz Visual
si hago VV @sym.check
y pulso varias veces -
Vere el esquema decisiones de la funcion sym.check.
Nota: puedes moverte por la interfaz con la flechas del teclado.
[0x080483d0]> VV @sym.check
[0x08048484]> 0x8048484 # sym.check (char *s); v │ └─┐ ┌───────────────────────────┐ │ │ │ ┌────────────────────┐ │ │ 0x8048498 [oc] │ │ └────────────────────┘ │ f t │ │ │ │ │ └──────┐ │ ┌────────────────┘ │ │ │ │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ 0x80484a8 [oe] │ │ 0x80484fb [oj] │ │ └────────────────────┘ └────────────────────┘ │ f t │ │ │ │ │ └──────┐ │ ┌────────────────┘ │ │ │ │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ 0x80484dc [oh] │ │ 0x80484f4 [oi] │ │ └────────────────────┘ └────────────────────┘ │ v │ │ │ └───────────────────────────────┘
Se ve claramente que hay dos decisiones (ramas roja y verde)
En la primera se ve claramente como Comprueba la longitud y si la longitud no es correcta salta a password incorrect
[0x08048484]> 0x8048484 # sym.check (char *s); ┌ ────────────────────────────────────────────────┐ │ │ ┌────────────────────────────────────────┐ │ 0x8048498 [oc] │ │ ; CODE XREF from sym.check @ 0x80484f9 │ │ mov eax, dword [s] │ │ ; const char *s │ │ mov dword [esp], eax │ │ ; size_t strlen(const char *s) │ │ call sym.imp.strlen;[ob] │ │ cmp dword [var_ch], eax │ │ jae 0x80484fb │ └────────────────────────────────────────┘ f t │ │ │ └───────────────────┐ ┌───────────────────────────────────────┘ │ │ │ ┌────────────────────────────────────────────────────────┐ ┌────────────────────────────────────────┐ │ 0x80484a8 [oe] │ │ 0x80484fb [oj] │ │ mov eax, dword [var_ch] │ │ ; const char *format │ │ add eax, dword [s] │ │ ; CODE XREF from sym.check @ 0x80484a6 │ │ movzx eax, byte [eax] │ │ ; [0x8048649:4]=0x73736150 │ │ mov byte [var_dh], al │ │ ; "Password Incorrect!\n" │ │ lea eax, [var_4h] │ │ mov dword [esp], str.Password_Incorrect│ │ ; ... │ │ ; int printf(const char *format) │ │ mov dword [var_8h_2], eax │ │ call sym.imp.printf;[of] │ │ ; const char *format │ │ leave │ │ ; [0x8048638:4]=0x50006425 │ │ ret │ │ mov dword [format], 0x8048638 │ └────────────────────────────────────────┘ │ lea eax, [var_dh] │ │ ; const char *s │ │ mov dword [esp], eax │ │ ; int sscanf(const char *s, const char *format, ...) │ │ call sym.imp.sscanf;[od] │ │ mov edx, dword [var_4h] │ │ lea eax, [var_8h] │ │ add dword [eax], edx │ │ cmp dword [var_8h], 0xf │ │ jne 0x80484f4 │ └────────────────────────────────────────────────────────┘ f t │ │
Aqui se ve claramente como Comprueba la longitud
call sym.imp.strlen;[ob] cmp dword [var_ch], eax
La Variable longitud esta en la variable var_ch
Intento ver que valor coge la variable var_ch
Simulando desde 0x08048484 hasta 0x08048498 s 0x08048484 ; aeim ; aeip ; aesu 0x08048498
[0x080483d0]> pdb @sym.check ┌ 133: sym.check (char *s); │ ; var char *var_dh @ ebp-0xd │ ; var uint32_t var_ch @ ebp-0xc │ ; var uint32_t var_8h @ ebp-0x8 │ ; var int32_t var_4h @ ebp-0x4 │ ; arg char *s @ ebp+0x8 │ ; var char *format @ esp+0x4 │ ; var int32_t var_8h_2 @ esp+0x8 │ ; CALL XREF from main @ 0x8048559 │ 0x08048484 55 push ebp │ 0x08048485 89e5 mov ebp, esp │ 0x08048487 83ec28 sub esp, 0x28 │ 0x0804848a c745f8000000. mov dword [var_8h], 0 │ 0x08048491 c745f4000000. mov dword [var_ch], 0 [0x080483d0]> s 0x08048484 ; aeim ; aeip ; aesu 0x08048498 [0x08048484]> afvd var var_8h = 0x00177ff4 = 0 var var_ch = 0x00177ff0 = 0 arg s = 0x00178004 = "" var var_dh = 0x00177fef = "" var var_4h = 0x00177ff8 = 0 var var_8h_2 = 0x00177fdc = 0 var format = 0x00177fd8 = ""
Parece que toma el valor 0.
Luego si la longitud de cadena introducida anteriormente es cero: sale.
Parece que forma parte de un loop en el que se comprueban todos los caracteres del string.
Veamos la otra decision.
│ │ │ ┌────────────────────────────────────────────────────────┐ │ │ 0x80484a8 [oe] │ │ │ mov eax, dword [var_ch] │ │ │ add eax, dword [s] │ │ │ movzx eax, byte [eax] │ │ │ mov byte [var_dh], al │ │ │ lea eax, [var_4h] │ │ │ ; ... │ │ │ mov dword [var_8h_2], eax │ │ │ ; const char *format │ │ │ ; [0x8048638:4]=0x50006425 │ │ │ mov dword [format], 0x8048638 │ │ │ lea eax, [var_dh] │ │ │ ; const char *s │ │ │ mov dword [esp], eax │ │ │ ; int sscanf(const char *s, const char *format, ...) │ │ │ call sym.imp.sscanf;[od] │ │ │ mov edx, dword [var_4h] │ │ │ lea eax, [var_8h] │ │ │ add dword [eax], edx │ │ │ cmp dword [var_8h], 0xf │ │ │ jne 0x80484f4 │ │ └────────────────────────────────────────────────────────┘ │ f t │ │ │ │ │ └───────────────────────┐ │ ┌───────────────┘ │ │ │ │ │┌─────────────────────────────────────┐ ┌────────────────────────────────────────┐ ││ 0x80484dc [oh] │ │ 0x80484f4 [oi] │ ││ ; const char *format │ │ ; CODE XREF from sym.check @ 0x80484da │ ││ ; [0x804863b:4]=0x73736150 │ │ lea eax, [var_ch] │ ││ ; "Password OK!\n" │ │ inc dword [eax] │ ││ mov dword [esp], str.Password_OK │ │ jmp 0x8048498 │ ││ ; int printf(const char *format) │ └────────────────────────────────────────┘ ││ call sym.imp.printf;[of] │ v ││ ; int status │ │ ││ mov dword [esp], 0 │ │ ││ ; void exit(int status) │ │ ││ call sym.imp.exit;[og] │ │ │└─────────────────────────────────────┘ │ │ │ │ │ └──────────────────────────────────────────────┘
Parece que llama a la funcion sscanfs (Read formatted data from string)
http://www.cplusplus.com/reference/cstdio/sscanf/
Para ver la dadena de formato que le pasa a la funcion sscanfs hago:
ps @0x8048638
: print string direccion @0x8048638
[0x08048484]> ps @0x8048638 %d
Veo que lee un numero decimal,
suma su valor a la variable var_8h
y lo compara con 0xf
y realiza un salto condicional jne (salto si no iguales / jump not equals)
lea eax, [var_8h] add dword [eax], edx cmp dword [var_8h], 0xf jne 0x80484f4
Conclusion: parece que la suma de los digitos de la contraseña tienen que set 15 (0xf)
Comprobemos que funciona:
jejo@em50l:~/reversing/IOLI-crackme$ ./crackme0x04 IOLI Crackme Level 0x04 Password: 555 Password OK! jejo@em50l:~/reversing/IOLI-crackme$ ./crackme0x04 IOLI Crackme Level 0x04 Password: 33333 Password OK! jejo@em50l:~/reversing/IOLI-crackme$ ./crackme0x04 IOLI Crackme Level 0x04 Password: 555555 Password OK! jejo@em50l:~/reversing/IOLI-crackme$ ./crackme0x04 IOLI Crackme Level 0x04 Password: 555loquetedelagana Password OK!
crackme0x05 (radare)
radare2 -c "aaaa;VV@sym.check" ./crackme0x05
Como es casi igual que el anterior vamos directos.-c
es para pasarle comandos :aaaa
, analisis ,VV@sym.check
Modo Visual funcion sym.check. Me desplazo con las flechas hasta el jne 0x804852b Y veo que el codigo es casi igual salvo que compara con 0x10 (16) y que la funcion en la que ivisualiza password correct se ha movido a sym.paralell
Esto hace que sea mas dicifil localizar la zona del codigo en la que se comprueba la contraseña.
v │ ┌┘ ┌────────────────────────────────────────────┐ │ │ │ │ ┌────────────────────────────────────────┐ │ │ 0x80484dc [oc] │ │ │ ; CODE XREF from sym.check @ 0x8048530 │ │ │ mov eax, dword [s] │ │ │ ; const char *s │ │ │ mov dword [esp], eax │ │ │ ; size_t strlen(const char *s) │ │ │ call sym.imp.strlen;[ob] │ │ │ cmp dword [var_ch], eax │ │ │ jae 0x8048532 │ │ └────────────────────────────────────────┘ │ f t │ │ │ │ │ └───────────────────┐ │ ┌───────────────────────────────────────┘ │ │ │ │ │┌────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────┐ ││ 0x80484ec [oe] │ │ 0x8048532 [oj] │ ││ mov eax, dword [var_ch] │ │ ; const char *format │ ││ add eax, dword [s] │ │ ; CODE XREF from sym.check @ 0x80484ea │ ││ movzx eax, byte [eax] │ │ ; [0x8048679:4]=0x73736150 │ ││ mov byte [var_dh], al │ │ ; "Password Incorrect!\n" │ ││ lea eax, [var_4h] │ │ mov dword [esp], str.Password_Incorrect │ ││ ; ... │ │ ; int printf(const char *format) │ ││ mov dword [var_8h_2], eax │ │ call sym.imp.printf;[oi] │ ││ ; const char *format │ │ leave │ ││ ; [0x8048668:4]=0x50006425 │ │ ret │ ││ mov dword [format], 0x8048668 │ └─────────────────────────────────────────┘ ││ lea eax, [var_dh] │ ││ ; const char *s │ ││ mov dword [esp], eax │ ││ ; int sscanf(const char *s, const char *format, ...) │ ││ call sym.imp.sscanf;[od] │ ││ mov edx, dword [var_4h] │ ││ lea eax, [var_8h] │ ││ add dword [eax], edx │ ││ cmp dword [var_8h], 0x10 │ ││ jne 0x804852b │ │└────────────────────────────────────────────────────────┘ │ f t │ │ │ │ │ └───────────────────────────────────────────────────────────────────────────┐ │ └───────────┐ │ │ │ │ │ ┌─────────────────────────┐ │ │ │ [0x8048520] │ │ │ │ mov eax, dword [s] │ │ │ │ mov dword [esp], eax │ │ │ │ call sym.parell;[of] │ │ │ └─────────────────────────┘ │ │ v │ │ │ │ │ └───────────────────────────┐ │ │ │ ┌───────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────┐ │ │ 0x804852b [oh] │ │ │ ; CODE XREF from sym.check @ 0x804851e │ │ │ lea eax, [var_ch] │ │ │ inc dword [eax] │ │ │ jmp 0x80484dc │ │ └────────────────────────────────────────┘ │ v │ │ └──────────────────────────────────────────────┘
Truco: si te fijas en call sym.parell;[of]
significa que tecleando of voy a la funcion sym.parell
Aunque tambien puedes ir a la funcion con el comando VV @sym.parell
[0x080483d0]> VV @sym.parell [0x08048484]> 0x8048484 # sym.parell (char *s); ┌────────────────────────────────────────────────────────┐ │ [0x8048484] │ │ 68: sym.parell (char *s); │ │ ; var int32_t var_4h @ ebp-0x4 │ │ ; arg char *s @ ebp+0x8 │ │ ; var char *format @ esp+0x4 │ │ ; var int32_t var_8h @ esp+0x8 │ │ ; CALL XREF from sym.check @ 0x8048526 │ │ push ebp │ │ mov ebp, esp │ │ sub esp, 0x18 │ │ lea eax, [var_4h] │ │ ; ... │ │ mov dword [var_8h], eax │ │ ; const char *format │ │ ; [0x8048668:4]=0x50006425 │ │ mov dword [format], 0x8048668 │ │ mov eax, dword [s] │ │ ; const char *s │ │ mov dword [esp], eax │ │ ; int sscanf(const char *s, const char *format, ...) │ │ call sym.imp.sscanf;[oa] │ │ mov eax, dword [var_4h] │ │ and eax, 1 │ │ test eax, eax │ │ jne 0x80484c6 │ └────────────────────────────────────────────────────────┘ f t │ │ │ └───────────────────────┐ ┌───────────────┘ │ │ │ ┌─────────────────────────────────────┐ ┌─────────────────────────────────────────┐ │ 0x80484ae [oe] │ │ 0x80484c6 [of] │ │ ; const char *format │ │ ; CODE XREF from sym.parell @ 0x80484ac │ │ ; [0x804866b:4]=0x73736150 │ │ leave │ │ ; "Password OK!\n" │ │ ret │ │ mov dword [esp], str.Password_OK │ └─────────────────────────────────────────┘ │ ; int printf(const char *format) │ │ call sym.imp.printf;[oc] │ │ ; int status │ │ mov dword [esp], 0 │ │ ; void exit(int status) │ │ call sym.imp.exit;[od] │ └─────────────────────────────────────┘
aqui tienes el password_OK
Comprobemos que funciona:
jejo@medion:~/Escritorio/reversing/IOLI-crackme$ ./crackme0x05 IOLI Crackme Level 0x05 Password: 88 Password OK! jejo@medion:~/Escritorio/reversing/IOLI-crackme$ ./crackme0x05 IOLI Crackme Level 0x05 Password: 88loquesea Password OK!
crackme0x06 (radare)
- Esta vez tengo que tirar de debug:
r2 -AA -d ./crackme0x06
db sym.imp.strncmp
pongo un breakpoint en la funcion strcmp
dc ; drr
debug continuar (saltara en el break potint) y debug muestro registros
Observo que con cada interaccion solo cambioa el registro A0 o eax Y parece que va apuntando a las variables del sistema
[0xf7fa4c70]> db sym.imp.strncmp [0xf7fa4c70]> dc ; drr IOLI Crackme Level 0x06 Password: 556 hit breakpoint at: 80483d8 A0 eax 0xffbd339c ([stack]) stack R W 0x435f534c (LS_COLORS=
[0x080483d8]> dc ; drr hit breakpoint at: 80483d8 A0 eax 0xffbd3988 ([stack]) stack R W 0x5353454c (LESSCLOSE=
Echandole un ojo a la funcin en la que esta el strcnmp veo que lo compara con la cadena LOL
En realidad compara con LOLO con un tamaño de 3 luego LOL
[0x080483d8]> pdf @sym.dummy ┌ 102: sym.dummy (int32_t arg_ch); │ ; CALL XREF from sym.parell @ 0x8048547 │ ...... │ │╎ 0x080484e6 c74424080300. mov dword [var_8h_2], 3 │ │╎ 0x080484ee c74424043887. mov dword [var_4h_2], str.LOLO ; [0x8048738:4]=0x4f4c4f4c ; "LOLO" │ │╎ 0x080484f6 8b0411 mov eax, dword [ecx + edx] │ │╎ 0x080484f9 890424 mov dword [esp], eax │ │╎ 0x080484fc e8d7feffff call sym.imp.strncmp; int strncmp(const char *s1, const char *s2, size_t n) │ │╎ 0x08048501 85c0 test eax, eax │ │└─<0x08048503 75bc jne 0x80484c1 │ ...... └ 0x08048519 c3 ret [0x080483d8]>
Probemos que funciona:
jejo@medion:~/Escritorio/reversing/IOLI-crackme$ LOL=? ./crackme0x06 IOLI Crackme Level 0x06 Password: 88 Password OK!
crackme0x07
crackme0x08
crackme0x09
Son iguales solo que con algunas funciones añadidas.
o compilados de forma distinta
Notas
Referencias
https://beginners.re/RE4B-EN.pdf
https://www.megabeets.net/a-journey-into-radare-2-part-1/ https://www.megabeets.net/a-journey-into-radare-2-part-2/
https://radare.gitbooks.io/radare2book/content/crackmes/ioli/intro.html
https://dustri.org/b/defeating-ioli-with-radare2.html
https://dustri.org/b/defeating-ioli-with-radare2-in-2017.html