Breaking

Reverse Engineering With Radare2 – Part 2

Welcome back to the radare2 reversing tutorials. If you’ve missed the previous parts, you can find them here and here.

Last time we’ve used the rabin2 application to view the  strings found inside the challenge01 binary to find password candidates. Based on the results we looked into the assembly to find the correct password. In this post, we’ll go through the next challenge and try out some of the features provided by radare2.

As recommended by the developers, I strongly suggest that you update your radare2 installation regularly, as it gets new features and bugfixes nearly every day.

Today, we’ll have a look into some of the visualization features of radare2.

First of all, you may have been noticed that the radare2 output is rather colorful. If the current colorscheme does not fit your needs, you’ll have multiple options to modify it. Several configuration parameters exist under the scr configuration space. You can view the different settings by using e?scr:

          scr.atport: V@ starts a background http server and spawns an r2 -C
           scr.color: Enable colors
     scr.color.bytes: Colorize bytes that represent the opcodes of the instruction
       scr.color.ops: Colorize numbers and registers in opcodes
         scr.columns: Force console column count (width)
            scr.echo: Show rcons output in realtime to stderr and buffer
        scr.feedback: Set visual feedback level (1=arrow on jump, 2=every key (useful for videos))
           scr.fgets: Use fgets() instead of dietline for prompt input
     scr.fix_columns: Workaround for Prompt iOS SSH client
        scr.fix_rows: Workaround for Linux TTY
             scr.fps: Show FPS in Visual
       scr.highlight: Highlight that word at RCons level
        scr.histsave: Always save history on exit
            scr.html: Disassembly uses HTML syntax
     scr.interactive: Start in interactive mode
            scr.nkey: Select the seek mode in visual
            scr.null: Show no output
           scr.pager: Select pager program (when output overflows the window)
       scr.pipecolor: Enable colors when using pipes
          scr.prompt: Show user prompt (used by r2 -q)
      scr.promptfile: Show user prompt file (used by r2 -q)
      scr.promptflag: Show flag name in the prompt
      scr.promptsect: Show section name in the prompt
         scr.randpal: Random color palete or just get the next one from 'eco'
      scr.responsive: Auto-adjust Visual depending on screen (e.g. unset asm.bytes)
        scr.rgbcolor: Use RGB colors (not available on Windows)
            scr.rows: Force console row count (height) (duplicate?)
            scr.seek: Seek to the specified address on startup
             scr.tee: Pipe output to file of this name
       scr.truecolor: Manage color palette (0: ansi 16, 1: 256, 2: 16M)
            scr.utf8: Show UTF-8 characters instead of ANSI
           scr.wheel: Mouse wheel in Visual; temporaryly disable/reenable by right click/Enter)
       scr.wheelnkey: Use sn/sp and scr.nkey on wheel instead of scroll
      scr.wheelspeed: Mouse wheel speed

For example if you have a terminal supporting utf8 characters, you might want to enable scr.utf8:

Before:

            ;-- main:
/ (fcn) sym.main 133
|           ; var int local_118h @ rbp-0x118
|           ; var int local_110h @ rbp-0x110
|           ; var int local_104h @ rbp-0x104
|           ; var int local_100h @ rbp-0x100
|           ; var int local_1h @ rbp-0x1
|           ; UNKNOWN XREF from 0x00400470 (unk)
|           ; DATA XREF from 0x0040073d (entry0)
|           0x004008e3      55             push rbp
|           0x004008e4      4889e5         mov rbp, rsp
|           0x004008e7      4881ec200100.  sub rsp, 0x120
|           0x004008ee      89bdfcfeffff   mov dword [rbp - local_104h], edi
|           0x004008f4      4889b5f0feff.  mov qword [rbp - local_110h], rsi
|           0x004008fb      488995e8feff.  mov qword [rbp - local_118h], rdx
|           0x00400902      b800000000     mov eax, 0
|           0x00400907      e80affffff     call sym.banner
|           0x0040090c      bf9d0a4000     mov edi, str.Enter_Password: ; "Enter Password: " @ 0x400a9d
|           0x00400911      b800000000     mov eax, 0
|           0x00400916      e8e5fdffff     call sym.imp.printf
|           0x0040091b      488d8500ffff.  lea rax, [rbp - local_100h]
|           0x00400922      4889c6         mov rsi, rax
|           0x00400925      bfae0a4000     mov edi, str._255s          ; "%255s" @ 0x400aae
|           0x0040092a      b800000000     mov eax, 0
|           0x0040092f      e8dcfdffff     call sym.imp.__isoc99_scanf
|           0x00400934      c645ff00       mov byte [rbp - local_1h], 0
|           0x00400938      488d8500ffff.  lea rax, [rbp - local_100h]
|           0x0040093f      4889c7         mov rdi, rax
|           0x00400942      e808ffffff     call sym.checkPassword
|           0x00400947      84c0           test al, al
|       ,=< 0x00400949      740c           je 0x400957
|       |   0x0040094b      bfb40a4000     mov edi, str.Password_accepted_ ; "Password accepted!" @ 0x400ab4
|       |   0x00400950      e88bfdffff     call sym.imp.puts
|      ,==< 0x00400955      eb0a           jmp 0x400961
|      |`-> 0x00400957      bfc70a4000     mov edi, str.Wrong_         ; "Wrong!" @ 0x400ac7
|      |    0x0040095c      e87ffdffff     call sym.imp.puts
|      |    ; JMP XREF from 0x00400955 (sym.main)
|      `--> 0x00400961      b800000000     mov eax, 0
|           0x00400966      c9             leave
\           0x00400967      c3             ret

 

After e scr.utf8=true:

            ;-- main:
╒ (fcn) sym.main 133
          ; var int local_118h @ rbp-0x118
          ; var int local_110h @ rbp-0x110
          ; var int local_104h @ rbp-0x104
          ; var int local_100h @ rbp-0x100
          ; var int local_1h @ rbp-0x1
          ; UNKNOWN XREF from 0x00400470 (unk)
          ; DATA XREF from 0x0040073d (entry0)
          0x004008e3      55             push rbp
          0x004008e4      4889e5         mov rbp, rsp
          0x004008e7      4881ec200100.  sub rsp, 0x120
          0x004008ee      89bdfcfeffff   mov dword [rbp - local_104h], edi
          0x004008f4      4889b5f0feff.  mov qword [rbp - local_110h], rsi
          0x004008fb      488995e8feff.  mov qword [rbp - local_118h], rdx
          0x00400902      b800000000     mov eax, 0
          0x00400907      e80affffff     call sym.banner
          0x0040090c      bf9d0a4000     mov edi, str.Enter_Password: ; "Enter Password: " @ 0x400a9d
          0x00400911      b800000000     mov eax, 0
          0x00400916      e8e5fdffff     call sym.imp.printf
          0x0040091b      488d8500ffff.  lea rax, [rbp - local_100h]
          0x00400922      4889c6         mov rsi, rax
          0x00400925      bfae0a4000     mov edi, str._255s          ; "%255s" @ 0x400aae
          0x0040092a      b800000000     mov eax, 0
          0x0040092f      e8dcfdffff     call sym.imp.__isoc99_scanf
          0x00400934      c645ff00       mov byte [rbp - local_1h], 0
          0x00400938      488d8500ffff.  lea rax, [rbp - local_100h]
          0x0040093f      4889c7         mov rdi, rax
          0x00400942      e808ffffff     call sym.checkPassword
          0x00400947      84c0           test al, al
      ┌─< 0x00400949      740c           je 0x400957
0x0040094b      bfb40a4000     mov edi, str.Password_accepted_ ; "Password accepted!" @ 0x400ab4
0x00400950      e88bfdffff     call sym.imp.puts
     ┌──< 0x00400955      eb0a           jmp 0x400961
     │└─> 0x00400957      bfc70a4000     mov edi, str.Wrong_         ; "Wrong!" @ 0x400ac7
0x0040095c      e87ffdffff     call sym.imp.puts
; JMP XREF from 0x00400955 (sym.main)
     └──> 0x00400961      b800000000     mov eax, 0
          0x00400966      c9             leave
          0x00400967      c3             ret

 

Currently, it mainly affects the drawn ascii arrows on the left hand side of the disassemblies. Of course you can also disable the coloring entirely by executing e scr.color = false (entirely), e scr.color.bytes = false (only the bytes in the disassembly) or e scr.color.ops = false (only the opcodes). If you like colors, but not the current scheme, you can choose another one by using the ec commands. One option is to choose one of the predefined themes, for example eco solarized:

            ;-- main:
(fcn) sym.main 133
          ; var int local_118h @ rbp-0x118
          ; var int local_110h @ rbp-0x110
          ; var int local_104h @ rbp-0x104
          ; var int local_100h @ rbp-0x100
          ; var int local_1h @ rbp-0x1
          ; UNKNOWN XREF from 0x00400470 (unk)
          ; DATA XREF from 0x0040073d (entry0)
          0x004008e3      55             push rbp
          0x004008e4      4889e5         mov rbp, rsp
          0x004008e7      4881ec200100.  sub rsp, 0x120
          0x004008ee      89bdfcfeffff   mov dword [rbp - local_104h], edi
          0x004008f4      4889b5f0feff.  mov qword [rbp - local_110h], rsi
          0x004008fb      488995e8feff.  mov qword [rbp - local_118h], rdx
          0x00400902      b800000000     mov eax, 0
          0x00400907      e80affffff     call sym.banner
          0x0040090c      bf9d0a4000     mov edi, str.Enter_Password: ; "Enter Password: " @ 0x400a9d
          0x00400911      b800000000     mov eax, 0
          0x00400916      e8e5fdffff     call sym.imp.printf
          0x0040091b      488d8500ffff.  lea rax, [rbp - local_100h]
          0x00400922      4889c6         mov rsi, rax
          0x00400925      bfae0a4000     mov edi, str._255s          ; "%255s" @ 0x400aae
          0x0040092a      b800000000     mov eax, 0
          0x0040092f      e8dcfdffff     call sym.imp.__isoc99_scanf
          0x00400934      c645ff00       mov byte [rbp - local_1h], 0
          0x00400938      488d8500ffff.  lea rax, [rbp - local_100h]
          0x0040093f      4889c7         mov rdi, rax
          0x00400942      e808ffffff     call sym.checkPassword
          0x00400947      84c0           test al, al
      ┌─< 0x00400949      740c           je 0x400957
0x0040094b      bfb40a4000     mov edi, str.Password_accepted_ ; "Password accepted!" @ 0x400ab4
0x00400950      e88bfdffff     call sym.imp.puts
     ┌──< 0x00400955      eb0a           jmp 0x400961
     │└─> 0x00400957      bfc70a4000     mov edi, str.Wrong_         ; "Wrong!" @ 0x400ac7
0x0040095c      e87ffdffff     call sym.imp.puts
; JMP XREF from 0x00400955 (sym.main)
     └──> 0x00400961      b800000000     mov eax, 0
          0x00400966      c9             leave
          0x00400967      c3             ret

(use eco without any parameters for a complete list). If this does not fits your need, you could also use random colors (ecr) or set the colors yourself (ec for a list of all available keys and ec <key> <frontcolor> (<backcolor>) eg. ec comment rgb:ffff00 blue (use ecs for supported colors)).

Besides the coloring, you’ll find various settings which affect the output of the disassembly in the asm configuration space. They allow you, for example, to customize the spacing between the different metadata of the disassembly (flow arrows, bytes, addresses, opcodes, comments,…) and also turning them on and off.

After you found some pleasing settings, let’s start with the current binary (btw, you can store option commands in the $HOME/.radare2rc file for persistence). After loading the binary into radare2 (r2 -AA ./challenge02), you should find yourself at the address 0x00400720. In case of some of you are wondering about why we are placed here and not at the main function, we’ll use different ways to get a clue. Assuming that we are currently placed inside or at the beginning of a function we could use the afi command to give us some information about the function we are placed in:

[0x00400720]> afi
#
 offset: 0x00400720
 name: entry0
 size: 43
 realsz: 43
 stackframe: 0
 call-convention: @�adAV
 cyclomatic-complexity: 1
 bits: 64
 type: fcn [NEW]
 num-bbs: 1
 edges: 0
 end-bbs: 1
 call-refs: 
 data-refs: 0x004009e0 0x00400970 0x004008e3 0x00600ff0 
 code-xrefs: 
 data-xrefs: 
 locals:0
 args: 0
 diff: type: new

 

As you may notice, besides the other different information, we get the name of the current function which is entry0. This strongly indicates that we are currently placed at the entrypoint of the application, which typically sets some things up (like parsing the commandline, retrieving environment variables etc.) and calls the actual main function afterwards. To check this assumption, we’ll use the ie command to get the address of the entry point as specified in the file headers (elf64 in this case):

[0x00400720]> ie
[Entrypoints]
vaddr=0x00400720 paddr=0x00000720 baddr=0x00400000 laddr=0x00000000 type=program

1 entrypoints

 

As you can see, the vaddr (virtual address) is matching the current position, so we are actually placed at the beginning of the entry point function.

Sidenote: the virtual address is most of the time a combination of the physical address (paddr; position in the binary) and the base address (baddr).

 

Let’s take a look at the disassembly of this function:

 

            ;-- section_end..plt:
            ;-- section..text:
            ;-- _start:
╒ (fcn) entry0 43
          ; UNKNOWN XREF from 0x00400440 (unk)
          0x00400720      31ed           xor ebp, ebp                ; [13] va=0x00400720 pa=0x00000720 sz=706 vsz=706 rwx=--r-x .text
          0x00400722      4989d1         mov r9, rdx
          0x00400725      5e             pop rsi
          0x00400726      4889e2         mov rdx, rsp
          0x00400729      4883e4f0       and rsp, 0xfffffffffffffff0
          0x0040072d      50             push rax
          0x0040072e      54             push rsp
          0x0040072f      49c7c0e00940.  mov r8, sym.__libc_csu_fini ; sym.__libc_csu_fini
          0x00400736      48c7c1700940.  mov rcx, sym.__libc_csu_init ; "AWAVA..AUATL.%.. " @ 0x400970
          0x0040073d      48c7c7e30840.  mov rdi, sym.main           ; "UH..H.. ." @ 0x4008e3
          0x00400744      ff15a6082000   call qword [rip + 0x2008a6] ; [0x600ff0:8]=0 LEA reloc.__libc_start_main_240 ; reloc.__libc_start_main_240
          0x0040074a      f4             hlt

 

After some stack setup between lines 0x400720 and 0x400729, some function is called in line 0x400744 with the main function as the first parameter (mov rdi, sym.main) besides some other parameters. As you can see, the call is using a process counter relative addressing. This technique is typically used to produce position independent code (PIC) which is needed for the ASLR technique. As the rip is pointing to the next address to be executed, it will contain the address 0x40074a. This results in the address 0x600ff0 (to calculate in radare2: ? 0x40074a+0x2008a6).

If we look into the sections of the binary (iS) we’ll see that this address belongs to the the .got section. This section contains the global object table (GOT) and is filled by the linker during load. It’s used as a jump table for library functions. Radare2 is also able to parse the relocation tables to tell us which functions will be mapped at runtime (ir command):

 

[Relocations]
vaddr=0x00600ff0 paddr=0x00000ff0 type=SET_64 __libc_start_main
vaddr=0x00600ff8 paddr=0x00000ff8 type=SET_64 __gmon_start__
vaddr=0x00601018 paddr=0x00001018 type=SET_64 puts
vaddr=0x00601020 paddr=0x00001020 type=SET_64 strlen
vaddr=0x00601028 paddr=0x00001028 type=SET_64 printf
vaddr=0x00601030 paddr=0x00001030 type=SET_64 __isoc99_scanf

6 relocations

Therefore the __libc_start_main function will be called at runtime :)… Some of you may already noticed that radare is smart enough to provide this information to us in the comment at the call line ;).

Radare is also able to replace the mnemonic RIP-relative addressing with the target function name. To get this, you’ve to enable the asm.relsub option:

call qword [reloc.__libc_start_main_240] ; [0x600ff0:8]=0 LEA reloc.__libc_start_main_240 ; reloc.__libc_start_main_240

 

As the __libc_start_main function is a standard library function provided by the linux library =libc, we’ll jump directly into the main function (s sym.main).

This time, we’ll try to get a fast overview of the referenced flags in this function (strings, global variables, functions, etc.). To get a graph of the references, we have two options. The first one is to generate a graphviz graph by using agc $$ | xdot - ($$ is a variable containing the current address, sym.main in this case):

Reference Graph

The other option would be the ascii graph in the visual mode (we’ll come to this later) which you’ll get by executing VV and then pressing >. As this looks very similar to the last challenge, we could directly look into the checkPassword function. You can do this either by seeking to it (s sym.checkPassword) or in the visual graph view by pressing the tab key until the function is marked. After pressing q, you’ll be placed inside the function (press q multiple times if you want to return to the main interface).

 

As we want to take a look into some of the visual modes of radere2, we’ll stay in the graph mode this time (either by pressing q only once or by entering VV).

[0x0040084f]> VV @ sym.checkPassword (nodes 9 edges 11 zoom 100%) BB-NORM mouse:canvas-y movements-speed:5
                                  =---------------------------------------------=
                                  |  0x40084f                                   |
                                  | (fcn) sym.checkPassword 148                 |
                                  | ; var int local_28h @ rbp-0x28              |
                                  | ; var int local_1ch @ rbp-0x1c              |
                                  | ; var int local_18h @ rbp-0x18              |
                                  | ; var int local_14h @ rbp-0x14              |
                                  | ; var int local_10h @ rbp-0x10              |
                                  | ; var int local_4h @ rbp-0x4                |
                                  | push rbp                                    |
                                  | mov rbp, rsp                                |
                                  | sub rsp, 0x30                               |
                                  | mov qword [rbp - local_28h], rdi            |
                                  | mov qword [rbp - local_10h], str.Sup3rP4ss  |
                                  | mov rax, qword [rbp - local_10h]            |
                                  | mov rdi, rax                                |
                                  | call sym.imp.strlen ;[a]                    |
                                  | mov dword [rbp - local_14h], eax            |
                                  | mov rax, qword [rbp - local_28h]            |
                                  | mov rdi, rax                                |
                                  | call sym.imp.strlen ;[a]                    |
                                  | mov dword [rbp - local_18h], eax            |
                                  | mov eax, dword [rbp - local_14h]            |
                                  | cmp eax, dword [rbp - local_18h]            |
                                  | je 0x400890 ;[b]                            |
                                  =---------------------------------------------=
                                        t f
                           .------------' '------------------------------.
                           |                                             |
                           |                                             |
                     =-----------------------------------=       =--------------------=
                     |  0x400890                         |       |  0x400889          |
                     | mov eax, dword [rbp - local_14h]  |       | mov eax, 0         |
                     | mov dword [rbp - local_1ch], eax  |       | jmp 0x4008e1 ;[f]  |
                     | mov dword [rbp - local_4h], 0     |       =--------------------=
                     | jmp 0x4008d4 ;[c]                 |           v
                     =-----------------------------------=           |
                         v                                           '------.
                         |                                                  |
                         |                                                  |
                         .----------------.                                 |
                     =-----------------------------------=                  |
                     |  0x4008d4          |              |                  |
                     | mov eax, dword [rbp - local_4h]   |                  |
                     | cmp eax, dword [rbp - local_1ch]  |                  |
                     | jl 0x40089f ;[d]   |              |                  |
                     =-----------------------------------=                  |
                           t f            |                                 |
      .--------------------' '------------|-------------.                   |
      |                                   |             |                   |
      |                                   |             |                   |
=-----------------------------------=     |     =--------------------=      |
|  0x40089f                         |     |     |  0x4008dc          |      |
| mov eax, dword [rbp - local_4h]   |     |     | mov eax, 1         |      |
| movsxd rdx, eax                   |     |     =--------------------=      |
| mov rax, qword [rbp - local_28h]  |     |         v                       |
| add rax, rdx                      |     |         |                       |
| movzx edx, byte [rax]             |     |         |                       |
| mov eax, dword [rbp - local_1ch]  |     |         |                       |
| sub eax, 1                        |     |         |                       |
| sub eax, dword [rbp - local_4h]   |     |         |                       |
| movsxd rcx, eax                   |     |         |                       |
| mov rax, qword [rbp - local_10h]  |     |         |                       |
| add rax, rcx                      |     |         '-----------------.     |
| movzx eax, byte [rax]             |     |                           |     |
| cmp dl, al                        |     |                           |     |
| je 0x4008d0 ;[e]                  |     |                           |     |
=-----------------------------------=     |                           |     |
        f t                               |                           |     |
        '---.-------------------------.   |                           |     |
            |                         |   |                           |     |
            |                         |   |                           |     |
    =--------------------=      =-------------------------------=     |     |
    |  0x4008c9          |      |  0x4008d0                     |     |     |
    | mov eax, 0         |      | add dword [rbp - local_4h], 1 |     |     |
    | jmp 0x4008e1 ;[f]  |      =-------------------------------=     |     |
    =--------------------=          `-----'                           |     |
        v                                                             |     |
        '-------------------------------------.-----------------------------'
                                              |
                                              |
                                          =--------------------=
                                          |  0x4008e1          |
                                          | leave              |
                                          | ret                |
                                          =--------------------=

 

This graph is available in different versions, which can be cycled through by pressing p. The view we’ll focus on this time is the minimap view. You’ll get this view by pressing p until you’ll see a BB-SMALL in the first line of the output. It should look like this:

[0x0040084f]> VV @ sym.checkPassword (nodes 9 edges 11 zoom 100%) BB-SMALL mouse:canvas-y movements-speed:5                                   

0x40084f:
(fcn) sym.checkPassword 148
; var int local_28h @ rbp-0x28
; var int local_1ch @ rbp-0x1c
; var int local_18h @ rbp-0x18
; var int local_14h @ rbp-0x14
; var int local_10h @ rbp-0x10
; var int local_4h @ rbp-0x4
push rbp
mov rbp, rsp
sub rsp, 0x30
mov qword [rbp - local_28h], rdi
mov qword [rbp - local_10h], str.Sup3rP4ss
mov rax, qword [rbp - local_10h]
mov rdi, rax
call sym.imp.strlen ;[a]                                                <@@@@@@>
mov dword [rbp - local_14h], eax                                           t f
mov rax, qword [rbp - local_28h]                                 .---------' '---------.
mov rdi, rax                                                     |                     |
call sym.imp.strlen ;[a]                                         |                     |
mov dword [rbp - local_18h], eax                              [_0890_]            [_0889_]
mov eax, dword [rbp - local_14h]                               v                   v
cmp eax, dword [rbp - local_18h]                               |                   |
je 0x400890 ;[b]                                               |                   |
                                                               .                   |
                                                              [_08d4_]             |
                                                               | t f               |
                                                       .-------|-' '---------.     |
                                                       |       |             |     |
                                                       |       |             |     |
                                                    [_089f_]   |        [_08dc_]   |
                                                         f t   |         v         |
                                                  .------' '--.|         '---.     |
                                                  |           ||             |     |
                                                  |           ||             |     |
                                             [_08c9_]      [_08d0_]          |     |
                                              v             `--'             |     |
                                              '---------------------.--------------'
                                                                    |
                                                                    |

This view shows you a minimap in the center and the current selected node as a disassembly on the upper left (the current node is marked with @@@@@@). This view helps us to focus on smaller blocks of a function, whilst always having the overview about the execution flow.

As we are now starting with reading the assembler code, I want to show you another nice feature for those of you who aren’t able to speak fluent assembler. Radare2 has an option to replace the assembler code by a pseudo code which is simpler to read (but sometimes not as detailed). To enable this feature, press : (colon, to open main shell) and enter e asm.pseudo = true. After leaving the shell again (press enter) you should see something like this:

(fcn) sym.checkPassword 148
; var int local_28h @ rbp-0x28
; var int local_1ch @ rbp-0x1c
; var int local_18h @ rbp-0x18
; var int local_14h @ rbp-0x14
; var int local_10h @ rbp-0x10
; var int local_4h @ rbp-0x4
push rbp
rbp = rsp
rsp -= 0x30
qword [rbp - local_28h] = rdi
qword [rbp - local_10h] = str.Sup3rP4ss
rax = qword [rbp - local_10h]
rdi = rax
sym.imp.strlen ()
dword [rbp - local_14h] = eax
rax = qword [rbp - local_28h]
rdi = rax
sym.imp.strlen ()
dword [rbp - local_18h] = eax
eax = dword [rbp - local_14h]
if (eax == dword [rbp - local_18h]
isZero 0x400890)

The first three lines are a typical function prologue. They create a backup of the rbp and reserve 48 bytes of memory on the stack. As you may noticed, radare2 displays constant values per default in a hexadecimal representation. Similar to IDA Pro, we are able to tell radare how we want to see a value at a specific position. Sadly it isn’t easily available in the graph view (yet?), but we can set it manually in the main shell. First we need the address of the the opcodes containing the value we want to change. We can get it for example by using pd 3 (which disassembles 3 opcodes from the current position). This would result in 0x00400853. To tell radare that we want to get the value in decimal, we enter ahi d @ 0x00400853. This stands for analysis hints intermediate, decimal at the address 0x00400853.

As we know from the last challenge, the function checkPassword gets one argument and returns if it contains the correct password. Currently, radare2 not detected that the function has an argument. Let’s tell it to do so. The command afvr rdi password const char* tells radare2 that the rdi register is used at an argument with the name password of the type const char* (based on the System V AMD64 ABI). This means that the local variable local_28h is a pointer to the entered password. Therefore we change the name and the type of the variable (afvn local_28h password_1 and afvbt password_1 const char*). The next line indicates that local_10h points to the string we compare with (I rename it to targetPassword). As rdi is always used in the amd64 ABI as the first argument, it becomes confusing as it is now aliased to password in the function disassembly. That’s why I prefer to leave it after I followed the dataflow (remove the alias by afvr- password).

This means that the next 8 lines calculate the length of the different strings and stores them in local_14h and local_18h respectively. Your current disassembly should look now close to this:

(fcn) sym.checkPassword 148
; var const char* password_1 @ rbp-0x28
; var int local_1ch @ rbp-0x1c
; var int inputPwdLength @ rbp-0x18
; var int targetPwdLength @ rbp-0x14
; var const char* targetPassword @ rbp-0x10
; var int local_4h @ rbp-0x4
; UNKNOWN XREF from 0x004004b8 (unk)
; CALL XREF from 0x00400942 (sym.main)
push rbp
rbp = rsp
rsp -= 48
qword [rbp - password_1] = rdi
qword [rbp - targetPassword] = str.Sup3rP4ss
rax = qword [rbp - targetPassword]
rdi = rax
sym.imp.strlen ()
dword [rbp - targetPwdLength] = eax
rax = qword [rbp - password_1]
rdi = rax
sym.imp.strlen ()
dword [rbp - inputPwdLength] = eax
eax = dword [rbp - targetPwdLength]
if (eax == dword [rbp - inputPwdLength]
isZero 0x400890)

If we directly translate what we have so far, we get:

int checkPassword(const char* password) {
    const char* password_1 = password;
    const char* targetPassword = "Sup3rP4ss";
    int targetPwdLength = strlen(targetPassword);
    int inputPwdLength = strlen(password_1);
    if(targetPwdLength == inputPwdLength) {

Let’s check what happens if the both lengths are not equal (you might already have a strong guess based on the minimap 😉 ). In the graph view you can simply press f or t to follow the true or the false branch. Pressing f brings us to a very small node. This node simply sets eax to zero and then jumps to 0x4008e1. To follow this jump, you can press g and the character(s) behind this jump (f in my case). What now happened in my current version of radare2 is that I’ve got an empty disassembly in the top left. This is one of the situations where the pseudo syntax misses some instructions. After disabling it (e asm.pseudo = false), you’ll see the instructions leave and ret. Those instructions tell the CPU to restore the stack based on the stored registers and jump back to the code which called the current function. In combination with the previous eax line, we have:

int checkPassword(const char* password) {
    const char* password_1 = password;
    const char* targetPassword = "Sup3rP4ss";
    int targetPwdLength = strlen(targetPassword);
    int inputPwdLength = strlen(password_1);
    if(targetPwdLength == inputPwdLength) {
    } else {
        return 0;
    }
}

Current state: 3 blocks decompiled (0x4084f, 0x40889 and 0x408e1).

Let’s continue with the next one (the true case of the comparison).

Sidenote: You can always navigate through the blocks by using tab and TAB (shift+tab)

This block consists of three instructions which take a copy of the targetPwdLength variable and set another variable to zero. This is a typical intro of a loop which sets up the counter variable(s). If we take a look on the minimap, we can see that this is actually the intro to a loop (this block jumps to block 0x4008d4 and block 0x408d0 also jumps back to block 0x4008d4). So let’s rename the variables accordingly (another block finished 🙂 ).

The next one is again a small one. It compares the counter variable to the copy of the length. If it is lower, it seems to continue with another check (based on the minimap) if not, it goes to a block which ends up in the function epilog. I’ll go with this one first as it seems to be a quick one… It stores the value on into eax, which means that the function returns true. Our current decompiled code:

int checkPassword(const char* password) {
    const char* password_1 = password;
    const char* targetPassword = "Sup3rP4ss";
    int targetPwdLength = strlen(targetPassword);
    int inputPwdLength = strlen(password_1);
    if(targetPwdLength == inputPwdLength) {
        int counter = 0;
        int length = targetPwdLength;
        while(counter < length) {
        }
        return 1;
    } else {
        return 0;
    }
}

The true block is a larger one with some calculations and one comparison:

eax = dword [rbp - counter]
rdx = eax
rax = qword [rbp - password_1]
rax += rdx
edx = byte [rax]
eax = dword [rbp - length]
eax -= 1
eax -= dword [rbp - counter]
rcx = eax
rax = qword [rbp - targetPassword]
rax += rcx
eax = byte [rax]
if (dl == al
isZero 0x4008d0)

 

The first five lines store the character from the password_1 array at a position based on the current counter value. The next lines simply takes a character from the targetPassword array, but this time counting from the end. Those values are then compared for equality. Again, let’s check the inequality case: zero is stored in eax and the function returns false. The true case simply increments the counter by one and starts over at the block 0x408d4.

We are now able to decompile all blocks of the function:

int checkPassword(const char* password) {
    const char* password_1 = password;
    const char* targetPassword = "Sup3rP4ss";
    int targetPwdLength = strlen(targetPassword);
    int inputPwdLength = strlen(password_1);
    if(targetPwdLength == inputPwdLength) {
        int counter = 0;
        int length = targetPwdLength;
        while(counter < length) {
            if(password_1[counter] == targetPassword[length-1-counter]) {
                counter += 1;
            } else {
                return 0;
            }
        }
        return 1;
    } else {
        return 0;
    }
}

Or after some manual reordering:

int checkPassword(const char* password) {
    const char* targetPassword = "Sup3rP4ss";
    int targetPwdLength = strlen(targetPassword);
    int inputPwdLength = strlen(password);
    if(targetPwdLength != inputPwdLength) {
        return 0;
    }


    for(int counter = 0; counter < targetPwdLength; counter++) {
        if(password[counter] != targetPassword[targetPwdLength - 1 - counter])
            return 0;
    }
    return 1;
}

Have a clue what this function does?

  1. It checks if the entered password is as long as the required password
  2. It compares the entered password with the reverse of “Sup3rP4ss”

This means our required password is “ss4Pr3puS” :

$ ./challenge02
##################################
#          Challenge 2           #
#                                #
#      (c) 2016 Timo Schmid      #
##################################
Enter Password: ss4Pr3puS
Password accepted!

That’s it!

The last command I would like to show you today is the experimental pseudo code feature:

[0x004008d0]> pdc
function sym.checkPassword () {
    loc_0x40084f:

  ; UNKNOWN XREF from 0x004004b8 (unk)
  ; CALL XREF from 0x00400942 (sym.main)
  push rbp
  rbp = rsp
  rsp -= 0x30
  qword [rbp - password_1] = password
  qword [rbp - targetPassword] = 0x400a93
  rax = qword [rbp - targetPassword]
  password = rax
  0x4006f0 ()                    ; sym.imp.strlen
  dword [rbp - targetPwdLength] = eax
  rax = qword [rbp - password_1]
  password = rax
  0x4006f0 ()                    ; sym.imp.strlen
  dword [rbp - inputPwdLength] = eax
  eax = dword [rbp - targetPwdLength]
  if (eax == dword [rbp - inputPwdLength]
  isZero 0x400890) {
        loc_0x400890:

      eax = dword [rbp - targetPwdLength]
      dword [rbp - length] = eax
      dword [rbp - counter] = 0
      goto 0x4008d4
       do {
            loc_0x4008d4:

          ; JMP XREF from 0x0040089d (sym.checkPassword)
          eax = dword [rbp - counter]
          if (eax == dword [rbp - length]
          jl 0x40089f 
           } while (?);
       } while (?);
      }
      return;
}

It’s currently very incomplete in terms of instruction coverage, but it shows where radare2 features might go in the future (there was a GSoC running which started to create a radare2 based decompiler named radeco).

Challenge 0x03

You will find the next challenge here (linux64 dynamically linked, linux64 statically linked, win64):

https://github.com/bluec0re/reversing-radare2/tree/master/sources/challenge03

MD5 (challenge03) = 748f98ced3ca0b9178f8d85cdcc9d750
MD5 (challenge03.exe) = 2a1dde4f0de546216779394ce8960aa5
MD5 (challenge03-static) = 8c48ac034d6cefe135e9f22d6431e930
SHA1 (challenge03) = 0a2317bfb952f64dcd8ca50d2a084275cf8e7816
SHA1 (challenge03.exe) = 1c00a49d1c2249cb528a06c08cce1d15b062f59c
SHA1 (challenge03-static) = 78fcffdbe56d1cea29723df30dc14dfe6baf101f
SHA256 (challenge03) = 00f7662f22a3d305575a0117ea17b8d7801c133ffa2201986d62ebade65bdbf7
SHA256 (challenge03.exe) = 0c4a760a4a8653c4a26d07e0970a3ef7474cbdb53596166a670f5c08453eb57e
SHA256 (challenge03-static) = cbea4018b2451e84b789bd909f892f77486e8d42aee659e175da942a555c97ae

The goal is again to find the correct password for the login into the binary. Next time, I’ll give you a walkthrough for the third challenge and challenge #4.

Happy reversing!

Best,

Timo

@bluec0re

Comments

Comments are closed.