Reverse Engineering with Radare2
For reverse engineering there are many tools, but one open source project really caught my attention:
Radare2. Radare2 is written in C, designed for use in the $SHELL, but it also comes with a GUI called iaito.
For me, it leaves nothing to be desired. The latest version can be downloaded from https://github.com/radareorg/radare2/releases
With the r2 package manager r2pm
, plugins can be installed easily:
r2pm -U # update the plugin database
r2pm -ci r2ghidra # ghidra decompiler
r2pm -ci r2dec # pseudo C representation
Next, add the following to your configuration file ~/.radare2rc
:
e bin.relocs.apply=true
Now the reversing can begin.
I created some small challenges that you can solve:
git clone https://github.com/jhelb1993/crackme_challanges.git
cd crackme_challanges
make
We start with crackme_01:
r2 -A -w crackme_01
The following output should appear:
home$ r2 -A -w crackme_01
INFO: Analyze all flags starting with sym. and entry0 (aa)
INFO: Analyze imports (af@@@i)
INFO: Analyze entrypoint (af@ entry0)
INFO: Analyze symbols (af@@@s)
INFO: Analyze all functions arguments/locals (afva@@@F)
INFO: Analyze function calls (aac)
INFO: Analyze len bytes of instructions for references (aar)
INFO: Finding and parsing C++ vtables (avrr)
INFO: Analyzing methods (af @@ method.*)
INFO: Recovering local variables (afva@@@F)
INFO: Type matching analysis for all functions (aaft)
INFO: Propagate noreturn information (aanr)
INFO: Use -AA or aaaa to perform additional experimental analysis
-- Welcome back, lazy human!
[0x00001780]>
To list functions we can use afl
:
[0x00001780]> afl
0x00001c00 1 10 sym.imp._csu_finish
0x00001c10 1 10 sym.imp.exit
0x00001c30 1 10 sym.imp.atexit
0x00001c40 1 10 sym.imp.puts
0x00001c50 1 10 sym.imp.strcmp
0x00001c60 1 10 sym.imp.printf
0x000017b0 9 233 fcn.000017b0
0x00001780 1 35 entry0
0x00001b40 1 18 sym.__init
0x000018f0 14 212 fcn.000018f0
0x000018c0 3 35 sym.__register_frame_info
0x00001c20 1 10 loc.imp._Jv_RegisterClasses
0x00001a80 8 178 main
0x00001b60 1 18 sym.__fini
0x000019e0 11 147 fcn.000019e0
[0x00001780]>
A disassembly of main
can be displayed with pdf @ main
:
[0x00001780]> pdf @ main
; CALL XREF from fcn.000017b0 @ 0x18a6(x)
/ 178: int main (uint32_t argc, char **argv);
| `- args(rdi, rsi) vars(3:sp[0x14..0x20])
| 0x00001a80 f30f1efa endbr64
| 0x00001a84 4c8b1d0d12.. mov r11, qword [obj.__retguard_3982] ; [0x2c98:8]=0
| 0x00001a8b 4c331c24 xor r11, qword [rsp]
| 0x00001a8f 55 push rbp
| 0x00001a90 4889e5 mov rbp, rsp
| 0x00001a93 4153 push r11
| 0x00001a95 4883ec18 sub rsp, 0x18
| 0x00001a99 c745f40000.. mov dword [var_ch], 0
| 0x00001aa0 897df0 mov dword [var_10h], edi ; argc
| 0x00001aa3 488975e8 mov qword [var_18h], rsi ; argv
| 0x00001aa7 837df002 cmp dword [var_10h], 2
| ,=< 0x00001aab 0f8418000000 je 0x1ac9
| | 0x00001ab1 488d3d63eb.. lea rdi, str.Usage:_._crackme_01_KEY ; 0x61b ; "Usage: ./crackme_01 KEY" ; const char *s
| | 0x00001ab8 e883010000 call sym.imp.puts ; int puts(const char *s)
| | 0x00001abd c745f40100.. mov dword [var_ch], 1
| ,==< 0x00001ac4 e94c000000 jmp 0x1b15
| || ; CODE XREF from main @ 0x1aab(x)
| |`-> 0x00001ac9 488b45e8 mov rax, qword [var_18h]
| | 0x00001acd 488b7808 mov rdi, qword [rax + 8] ; const char *s1
| | 0x00001ad1 488d355beb.. lea rsi, str.kauw_oPOq_ADd9_sGGf ; 0x633 ; "kauw-oPOq-ADd9-sGGf" ; const char *s2
| | 0x00001ad8 e873010000 call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
| | 0x00001add 83f800 cmp eax, 0
| |,=< 0x00001ae0 0f851a000000 jne 0x1b00
| || 0x00001ae6 488d3d13eb.. lea rdi, str.Your_crackme_is_licensed__n ; section..rodata
| || ; 0x600 ; "Your crackme is licensed!\n" ; const char *format
| || 0x00001aed b000 mov al, 0
| || 0x00001aef e86c010000 call sym.imp.printf ; int printf(const char *format)
| || 0x00001af4 c745f40000.. mov dword [var_ch], 0
| ,===< 0x00001afb e915000000 jmp 0x1b15
| ||| ; CODE XREF from main @ 0x1ae0(x)
| ||`-> 0x00001b00 488d3d40eb.. lea rdi, str.Invalid_license__n ; 0x647 ; "Invalid license!\n" ; const char *format
| || 0x00001b07 b000 mov al, 0
| || 0x00001b09 e852010000 call sym.imp.printf ; int printf(const char *format)
| || 0x00001b0e c745f40100.. mov dword [var_ch], 1
| || ; CODE XREFS from main @ 0x1ac4(x), 0x1afb(x)
| ``--> 0x00001b15 8b45f4 mov eax, dword [var_ch]
| 0x00001b18 4883c418 add rsp, 0x18
| 0x00001b1c 415b pop r11
| 0x00001b1e 5d pop rbp
| 0x00001b1f 4c331c24 xor r11, qword [rsp]
| 0x00001b23 4c3b1d6e11.. cmp r11, qword [obj.__retguard_3982] ; [0x2c98:8]=0
| ,=< 0x00001b2a 0f840f000000 je 0x1b3f
| | 0x00001b30 cc int3
..
| | ; CODE XREF from main @ 0x1b2a(x)
\ `-> 0x00001b3f c3 ret
The key section for us is:
| | 0x00001ad1 488d355beb.. lea rsi, str.kauw_oPOq_ADd9_sGGf ; 0x633 ; "kauw-oPOq-ADd9-sGGf"
| | 0x00001ad8 e873010000 call sym.imp.strcmp
| | 0x00001add 83f800 cmp eax, 0
| |,=< 0x00001ae0 0f851a000000 jne 0x1b00
| || 0x00001ae6 488d3d13eb.. lea rdi, str.Your_crackme_is_licensed__n
At 0x00001ae0
we write a jmp 0x00001ae6
:
[0x00001780]> wa jmp 0x00001ae6 @ 0x00001ae0
INFO: Written 2 byte(s) (jmp 0x00001ae6) = wx eb04 @ 0x00001ae0
[0x00001780]> q
Now the license check will be bypassed:
home$ ./crackme_01 test123lololol
Your crackme is licensed!
Pseudo code with r2dec and r2ghidra can be displayed with:
pdda @ main # r2dec
pdga @ main # r2ghidra
Example output from pdda @ main
:
[0x00001780]> pdda @ main
; assembly | /* r2dec pseudo code output (r2 6.0.3) */
| /* crackme_01 @ 0x1a80 */
| #include <stdint.h>
|
; (fcn) main () | int32_t main (uint32_t argc, char ** argv) {
| char ** var_18h;
| uint32_t var_10h;
| int64_t var_ch;
| rdi = argc;
| rsi = argv;
0x00001a80 endbr64 |
0x00001a84 mov r11, qword [rip + 0x120d] | r11 = *(obj.__retguard_3982);
0x00001a8b xor r11, qword [rsp] | r11 ^= *(rsp);
0x00001a8f push rbp |
0x00001a90 mov rbp, rsp |
0x00001a93 push r11 |
0x00001a95 sub rsp, 0x18 |
0x00001a99 mov dword [rbp - 0xc], 0 | *((rbp - 0xc)) = 0;
0x00001aa0 mov dword [rbp - 0x10], edi | *((rbp - 0x10)) = edi;
0x00001aa3 mov qword [rbp - 0x18], rsi | *((rbp - 0x18)) = rsi;
0x00001aa7 cmp dword [rbp - 0x10], 2 |
| if (*((rbp - 0x10)) != 2) {
0x00001aab je 0x1ac9 |
0x00001ab1 lea rdi, [rip - 0x149d] |
0x00001ab8 call 0x1c40 | puts ("Usage: ./crackme_01 KEY");
0x00001abd mov dword [rbp - 0xc], 1 | *((rbp - 0xc)) = 1;
0x00001ac4 jmp 0x1b15 |
| } else {
0x00001ac9 mov rax, qword [rbp - 0x18] | rax = *((rbp - 0x18));
0x00001acd mov rdi, qword [rax + 8] |
0x00001ad1 lea rsi, [rip - 0x14a5] |
0x00001ad8 call 0x1c50 | eax = strcmp (*((rax + 8)), "kauw-oPOq-ADd9-sGGf");
0x00001add cmp eax, 0 |
| if (eax == 0) {
0x00001ae0 jne 0x1b00 |
0x00001ae6 lea rdi, [rip - 0x14ed] |
0x00001aed mov al, 0 | al = 0;
0x00001aef call 0x1c60 | printf ("Your crackme is licensed!\n");
0x00001af4 mov dword [rbp - 0xc], 0 | *((rbp - 0xc)) = 0;
0x00001afb jmp 0x1b15 |
| } else {
0x00001b00 lea rdi, [rip - 0x14c0] |
0x00001b07 mov al, 0 | al = 0;
0x00001b09 call 0x1c60 | printf ("Invalid license!\n");
0x00001b0e mov dword [rbp - 0xc], 1 | *((rbp - 0xc)) = 1;
| }
| }
0x00001b15 mov eax, dword [rbp - 0xc] | eax = *((rbp - 0xc));
0x00001b18 add rsp, 0x18 |
0x00001b1c pop r11 |
0x00001b1e pop rbp |
0x00001b1f xor r11, qword [rsp] | r11 ^= *(rsp);
0x00001b23 cmp r11, qword [rip + 0x116e] |
| if (r11 != *(obj.__retguard_3982)) {
0x00001b2a je 0x1b3f |
0x00001b30 int3 | __asm ("int3");
| }
0x00001b3f ret | return rax;
| }
[0x00001780]>
Example output from pdga @ main
:
[0x00001780]> pdga @ main
|
0x00001a80 endbr64 |uint64_t main(int param_1,int64_t param_2)
|
|{
| code *pcVar1;
| int64_t iVar2;
| int iVar3;
| uint64_t uVar4;
| uint uStack_14;
|
0x00001a84 mov r11, qword [rip + 0x120d] | iVar2 = _obj.__retguard_3982;
0x00001aab je 0x1ac9 | if (param_1 == 2) {
0x00001ad8 call sym.imp.strcmp | iVar3 = sym.imp.strcmp(*(param_2 + 8),0x633);
0x00001ae0 jne 0x1b00 | if (iVar3 == 0) {
0x00001aef call sym.imp.printf | sym.imp.printf(0x600);
0x00001af4 mov dword [rbp - 0xc], 0 | uStack_14 = 0;
| }
| else {
0x00001b09 call sym.imp.printf | sym.imp.printf(0x647);
0x00001b0e mov dword [rbp - 0xc], 1 | uStack_14 = 1;
| }
| }
| else {
0x00001ab8 call sym.imp.puts | sym.imp.puts(0x61b);
0x00001abd mov dword [rbp - 0xc], 1 | uStack_14 = 1;
| }
0x00001b2a je 0x1b3f | if (iVar2 != _obj.__retguard_3982) {
0x00001b30 int3 | pcVar1 = swi(3);
0x00001b30 int3 | uVar4 = (*pcVar1)();
0x00001b30 int3 | return uVar4;
| }
0x00001b3f ret | return uStack_14;
|}
|
[0x00001780]>