IOLI 0x07

a weird “wtf?” string.

  1. $ rabin2 -z ./crackme0x07
  2. [Strings]
  3. nth paddr vaddr len size section type string
  4. -------------------------------------------------------
  5. 0 0x000007a8 0x080487a8 4 5 .rodata ascii LOLO
  6. 1 0x000007ad 0x080487ad 20 21 .rodata ascii Password Incorrect!\n
  7. 2 0x000007c5 0x080487c5 13 14 .rodata ascii Password OK!\n
  8. 3 0x000007d3 0x080487d3 5 6 .rodata ascii wtf?\n
  9. 4 0x000007d9 0x080487d9 24 25 .rodata ascii IOLI Crackme Level 0x07\n
  10. 5 0x000007f2 0x080487f2 10 11 .rodata ascii Password:

again, no password string or compare in main(). I put the simplified pseudo code here. var_78h is likely to a char *pointer (string) .

  1. #include <stdint.h>
  2. int32_t main (int32_t arg_10h) {
  3. printf ("IOLI Crackme Level 0x07\n");
  4. printf ("Password: ");
  5. scanf (%s, &var_78h);
  6. return fcn_080485b9 (&var_78h, arg_10h);
  7. }

due to the symbol info lost, neither aa nor aaa show the name of functions. we can double check this in “flagspace”. Radare2 use fcn_080485b9 as the function name. It’s a common case in reverse engineering that we don’t have any symbol info of the binary.

  1. [0x080487fd]> fs symbols
  2. [0x080487fd]> f
  3. 0x08048400 33 entry0
  4. 0x0804867d 92 main
  5. 0x080487a4 4 obj._IO_stdin_used

decompile the fcn_080485b9():

  1. [0x080485b9]> pdfc
  2. ; CALL XREF from main @ 0x80486d4
  3. / 118: fcn.080485b9 (char *s, int32_t arg_ch);
  4. | ; var char *var_dh @ ebp-0xd
  5. | ; var signed int var_ch { >= 0xffffffffffffffff} @ ebp-0xc
  6. | ; var uint32_t var_8h @ ebp-0x8
  7. | ; var int32_t var_bp_4h @ ebp-0x4
  8. | ; arg char *s @ ebp+0x8
  9. | ; arg int32_t arg_ch @ ebp+0xc
  10. | ; var char *format @ esp+0x4
  11. | ; var int32_t var_sp_8h @ esp+0x8
  12. | 0x080485b9 55 push ebp
  13. | 0x080485ba 89e5 mov ebp, esp
  14. | 0x080485bc 83ec28 sub esp, 0x28
  15. | 0x080485bf c745f8000000. mov dword [var_8h], 0
  16. | 0x080485c6 c745f4000000. mov dword [var_ch], 0
  17. | ; CODE XREF from fcn.080485b9 @ 0x8048628
  18. | .-> 0x080485cd 8b4508 mov eax, dword [s]
  19. | : 0x080485d0 890424 mov dword [esp], eax ; const char *s
  20. | : 0x080485d3 e8d0fdffff call sym.imp.strlen ; size_t strlen(const char *s)
  21. | : 0x080485d8 3945f4 cmp dword [var_ch], eax
  22. | ,==< 0x080485db 734d jae 0x804862a
  23. | |: 0x080485dd 8b45f4 mov eax, dword [var_ch]
  24. | |: 0x080485e0 034508 add eax, dword [s]
  25. | |: 0x080485e3 0fb600 movzx eax, byte [eax]
  26. | |: 0x080485e6 8845f3 mov byte [var_dh], al
  27. | |: 0x080485e9 8d45fc lea eax, [var_bp_4h]
  28. | |: 0x080485ec 89442408 mov dword [var_sp_8h], eax ; ...
  29. | |: 0x080485f0 c7442404c287. mov dword [format], 0x80487c2 ; [0x80487c2:4]=0x50006425 ; const char *format
  30. | |: ;-- eip:
  31. | |: 0x080485f8 8d45f3 lea eax, [var_dh]
  32. | |: 0x080485fb 890424 mov dword [esp], eax ; const char *s
  33. | |: 0x080485fe e8c5fdffff call sym.imp.sscanf ; int sscanf(const char *s, const char *format, ...)
  34. | |: 0x08048603 8b55fc mov edx, dword [var_bp_4h]
  35. | |: 0x08048606 8d45f8 lea eax, [var_8h]
  36. | |: 0x08048609 0110 add dword [eax], edx
  37. | |: 0x0804860b 837df810 cmp dword [var_8h], 0x10
  38. | ,===< 0x0804860f 7512 jne 0x8048623
  39. | ||: 0x08048611 8b450c mov eax, dword [arg_ch]
  40. | ||: 0x08048614 89442404 mov dword [format], eax ; char *arg_ch
  41. | ||: 0x08048618 8b4508 mov eax, dword [s]
  42. | ||: 0x0804861b 890424 mov dword [esp], eax ; char *s
  43. | ||: 0x0804861e e81fffffff call fcn.08048542
  44. | ||: ; CODE XREF from fcn.080485b9 @ 0x804860f
  45. | `---> 0x08048623 8d45f4 lea eax, [var_ch]
  46. | |: 0x08048626 ff00 inc dword [eax]
  47. | |`=< 0x08048628 eba3 jmp 0x80485cd
  48. | | ; CODE XREF from fcn.080485b9 @ 0x80485db
  49. \ `--> 0x0804862a e8f5feffff call fcn.08048524

we got familiar with this code structure in the previous challenges (the check function). It’s not difficult for us even we don’t have the symbol info. you can also use afn command to rename the function name if you like.

  1. int32_t fcn_080485b9 (char * s, void* envp)
  2. {
  3. var_ch = 0;
  4. var_8h = 0;
  5. for (var_ch = 0; var_ch < strlen(s); ++var_ch)
  6. {
  7. var_dh = s[var_ch];
  8. sscanf(&var_dh, %d, &var_4h); // read from string[var_ch], store to var_4h
  9. var_8h += var_4h;
  10. if(var_8h == 0x10)
  11. fcn_08048542(s, envp);
  12. }
  13. return fcn_08048524();
  14. }

most part of crackme 0x07 is the same with 0x06. and it can be solved by the same password & environment:

  1. $ export LOLAA=help
  2. $ ./cracke0x07
  3. IOLI Crackme Level 0x07
  4. Password: 12346
  5. Password OK!

wait … where is the ‘wtf?’. Often, we would like to find the cross reference (xref) to strings (or data, functions, etc.) in reverse engineering. The related commands in Radare2 are under “ax” namespace:

  1. [0x08048400]> f
  2. 0x080487a8 5 str.LOLO
  3. 0x080487ad 21 str.Password_Incorrect
  4. 0x080487c5 14 str.Password_OK
  5. 0x080487d3 6 str.wtf
  6. 0x080487d9 25 str.IOLI_Crackme_Level_0x07
  7. 0x080487f2 11 str.Password:
  8. [0x08048400]> axt 0x80487d3
  9. (nofunc) 0x804865c [DATA] mov dword [esp], str.wtf
  10. [0x08048400]> axF str.wtf
  11. Finding references of flags matching 'str.wtf'...
  12. [0x001eff28-0x001f0000] (nofunc) 0x804865c [DATA] mov dword [esp], str.wtf
  13. Macro 'findstref' removed.

the [DATA] mov dword [esp], str.wtf at 0x804865c is an instruction of fcn.080485b9. But the analysis in my PC ignores the remained instructions and only display the incomplete assembly. the range of fcn.080485b9 should be 0x080485b9 ~ 0x0804867c . we can reset block size and print opcodes.

  1. [0x08040000]> s 0x080485b9
  2. [0x080485b9]> b 230
  3. [0x08048400]> pd
  4. ...
  5. 0x0804862f 8b450c mov eax, dword [ebp + 0xc]
  6. 0x08048632 89442404 mov dword [esp + 4], eax
  7. 0x08048636 8b45fc mov eax, dword [ebp - 4]
  8. 0x08048639 890424 mov dword [esp], eax ; char **s1
  9. 0x0804863c e873feffff call fcn.080484b4
  10. 0x08048641 85c0 test eax, eax
  11. ,=< 0x08048643 7436 je 0x804867b
  12. | 0x08048645 c745f4000000. mov dword [ebp - 0xc], 0
  13. | ; CODE XREF from fcn.080485b9 @ +0xc0
  14. .--> 0x0804864c 837df409 cmp dword [ebp - 0xc], 9
  15. ,===< 0x08048650 7f29 jg 0x804867b
  16. |:| 0x08048652 8b45fc mov eax, dword [ebp - 4]
  17. |:| 0x08048655 83e001 and eax, 1
  18. |:| 0x08048658 85c0 test eax, eax
  19. ,====< 0x0804865a 7518 jne 0x8048674
  20. ||:| 0x0804865c c70424d38704. mov dword [esp], str.wtf ; [0x80487d3:4]=0x3f667477 ; "wtf?\n" ; const char *format
  21. ||:| 0x08048663 e850fdffff call sym.imp.printf ; int printf(const char *format)
  22. ||:| 0x08048668 c70424000000. mov dword [esp], 0 ; int status
  23. ||:| 0x0804866f e874fdffff call sym.imp.exit ; void exit(int status)
  24. ||:| ; CODE XREF from fcn.080485b9 @ +0xa1
  25. `----> 0x08048674 8d45f4 lea eax, [ebp - 0xc]
  26. |:| 0x08048677 ff00 inc dword [eax]
  27. |`==< 0x08048679 ebd1 jmp 0x804864c
  28. | | ; CODE XREFS from fcn.080485b9 @ +0x8a, +0x97
  29. `-`-> 0x0804867b c9 leave
  30. 0x0804867c c3 ret

test eax, ea;je 0x804867b will jump to leave; ret, which forever skips the str.wtf part. only use aa to analyze this binary can display the whole function.