Contents

Practical Reverse Engineering Exercise Solutions: Page 35 / Exercise 6

Contents

Exercise 6 on page 35 of the book Practical Reverse Engineering presents us with a malware samples.

These can be downloaded at the following page:

https://grsecurity.net/malware_research/

In this exercise, we are expected to have a look at the following routine sub_13842:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.text:00013842 sub_13842      
.text:00013842                 mov     eax, [ecx+60h]
.text:00013845                 push    esi
.text:00013846                 mov     esi, [edx+8]
.text:00013849                 dec     byte ptr [ecx+23h]
.text:0001384C                 sub     eax, 24h
.text:0001384F                 mov     [ecx+60h], eax
.text:00013852                 mov     [eax+14h], edx
.text:00013855                 movzx   eax, byte ptr [eax]
.text:00013858                 push    ecx
.text:00013859                 push    edx
.text:0001385A                 call    dword ptr [esi+eax*4+38h]
.text:0001385E                 pop     esi
.text:0001385F                 retn

Firstly, we see that the function prototype takes two parameters, which are not saved on the stack but in the two registers ecx and edx. This can be deducted from the fact that these two registers are immediately referenced without prior initialization.

On a very high abstraction level, the function prototype looks as follows:

1
sub_13842(struct1* a, struct2* b);

Moreover, we see that edx+8 points to some kind of base address, as it is used to compute the address of the function called on the following line:

1
.text:0001385A                 call    dword ptr [esi+eax*4+38h]

Both parameters edx and ecx obviously point to some kind of data structure, whose contents we are about to reveal:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct1: 
offset+60: struct3*: s3
offset+23: char: unknown0x23

struct2:
offset+08: void*: here we have some kind of trampoline / array of function pointers

struct3:
offset+00: char: index
offset+14: struct2*: s2

struct4:
offset+38: int*: unknown

sub_13842(struct1* a, struct2* b) 

    .text:00013842 sub_13842      
    .text:00013842                 mov     eax, dword ptr[ecx+60h] // struct3* v1 = a->s3;
    .text:00013845                 push    esi // just save esi register value
    .text:00013846                 mov     esi, [edx+8] // void* v2 = b->unknown0x8
    .text:00013849                 dec     byte ptr [ecx+23h] // a->unknown0x23 = a->unknown0x23 - 1;
    .text:0001384C                 sub     eax, 24h // v1 = v1 - 0x24;
    .text:0001384F                 mov     [ecx+60h], eax // a->s3 = v1;
    .text:00013852                 mov     [eax+14h], edx // v1->s2 = b;
    .text:00013855                 movzx   eax, byte ptr [eax] // char index = v1->index;
    .text:00013858                 push    ecx 
    .text:00013859                 push    edx // call fct(
    .text:0001385A                 call    dword ptr [esi+eax*4+38h] // call b->unknown0x8+0x38h[index](b, a)
    .text:0001385E                 pop     esi
    .text:0001385F                 retn

Overall, the decompiled code looks as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
sub_13842(struct1* a, struct2* b) {

    struct3* v1 = a->s3;
    void* v2 = b->unknown0x8;
    a->unknown0x23 = a->unknown0x23 -1;
    v1 = v1 - 0x24;
    a->s3 = v1;
    v1->s2 = b;
    char index = v1->index;
    b->unknown0x8+0x38h[index](b, a);
}