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);
}
|