While procrastinating, I recently stumbled upon an article about a simple crackme challenge. The challenge was to create a program that would generate semi-random password strings that would pass the program’s “checksum” function. I see the educational purpose in this challenge, but I asked myself “why not just change the program to accept any password?”, which would probably be a more common use case.
Radare2 had surfaced a few times in my twitter feed, so this seemed like the right time to do my first itty-bitty steps with it. And surely enough, this was dead simple:
Open the file in r2 and analyze all flags
% r2 101-crackme
[0x00400470]> aa
[x] Analyze all flags starting with sym. and entry0 (aa)
Print the disassembly of the main function
[0x00400470]> pdf @ main
;-- main:
/ (fcn) sym.main 120
| sym.main ();
| ; var int local_20h @ rbp-0x20
| ; var int local_14h @ rbp-0x14
| ; var int local_8h @ rbp-0x8
| ; DATA XREF from 0x0040048d (entry0)
| 0x0040059d 55 push rbp
| 0x0040059e 4889e5 mov rbp, rsp
| 0x004005a1 4883ec20 sub rsp, 0x20
| 0x004005a5 897dec mov dword [rbp - local_14h], edi
| 0x004005a8 488975e0 mov qword [rbp - local_20h], rsi
| 0x004005ac 837dec02 cmp dword [rbp - local_14h], 2 ; [0x2:4]=0x102464c
| ,=< 0x004005b0 7420 je 0x4005d2
| | 0x004005b2 488b45e0 mov rax, qword [rbp - local_20h]
| | 0x004005b6 488b00 mov rax, qword [rax]
| | 0x004005b9 4889c6 mov rsi, rax
| | 0x004005bc bfa4064000 mov edi, str.Usage:__s_password_n ; "Usage: %s password." @ 0x4006a4
| | 0x004005c1 b800000000 mov eax, 0
| | 0x004005c6 e875feffff call sym.imp.printf ; int printf(const char *format);
| | 0x004005cb b801000000 mov eax, 1
| ,==< 0x004005d0 eb41 jmp 0x400613
| |`-> 0x004005d2 488b45e0 mov rax, qword [rbp - local_20h]
| | 0x004005d6 4883c008 add rax, 8
| | 0x004005da 488b00 mov rax, qword [rax]
| | 0x004005dd 4889c7 mov rdi, rax
| | 0x004005e0 e881ffffff call sym.checksum
| | 0x004005e5 488945f8 mov qword [rbp - local_8h], rax
| | 0x004005e9 48817df8d40a. cmp qword [rbp - local_8h], 0xad4 ; [0xad4:8]=0
| |,=< 0x004005f1 7411 je 0x400604
| || 0x004005f3 bfb8064000 mov edi, str.Wrong_password ; "Wrong password" @ 0x4006b8
| || 0x004005f8 e833feffff call sym.imp.puts ; int puts(const char *s);
| || 0x004005fd b801000000 mov eax, 1
| ,===< 0x00400602 eb0f jmp 0x400613
| ||`-> 0x00400604 bfc7064000 mov edi, str._a_a_aTada__Congrats ; str._a_a_aTada__Congrats
| || 0x00400609 e822feffff call sym.imp.puts ; int puts(const char *s);
| || 0x0040060e b800000000 mov eax, 0
| || ; JMP XREF from 0x00400602 (sym.main)
| || ; JMP XREF from 0x004005d0 (sym.main)
| ``--> 0x00400613 c9 leave
\ 0x00400614 c3 ret
[0x00400470]>
We can see the suspicious je instruction at 0x004005f1 - if the comparison in the previous line yields an equal, it would jump ahead to 0x400604, otherwise it would print “Wrong password”. So we if we just replace the je with an unconditional jmp, we are done.
So we go to (seek) 0x004005f1
[0x00400470]> s 0x004005f1
[0x004005f1]>
… reopen the file in read-write mode and write our desired jmp instruction to the file.
[0x004005f1]> oo+
File 101-crackme reopened in read-write mode
[0x004005f1]> wa jmp 0x400604
Written 2 bytes (jmp 0x400604) = wx eb11
[0x004005f1]>
And double check
[0x004005f1]> pdf
;-- main:
/ (fcn) sym.main 120
| sym.main ();
| ; var int local_20h @ rbp-0x20
| ; var int local_14h @ rbp-0x14
| ; var int local_8h @ rbp-0x8
| ; DATA XREF from 0x0040048d (entry0)
| 0x0040059d 55 push rbp
| 0x0040059e 4889e5 mov rbp, rsp
| 0x004005a1 4883ec20 sub rsp, 0x20
| 0x004005a5 897dec mov dword [rbp - local_14h], edi
| 0x004005a8 488975e0 mov qword [rbp - local_20h], rsi
| 0x004005ac 837dec02 cmp dword [rbp - local_14h], 2 ; [0x2:4]=0x102464c
| ,=< 0x004005b0 7420 je 0x4005d2
| | 0x004005b2 488b45e0 mov rax, qword [rbp - local_20h]
| | 0x004005b6 488b00 mov rax, qword [rax]
| | 0x004005b9 4889c6 mov rsi, rax
| | 0x004005bc bfa4064000 mov edi, str.Usage:__s_password_n ; "Usage: %s password." @ 0x4006a4
| | 0x004005c1 b800000000 mov eax, 0
| | 0x004005c6 e875feffff call sym.imp.printf ; int printf(const char *format);
| | 0x004005cb b801000000 mov eax, 1
| ,==< 0x004005d0 eb41 jmp 0x400613
| |`-> 0x004005d2 488b45e0 mov rax, qword [rbp - local_20h]
| | 0x004005d6 4883c008 add rax, 8
| | 0x004005da 488b00 mov rax, qword [rax]
| | 0x004005dd 4889c7 mov rdi, rax
| | 0x004005e0 e881ffffff call sym.checksum
| | 0x004005e5 488945f8 mov qword [rbp - local_8h], rax
| | 0x004005e9 48817df8d40a. cmp qword [rbp - local_8h], 0xad4 ; [0xad4:8]=0
| |,=< 0x004005f1 eb11 jmp 0x400604
| || 0x004005f3 bfb8064000 mov edi, str.Wrong_password ; "Wrong password" @ 0x4006b8
| || 0x004005f8 e833feffff call sym.imp.puts ; int puts(const char *s);
| || 0x004005fd b801000000 mov eax, 1
| ,===< 0x00400602 eb0f jmp 0x400613
| ||`-> 0x00400604 bfc7064000 mov edi, str._a_a_aTada__Congrats ; str._a_a_aTada__Congrats
| || 0x00400609 e822feffff call sym.imp.puts ; int puts(const char *s);
| || 0x0040060e b800000000 mov eax, 0
| || ; JMP XREF from 0x00400602 (sym.main)
| || ; JMP XREF from 0x004005d0 (sym.main)
| ``--> 0x00400613 c9 leave
\ 0x00400614 c3 ret
So we leave r2 with q and check:
[0x004005f1]> q
% ./101-crackme somethingrandom
Tada! Congrats
And we’re done.