Breaking

Reverse Engineering With Radare2 – Part 3

Sorry about the larger delay between the previous post and this one, but I was very busy the last weeks.
(And the technology I wanted to show wasn’t completely implemented in radare2, which means that I had to implement it on my own 😉 ). In case you’re new to this series, you’ll find the previous posts here.

As you may already know, we’ll deal with the third challenge today. The purpose for this one is to introduce
some constructs which are often used in real programs.

Let’s start like the last times by loading the binary into radare2 (r2 -AA ./challenge03).

As you may notice, the main function differs from the previous challenges:

[0x00400a04]> VV @ sym.main (nodes 4 edges 4 zoom 100%) BB-NORM mouse:canvas-y movements-speed:5
        =---------------------------------------------=
        |  0x400a04                                   |
        |   ;-- main:                                 |
        | (fcn) sym.main 115                          |
        | ; var int local_138h @ rbp-0x138            |
        | ; var int local_130h @ rbp-0x130            |
        | ; var int local_124h @ rbp-0x124            |
        | ; var int local_120h @ rbp-0x120            |
        | ; var int local_18h @ rbp-0x18              |
        | ; var int local_10h @ rbp-0x10              |
        | push rbp                                    |
        | mov rbp, rsp                                |
        | sub rsp, 0x140                              |
        | mov dword [rbp - local_124h], edi           |
        | mov qword [rbp - local_130h], rsi           |
        | mov qword [rbp - local_138h], rdx           |
        | mov eax, 0                                  |
        | call sym.banner ;[a]                        |
        | mov dword [rbp - local_120h], 0             |
        | mov qword [rbp - local_18h], obj.passwords  |
        | mov qword [rbp - local_10h], obj.passwords  |
        | lea rax, [rbp - local_120h]                 |
        | mov rdi, rax                                |
        | call sym.checkPassword ;[b]                 |
        | test al, al                                 |
        | je 0x400a66 ;[c]                            |
        =---------------------------------------------=
              t f
      .-------' '-----------------------.
      |                                 |
      |                                 |
=-------------------------=     =---------------------------------=
|  0x400a66               |     |  0x400a5a                       |
| mov edi, str.Wrong_     |     | mov edi, str.Password_accepted_ |
| call sym.imp.puts ;[d]  |     | call sym.imp.puts ;[d]          |
=-------------------------=     | jmp 0x400a70 ;[e]               |
    v                           =---------------------------------=
    |                               v
    '-------------------.-----------'
                        |
                        |
                    =--------------------=
                    |  0x400a70          |
                    | mov eax, 0         |
                    | leave              |
                    | ret                |
                    =--------------------=

versus

[0x004008e3]> VV @ sym.main (nodes 4 edges 4 zoom 100%) BB-NORM mouse:canvas-y movements-speed:5
            =------------------------------------=
            |  0x4008e3                          |
            |   ;-- main:                        |
            | (fcn) sym.main 133                 |
            |   sym.main ();                     |
            | ; 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       |
            | push rbp                           |
            | mov rbp, rsp                       |
            | sub rsp, 0x120                     |
            | mov dword [rbp - local_104h], edi  |
            | mov qword [rbp - local_110h], rsi  |
            | mov qword [rbp - local_118h], rdx  |
            | mov eax, 0                         |
            | call sym.banner ;[a]               |
            | mov edi, str.Enter_Password:       |
            | mov eax, 0                         |
            | call sym.imp.printf ;[b]           |
            | lea rax, [rbp - local_100h]        |
            | mov rsi, rax                       |
            | mov edi, str._255s                 |
            | mov eax, 0                         |
            | call sym.imp.__isoc99_scanf ;[c]   |
            | mov byte [rbp - local_1h], 0       |
            | lea rax, [rbp - local_100h]        |
            | mov rdi, rax                       |
            | call sym.checkPassword ;[d]        |
            | test al, al                        |
            | je 0x400957 ;[e]                   |
            =------------------------------------=
                  t f
      .-----------' '-------------------.
      |                                 |
      |                                 |
=-------------------------=     =---------------------------------=
|  0x400957               |     |  0x40094b                       |
| mov edi, str.Wrong_     |     | mov edi, str.Password_accepted_ |
| call sym.imp.puts ;[f]  |     | call sym.imp.puts ;[f]          |
=-------------------------=     | jmp 0x400961 ;[g]               |
    v                           =---------------------------------=
    |                               v
    '-------------------.-----------'
                        |
                        |
                    =--------------------=
                    |  0x400961          |
                    | mov eax, 0         |
                    | leave              |
                    | ret                |
                    =--------------------=

The second half of the function seems the same (pseudo):

int main(int argc, char **argv) {
    banner();
    [...]
    if(checkPassword(...)) {
        puts("Password accepted!\n");
    } else {
        puts("Wrong!\n");
    }
    return 0;
}

Whilst the previous challenges read the user password via scanf, this time some local variables are set.
One of them is set to zero, the other two to an address flagged as obj.passwords. As this seems very promising,
we’ll look what’s at this address (with px @ obj.passwords):

- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00601060  080b 4000 0000 0000 100b 4000 0000 0000  ..@.......@.....
0x00601070  180b 4000 0000 0000 200b 4000 0000 0000  ..@..... .@.....
0x00601080  280b 4000 0000 0000 0000 0000 0000 0000  (.@.............
0x00601090  4743 433a 2028 474e 5529 2036 2e31 2e31  GCC: (GNU) 6.1.1
0x006010a0  2032 3031 3630 3830 3200 0000 0000 0000   20160802.......
0x006010b0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x006010c0  0000 0000 0000 0000 0000 0000 0300 0100  ................
0x006010d0  3802 4000 0000 0000 0000 0000 0000 0000  8.@.............
0x006010e0  0000 0000 0300 0200 5402 4000 0000 0000  ........T.@.....
0x006010f0  0000 0000 0000 0000 0000 0000 0300 0300  ................
0x00601100  7402 4000 0000 0000 0000 0000 0000 0000  t.@.............
0x00601110  0000 0000 0300 0400 9802 4000 0000 0000  ..........@.....
0x00601120  0000 0000 0000 0000 0000 0000 0300 0500  ................
0x00601130  0803 4000 0000 0000 0000 0000 0000 0000  ..@.............
0x00601140  0000 0000 0300 0600 4805 4000 0000 0000  ........H.@.....
0x00601150  0000 0000 0000 0000 0000 0000 0300 0700  ................

Hm, this is obviously not a list of readable strings… But if we look closer,
we can see that the values at this address could contain again addresses…
(the 2nd-7th, 10th-15th, 18th-23th, … bytes are the same and fit into valid memory regions).

As this is a 64bit binary, we’ll do the hexdump again, but this time with quadwords (pxq):

0x00601060  0x0000000000400b08  0x0000000000400b10   ..@.......@.....
0x00601070  0x0000000000400b18  0x0000000000400b20   ..@..... .@.....
0x00601080  0x0000000000400b28  0x0000000000000000   (.@.............
0x00601090  0x4e4728203a434347  0x312e312e36202955   GCC: (GNU) 6.1.1
0x006010a0  0x3038303631303220  0x0000000000000032    20160802.......
0x006010b0  0x0000000000000000  0x0000000000000000   ................
0x006010c0  0x0000000000000000  0x0001000300000000   ................
0x006010d0  0x0000000000400238  0x0000000000000000   8.@.............
0x006010e0  0x0002000300000000  0x0000000000400254   ........T.@.....
0x006010f0  0x0000000000000000  0x0003000300000000   ................
0x00601100  0x0000000000400274  0x0000000000000000   t.@.............
0x00601110  0x0004000300000000  0x0000000000400298   ..........@.....
0x00601120  0x0000000000000000  0x0005000300000000   ................
0x00601130  0x0000000000400308  0x0000000000000000   ..@.............
0x00601140  0x0006000300000000  0x0000000000400548   ........H.@.....
0x00601150  0x0000000000000000  0x0007000300000000   ................

This looks far better… As we see, radare also shows the values in green this time, which is another point into
the address direction. By using pxQ we can advise radare2 to print them one per aline and additional give some meta infos about them:

0x00601060 0x0000000000400b08 str.shad7Pe
0x00601068 0x0000000000400b10 str.miTho7i
0x00601070 0x0000000000400b18 str.Wa3suac
0x00601078 0x0000000000400b20 str.ohGhah7
0x00601080 0x0000000000400b28 str.Aibah1a
0x00601088 0x0000000000000000 section.
0x00601090 0x4e4728203a434347 
0x00601098 0x312e312e36202955 
0x006010a0 0x3038303631303220 
0x006010a8 0x0000000000000032 section_end..comment+24
0x006010b0 0x0000000000000000 section.
0x006010b8 0x0000000000000000 section.
0x006010c0 0x0000000000000000 section.
0x006010c8 0x0001000300000000 
0x006010d0 0x0000000000400238 section..interp
0x006010d8 0x0000000000000000 section.
0x006010e0 0x0002000300000000 
0x006010e8 0x0000000000400254 section_end..interp
0x006010f0 0x0000000000000000 section.
0x006010f8 0x0003000300000000 
0x00601100 0x0000000000400274 section_end..note.ABI_tag
0x00601108 0x0000000000000000 section.
0x00601110 0x0004000300000000 
0x00601118 0x0000000000400298 section_end..note.gnu.build_id
0x00601120 0x0000000000000000 section.
0x00601128 0x0005000300000000 
0x00601130 0x0000000000400308 section..dynsym
0x00601138 0x0000000000000000 section.
0x00601140 0x0006000300000000 
0x00601148 0x0000000000400548 section_end..dynsym
0x00601150 0x0000000000000000 section.
0x00601158 0x0007000300000000 

If you want to have an even more annotated view, use pxr:

0x00601060  0x0000000000400b08   ..@..... (.rodata) str.shad7Pe R 0x65503764616873 (shad7Pe) --> ascii
0x00601068  0x0000000000400b10   ..@..... (.rodata) str.miTho7i R 0x69376f6854696d (miTho7i) --> ascii
0x00601070  0x0000000000400b18   ..@..... (.rodata) str.Wa3suac R 0x63617573336157 (Wa3suac) --> ascii
0x00601078  0x0000000000400b20    .@..... (.rodata) str.ohGhah7 R 0x3768616847686f (ohGhah7) --> ascii
0x00601080  0x0000000000400b28   (.@..... (.rodata) str.Aibah1a R 0x61316861626941 (Aibah1a) --> ascii
0x00601088  0x0000000000000000   ........ section_end.GNU_STACK
0x00601090  0x4e4728203a434347   GCC: (GN ascii
0x00601098  0x312e312e36202955   U) 6.1.1 ascii
0x006010a0  0x3038303631303220    2016080 ascii
0x006010a8  0x0000000000000032   2....... (.shstrtab) ascii
0x006010b0  0x0000000000000000   ........ section_end.GNU_STACK
0x006010b8  0x0000000000000000   ........ section_end.GNU_STACK
0x006010c0  0x0000000000000000   ........ section_end.GNU_STACK
0x006010c8  0x0001000300000000   ........
0x006010d0  0x0000000000400238   8.@..... (.interp) section.INTERP R 0x6c2f343662696c2f (/lib64/ld-linux-x86-64.so.2) --> ascii
0x006010d8  0x0000000000000000   ........ section_end.GNU_STACK
0x006010e0  0x0002000300000000   ........
0x006010e8  0x0000000000400254   T.@..... (.note.ABI_tag) section.NOTE R 0x1000000004
0x006010f0  0x0000000000000000   ........ section_end.GNU_STACK
0x006010f8  0x0003000300000000   ........
0x00601100  0x0000000000400274   t.@..... (.note.gnu.build_id) section..note.gnu.build_id R 0x1400000004
0x00601108  0x0000000000000000   ........ section_end.GNU_STACK
0x00601110  0x0004000300000000   ........
0x00601118  0x0000000000400298   ..@..... (.gnu.hash) section_end.NOTE R 0x800000003
0x00601120  0x0000000000000000   ........ section_end.GNU_STACK
0x00601128  0x0005000300000000   ........
0x00601130  0x0000000000400308   ..@..... (.dynsym) section..dynsym R 0x0 --> section_end.GNU_STACK
0x00601138  0x0000000000000000   ........ section_end.GNU_STACK
0x00601140  0x0006000300000000   ........
0x00601148  0x0000000000400548   H.@..... (.dynstr) section..dynstr R 0x6f732e6362696c00 --> ascii
0x00601150  0x0000000000000000   ........ section_end.GNU_STACK
0x00601158  0x0007000300000000   ........

So we know that the obj.passwords is an array of string pointers, suffixed with a null pointer. Let’s look again at
the main function. We see that this list is assigned to local variables (local_18h and local_10h), but it seems
that only a pointer to local_120h is passed as an argument to the checkPassword function (the lea rax, [rbp - local_120h] part).

Based on our current information the following stack layout is used:

rbp-0x120: 0000 0000
rbp-0x11c: ???? ????
[...]
rbp-0x018: &obj.passwords
rbp-0x010: &obj.passwords

This means that if you have a pointer to the local_120h variable, you’ll be able to read from forwards till the other
two variables. If we look into the checkPassword function, we’ll see that the address in the first argument is used
multiple times (stored in local_18h) and the value 0x110 is added to it. If we make some calculations (? -0x120 + 0x110)
We see that this matches to the local_10h variable of the main function:

-16 0xfffffffffffffff0 01777777777777777777760 17179869184.0G fffff000:0ff0 -16 1111111111111111111111111111111111111111111111111111111111110000 -16.0 -16.000000f -16.000000

(-16 is the same as -0x10 or 0xfffffffffffffff0 for quadwords). Such constructs are high indicators for structures.

radare2 has some rudimentary support for structures (primarily for representing data in memory) and a generic type system.

Based on our current knowledge, the structure should be something like this:

struct Foo {
    int field0;
    char unknown[260];
    char** field108;
    char** field110;
};

If we place this definition in a header file, we can tell radare2 to parse it with the to command. To view all currently stored structures, you can use the ts command:

Foo

By using t Foo you can generate a formatting command for viewing data based on the parsed definitions. As you notice, this produces some errors:

Cannot resolve type 'type.char **'
Cannot resolve type 'type.char **'
pf d[260]z field0 unknown

This is because radare2 currently doesn’t have a definition for the type char **. We can either change the field types to void* or define the missing type. For learning purposes, we’ll define the missing type.

Radare2 uses internally a database named sdb. This is more or less a key value storage with support for namespaces.

All types are defined inside the anal/types namespace. To interact with the database, you can use either the k command (complete database)
or the tk command (type namespace only). To define a new type, we’ll have to make it known to radare2. This is done by assigning the type identifer the string type:

tk char **=type

In the next step we tell radare how this value should be printed:

tk type.char **=p

We’ve used the p format here, which means that this type represents a pointer. (You can see all possible formatting options by using pf??).

This time, the t Foo command succeeds:

pf d[260]zpp field0 unknown field108 field110

Note: If you are interested how radare2 stores the information about this struct, run tk~Foo. The format for the fields is type, offset, arraysize

As we’ve now the structure defined, we can use it in the main function. To do this, we’ll set the type of the local_120h variable to this new type Foo:

afvt local_120h Foo

(afvt stands for analysis function variables type).

This time, the disassembly has some additional annotations:

            ;-- main:
/ (fcn) sym.main 115
|   sym.main ();
|           ; var int local_138h @ rbp-0x138
|           ; var int local_130h @ rbp-0x130
|           ; var int local_124h @ rbp-0x124
|           ; var Foo local_120h @ rbp-0x120
|           ; UNKNOWN XREF from 0x004004a8 (unk)
|           ; DATA XREF from 0x004007dd (entry0)
|           0x00400a04      55             push rbp
|           0x00400a05      4889e5         mov rbp, rsp
|           0x00400a08      4881ec400100.  sub rsp, 0x140
|           0x00400a0f      89bddcfeffff   mov dword [rbp - local_124h], edi
|           0x00400a15      4889b5d0feff.  mov qword [rbp - local_130h], rsi
|           0x00400a1c      488995c8feff.  mov qword [rbp - local_138h], rdx
|           0x00400a23      b800000000     mov eax, 0
|           0x00400a28      e889feffff     call sym.banner
|           0x00400a2d      c785e0feffff.  mov dword [rbp - local_120h.field0], 0
|           0x00400a37      48c745e86010.  mov qword [rbp - local_120h.field108], obj.passwords
|           0x00400a3f      48c745f06010.  mov qword [rbp - local_120h.field110], obj.passwords
|           0x00400a47      488d85e0feff.  lea rax, [rbp - local_120h.field0]
|           0x00400a4e      4889c7         mov rdi, rax
|           0x00400a51      e815ffffff     call sym.checkPassword
|           0x00400a56      84c0           test al, al
|       ,=< 0x00400a58      740c           je 0x400a66
|       |   0x00400a5a      bfe90b4000     mov edi, str.Password_accepted_ ; "Password accepted!" @ 0x400be9
|       |   0x00400a5f      e81cfdffff     call sym.imp.puts
|      ,==< 0x00400a64      eb0a           jmp 0x400a70
|      |`-> 0x00400a66      bffc0b4000     mov edi, str.Wrong_         ; "Wrong!" @ 0x400bfc
|      |    0x00400a6b      e810fdffff     call sym.imp.puts
|      |    ; JMP XREF from 0x00400a64 (sym.main)
|      `--> 0x00400a70      b800000000     mov eax, 0
|           0x00400a75      c9             leave
\           0x00400a76      c3             ret

If your output is different from the one above (e.g. missing local_120h.field110), make sure that the offsets were correctly calculated in the database. You can change every entry with the tk command, e.g. tk struct.Foo.field110=char **,272,0.

As we can see, the output now nicely aligns with the assignments. An additional try to represent the asm code as C code
would now look like:

int main(int argc, char **argv) {
    struct Foo local_120h;
    banner();
    local_120h.field0 = 0;
    local_120h.field108 = obj.passwords;
    local_120h.field110 = obj.passwords;
    if(checkPassword(&local_120h)) {
        puts("Password accepted!\n");
    } else {
        puts("Wrong!\n");
    }
    return 0;
}

The next function (checkPassword) is (obviously) also different to the previous ones:

[0x0040096b]> VV @ sym.checkPassword (nodes 7 edges 8 zoom 100%) BB-NORM mouse:canvas-y movements-speed:5
                     =-----------------------------------------------=
                     |  0x40096b                                     |
                     | (fcn) sym.checkPassword 153                   |
                     |   sym.checkPassword ();                       |
                     | ; var int local_18h @ rbp-0x18                |
                     | ; var int local_4h @ rbp-0x4                  |
                     | push rbp                                      |
                     | mov rbp, rsp                                  |
                     | sub rsp, 0x20                                 |
                     | mov qword [rbp - local_18h], rdi              |
                     | mov rax, qword [rbp - local_18h]              |
                     | mov rdx, qword [rax + section_end..shstrtab]  |
                     | mov rax, qword [rbp - local_18h]              |
                     | mov qword [rax + 0x110], rdx                  |
                     | jmp 0x4009ea ;[a]                             |
                     =-----------------------------------------------=
                         v
                         '-----.
                               |
                               .------------.
                           =----------------------------------=
                           |  0x4009ea      |                 |
                           | mov rax, qword [rbp - local_18h] |
                           | mov rax, qword [rax + 0x110]     |
                           | mov rax, qword [rax]             |
                           | test rax, rax  |                 |
                           | jne 0x40098f ;[b]                |
                           =----------------------------------=
                                 t f        |
        .------------------------' '--------|----------------------.
        |                                   |                      |
        |                                   |                      |
  =-----------------------------------=     |              =--------------------=
  |  0x40098f                         |     |              |  0x4009fd          |
  | mov rax, qword [rbp - local_18h]  |     |              | mov eax, 1         |
  | mov rdi, rax                      |     |              =--------------------=
  | call sym.readPassword ;[c]        |     |                  v
  | mov dword [rbp - local_4h], eax   |     |                  |
  | mov eax, dword [rbp - local_4h]   |     |                  |
  | movsxd rdx, eax                   |     |                  |
  | mov rax, qword [rbp - local_18h]  |     |                  |
  | mov rax, qword [rax + 0x110]      |     |                  |
  | mov rax, qword [rax]              |     |                  |
  | mov rcx, qword [rbp - local_18h]  |     |                  |
  | add rcx, 4                        |     |                  |
  | mov rsi, rax                      |     |                  '------.
  | mov rdi, rcx                      |     |                         |
  | call sym.imp.strncmp ;[d]         |     |                         |
  | test eax, eax                     |     |                         |
  | je 0x4009d0 ;[e]                  |     |                         |
  =-----------------------------------=     |                         |
          f t                               |                         |
        .-' '---------------------.         |                         |
        |                         |         |                         |
        |                         |         |                         |
=--------------------=      =-----------------------------------=     |
|  0x4009c9          |      |  0x4009d0     |                   |     |
| mov eax, 0         |      | mov rax, qword [rbp - local_18h]  |     |
| jmp 0x400a02 ;[f]  |      | mov rax, qword [rax + 0x110]      |     |
=--------------------=      | lea rdx, [rax + 8]                |     |
    v                       | mov rax, qword [rbp - local_18h]  |     |
    |                       | mov qword [rax + 0x110], rdx      |     |
    |                       =-----------------------------------=     |
    '----------------------------.----------'                         |
                                 |------------------------------------'
                                 |
                                 |
                             =--------------------=
                             |  0x400a02          |
                             | leave              |
                             | ret                |
                             =--------------------=

First of all, the pointer to the previously used structure is stored in the local variable local_18h. We’ll rename it
to a more meaningful name (afvn local_18h passwordStruct). Let’s also look into the function block by block (e.g. using the minimap mode of VV (remember from the last post?)).

    ; UNKNOWN XREF from 0x00400508 (unk)
    ; CALL XREF from 0x00400a51 (sym.main)
    0x0040096b      55             push rbp
    0x0040096c      4889e5         mov rbp, rsp
    0x0040096f      4883ec20       sub rsp, 0x20
    0x00400973      48897de8       mov qword [rbp - passwordStruct], rdi
    0x00400977      488b45e8       mov rax, qword [rbp - passwordStruct]
    0x0040097b      488b90080100.  mov rdx, qword [rax + section_end..shstrtab] ; [0x108:8]=0x288 LEA section_end..shstrtab ; section_end..shstrtab
    0x00400982      488b45e8       mov rax, qword [rbp - passwordStruct]
    0x00400986      488990100100.  mov qword [rax + 0x110], rdx
    0x0040098d      eb5b           jmp 0x4009ea

This block does only two things. First, it reserves space on the stack (0x20 or 32 bytes). Second, it stores some values based on the input struct. The input pointer goes to our local variable at 0x400973, this address is afterwards stored into rax to access it’s members (typical for structures). In 0x40097b the field a offset 0x108 is accessed (radare2 was a bit to eager when it tried to replace constants with flags). This means it reads the value of our field108 variable. The value is then stored into a field at offset 0x110 (field110). Sadly, the inline annotation of structure offsets is currently not implemented in radare2 (but is already planned for future releases).

int checkPassword(struct Foo *input) {
    // 0x00400973
    struct Foo* passwordStruct = input;
    // 0x00400977 - 0x00400986
    passwordStruct->field110 = passwordStruct->field108;

The following uncondtional jump brings us to the following small block:

    ; JMP XREF from 0x0040098d (sym.checkPassword)
    0x004009ea      488b45e8       mov rax, qword [rbp - passwordStruct]
    0x004009ee      488b80100100.  mov rax, qword [rax + 0x110]        ; [0x110:8]=0x290
    0x004009f5      488b00         mov rax, qword [rax]
    0x004009f8      4885c0         test rax, rax
    0x004009fb      7592           jne 0x40098f

This block simply checks if the pointer in field110 points to a valid pointer itself.

// 0x004009ea - 0x004009fb
if (*passwordStruct->field110) {

If the pointer is valid, it will jump to the largest block of this function:

    0x0040098f      488b45e8       mov rax, qword [rbp - passwordStruct]
    0x00400993      4889c7         mov rdi, rax
    0x00400996      e854ffffff     call sym.readPassword
    0x0040099b      8945fc         mov dword [rbp - local_4h], eax
    0x0040099e      8b45fc         mov eax, dword [rbp - local_4h]
    0x004009a1      4863d0         movsxd rdx, eax
    0x004009a4      488b45e8       mov rax, qword [rbp - passwordStruct]
    0x004009a8      488b80100100.  mov rax, qword [rax + 0x110]        ; [0x110:8]=0x290
    0x004009af      488b00         mov rax, qword [rax]
    0x004009b2      488b4de8       mov rcx, qword [rbp - passwordStruct]
    0x004009b6      4883c104       add rcx, 4
    0x004009ba      4889c6         mov rsi, rax
    0x004009bd      4889cf         mov rdi, rcx
    0x004009c0      e8abfdffff     call sym.imp.strncmp
    0x004009c5      85c0           test eax, eax
    0x004009c7      7407           je 0x4009d0

This block uses the pointer to the structure as an argument for the function readPassword. The result of this function (eax) is then stored into a local variable local_4h. This value is afterwards used for the following strncmp call (third argument, rdx). The first argument of this call is our current unknown field (offset 4) of the structure, the second argument is where field110 points at.

A pseudo representation might look like the following:

// 0x0040098f - 0x0040099b
int x = readPassword(passwordStruct);
// 0x0040099e - 0x004009c7
if (!strncmp(passwordStruct->unknown, *passwordStruct->field110, x)) {

If the inputs are equal, the following block will be exectued

    0x004009d0      488b45e8       mov rax, qword [rbp - passwordStruct]
    0x004009d4      488b80100100.  mov rax, qword [rax + 0x110]        ; [0x110:8]=0x290
    0x004009db      488d5008       lea rdx, [rax + 8]                  ; 0x8
    0x004009df      488b45e8       mov rax, qword [rbp - passwordStruct]
    0x004009e3      488990100100.  mov qword [rax + 0x110], rdx

This is again a smaller one, which takes the value in field110, increments it by 8 (one pointer size on 64bit systems) and stores it.

// 0x004009d0 - 0x004009e3
// passwordStruct->field110 = ((char*)passwordStruct->field110) + 8;
passwordStruct->field110++;

As this starts now starts over at 0x004009ea we’ll continue with the non matching case of the strings.

    0x004009c9      b800000000     mov eax, 0
    0x004009ce      eb32           jmp 0x400a02

This is simple, it just sets eax to zero and jumps to the end of the function

} else {
    // 0x004009c9 & 0x00400a02
    return 0;
}

The last block(s) represent the success case of the function

    0x004009fd      b801000000     mov eax, 1
    ; JMP XREF from 0x004009ce (sym.checkPassword)
    0x00400a02      c9             leave
    0x00400a03      c3             ret
    }
    // 0x004009fd - 0x00400a03
    return 1;
}

With some refactoring based on an overall look (jumping back to the zero check for example) we can represent the function as the following:

int checkPassword(struct Foo *input) {
    // 0x00400973
    struct Foo* passwordStruct = input;
    // 0x00400977 - 0x00400986
    passwordStruct->field110 = passwordStruct->field108;
    // 0x004009ea - 0x004009fb
    while (*passwordStruct.field110) {
        // 0x0040098f - 0x0040099b
        int x = readPassword(passwordStruct);
        // 0x0040099e - 0x004009c7
        if (!strncmp(passwordStruct->unknown, *passwordStruct->field110, x)) {
            // 0x004009d0 - 0x004009e3
            // passwordStruct->field110 = ((char*)passwordStruct->field110) + 8;
            passwordStruct->field110++;
        } else {
            // 0x004009c9 & 0x00400a02
            return 0;
        }
    }
    // 0x004009fd - 0x00400a03
    return 1;
}

As it seems, there is still one additional function missing (readPassword).

/ (fcn) sym.readPassword 124
|   sym.readPassword ();
|           ; var int local_8h @ rbp-0x8
|           ; CALL XREF from 0x00400996 (sym.checkPassword)
|           0x004008ef      55             push rbp
|           0x004008f0      4889e5         mov rbp, rsp
|           0x004008f3      4883ec10       sub rsp, 0x10
|           0x004008f7      48897df8       mov qword [rbp - local_8h], rdi
|           0x004008fb      488b45f8       mov rax, qword [rbp - local_8h]
|           0x004008ff      488b80100100.  mov rax, qword [rax + 0x110] ; [0x110:8]=0x290
|           0x00400906      4889c2         mov rdx, rax
|           0x00400909      488b45f8       mov rax, qword [rbp - local_8h]
|           0x0040090d      488b80080100.  mov rax, qword [rax + section_end..shstrtab] ; [0x108:8]=0x288 LEA section_end..shstrtab ; section_end..shstrtab
|           0x00400914      4829c2         sub rdx, rax
|           0x00400917      4889d0         mov rax, rdx
|           0x0040091a      48c1f803       sar rax, 3
|           0x0040091e      4883c001       add rax, 1
|           0x00400922      4889c6         mov rsi, rax
|           0x00400925      bfcb0b4000     mov edi, str.Enter_Password_no.__d: ; "Enter Password no. %d: " @ 0x400bcb
|           0x0040092a      b800000000     mov eax, 0
|           0x0040092f      e86cfeffff     call sym.imp.printf
|           0x00400934      488b45f8       mov rax, qword [rbp - local_8h]
|           0x00400938      4883c004       add rax, 4
|           0x0040093c      4889c6         mov rsi, rax
|           0x0040093f      bfe30b4000     mov edi, str._255s          ; "%255s" @ 0x400be3
|           0x00400944      b800000000     mov eax, 0
|           0x00400949      e862feffff     call sym.imp.__isoc99_scanf
|           0x0040094e      488b45f8       mov rax, qword [rbp - local_8h]
|           0x00400952      c68003010000.  mov byte [rax + 0x103], 0
|           0x00400959      488b45f8       mov rax, qword [rbp - local_8h]
|           0x0040095d      4883c004       add rax, 4
|           0x00400961      4889c7         mov rdi, rax
|           0x00400964      e827feffff     call sym.imp.strlen
|           0x00400969      c9             leave
\           0x0040096a      c3             ret

This function has no jumps or internal function calls. The summary view (pdfs) shows which flags and calls are used
by the function:

0x00400925 "Enter Password no. %d: "
0x0040092f call sym.imp.printf
0x0040093f "%255s"
0x00400949 call sym.imp.__isoc99_scanf
0x00400964 call sym.imp.strlen

Simply based on this output we could already guess what this function does:

int readPassword(struct Foo* foo) {
    printf("Enter Password no. %d\n");
    scanf("%255s");
    return strlen();
}

Obviously, this is not enough for us, as we want to know exactly what’s really going on. Let’s start with the part 0x004008ef-0x0040090d:

The first instructions represent again a typical function prologue. They reserve 0x10 bytes on the stack and store the input pointer into this buffer (local_8h). Afterwards, field110 is stored in rdx and field108 in rax.

int readPassword(struct Foo* foo) {
    // 0x004008ef-0x0040090d
    struct Foo* local_8h = foo;
    rdx = local_8h->field110;
    rax = local_8h->field108;

Part 0x00400914-0x0040091e consists of some mathematical instructions which can be translated into the following code snippet:

rax = rdx - rax
rax >>= 3
rax += 1

As we know, the rdx and rax registers contain pointers to a list of strings. We also know (based on the checkPassword function) that field110 is incremented during the processing, whilst field108 stays the same. This ultimatively means that rdx - rax describes the offset between the two list entries on which field110 and field108 are pointing at. The next instruction (rax >>= 3) dos a right shift of the value in rax (our offset) by three positions. A right shift by one is equivalent to a division by two, therefore we have a
division by eight here. As we are dealing with 64bit pointers, these two instructions simply calculate how many times the field110 pointer was moved forward. The last increment just increments them by one, as (based on the following string) the current (one based) position should be printed out.

Together with the instructions 0x00400922-0x0040092f we get:

int readPassword(struct Foo* foo) {
    // 0x004008ef-0x0040092f
    struct Foo* local_8h = foo;
    printf("Enter Password no. %d\n", (local_8h->field110 - local_8h-field108) + 1);

Part 0x00400934-0x00400952 represents a scanf call with the unknown field of the struct (offset 4) followed by an explicit zero byte at the end:

// 0x00400934-0x00400952
scanf("%255s", local_8h->unknown);
local_8h->unknown[0x103] = 0;

The end of the function simply returns the result of a strlen call with the input read by scanf.

int readPassword(struct Foo* foo) {
    // 0x004008ef-0x0040092f
    struct Foo* local_8h = foo;
    printf("Enter Password no. %d\n", (local_8h->field110 - local_8h-field108) + 1);
    // 0x00400934-0x00400952
    scanf("%255s", local_8h->unknown);
    local_8h->unknown[259] = 0;
    // 0x00400959 - 0x0040096a
    return strlen(local_8h->unknown);
}

As we have now reversed all functions, we can see that this time multiple passwords are required and that they are stored in an array at obj.passwords. We’ve also reversed the format of the structure and (just for reference) can now rename the corresponding fields. This could be either done by editing the
header file, editing the database entries directly (tk) or by overwriting the definition inline

"td struct Foo {int field0; char password[260]; char** passwordListHead; char** currentPasswordListEntry;};"

Note: the quotes around the command are required to ensure that the definition is parsed correctly. Also be aware that the offsets may be incorrectly parsed.

Let’s test the passwords:

$ ./challenge03
##################################
#          Challenge 3           #
#                                #
#      (c) 2016 Timo Schmid      #
##################################
Enter Password no. 1: shad7Pe
Enter Password no. 2: miTho7i
Enter Password no. 3: Wa3suac
Enter Password no. 4: ohGhah7
Enter Password no. 5: Aibah1a
Password accepted!

Challenge 0x04

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

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

MD5 (challenge04) = 215a8fa0a80c95082f8fb279ad7e5b9b
MD5 (challenge04.exe) = 016b96d38e914a96dca372b30d6d0949
MD5 (challenge04-static) = 87e67337a34fa8892bc2fc3c68c4db7d

SHA1 (challenge04) = 95a042b68c036c1d6b6698820589480be29a1437
SHA1 (challenge04.exe) = e4351d511c05666984c43d24cd4c338b3d715a66
SHA1 (challenge04-static) = 64d1ce0ae9a653860953faa87f88377fc0eda34b

SHA256 (challenge04) = 2829ef89135f9065c8b1122a13cde09df5d19768e25feb162a758ab168e1bfb4
SHA256 (challenge04.exe) = d63aa0768ba64eb5211a4f86c07b4b45b17e6125b79b24620f854c70aab401de
SHA256 (challenge04-static) = f723e4f44657484945501bf5ccdab2e7dd229db719f3568ad3cd88da382dccf9

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 fourth challenge and challenge #5.

Happy reversing!

Best,

Timo

@bluec0re

Comments

  1. Hi, I am just wondering how did you get the value “char unknown[260]”?
    How do you know the password is 260 bytes?

  2. We know that the array begins at offset 4 (line 0x004009b6) and we know that another field is starting at offset 0x108 = 264 (line 0x0040097b). 264 minus 4 equals 260. The original code actually specified 256 bytes, but due to alignment optimisations (to 8 bytes on amd64) it was extended to 260 by the compiler. Does this help?

    Best,
    Timo

    1. Thank you for your kind reply. I am still pretty new to reversing so I still have two questions:

      1. I think there is a typo in your answer. Did you mean (line 0x0040096b)? Also, how to know that the array begins at offset 4? There is no indications in that line.

      2. 256 bytes are already power of 2 and can be divided by 8. Why would the compiler optimize it to 260 bytes?

      Thank you again for your help.

      1. In the line 0x004009b6 4883c104 add rcx, 4 you can see that rcx is incremented by 4. And rcx was set to the beginning of the struct in the line before. This repeats btw also in lines 0x00400938 and 0x0040095d (rax this time). Yes, you’re right that 256 bytes is already dividable by 8. But as the array is preceded by a 32bit value, it would actually ends after 260 bytes from the beginning of the struct. This is not dividable by 8 and why the compiler extends it to 260 bytes (array size) or 264 bytes (actual end of the array from the beginning of the struct).

Comments are closed.