Jan Helblings Seite

Reverse Engineering mit Radare2

Für das Reverse Engineering gibt es viele Tools, eines davon das Open Source ist, ist mir besonders ins Auge gestochen: Radare2, entwickelt in C für die Benutzung in der $SHELL, jedoch auch als GUI mit iaito verwendbar, lässt mir keine Wünsche offen. Die aktuelle version kann unter https://github.com/radareorg/radare2/releases heruntergeladen und installiert werden. Über den r2 package manager r2pm können Plugis heruntergeladen und nachinstalliert werden.

r2pm -U              # updatet die Datenbank der Plugins
r2pm -ci r2ghidra    # ghidra decompiler
r2pm -ci r2dec       # Pseudo C Codedarstellung

Danach setzen wir in die konfiguration ~/.radare2rc:

e bin.relocs.apply=true

Nun kann das reverse engineeren beginnen. Ich habe ein paar challanges erstellt, die es zu lösen gilt:

git clone https://github.com/jhelb1993/crackme_challanges.git
cd crackme_challanges
make

Wir beginnen mit crackme_01:

r2 -A -w crackme_01

Dann sollte folgender output kommen:

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]>

Um die Funktionen aufzulisten verwenden wir 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]> 

Ein Disassembly output von main bekommen wir mit 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

Für uns ist folgender Abschnitt wichtig:

|      |    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

An 0x00001ae0 schreiben wir jmp 0x00001ae6:

[0x00001780]> wa jmp 0x00001ae6 @ 0x00001ae0
INFO: Written 2 byte(s) (jmp 0x00001ae6) = wx eb04 @ 0x00001ae0
[0x00001780]> q

Nun sollte der Lizenz-check übersprungen werden:

home$ ./crackme_01 test123lololol
Your crackme is licensed!

Pseudocode mit r2dec und r2ghidra kann man mit

pdda @ main # r2dec
pdga @ main # r2ghidra

anzeigen lassen.

Ausgabe von 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]> 

Ausgabe von 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]>