Exercise 7 on page 35:
Sample H. The function sub_10BB6
has a loop searching for something. First recover the function prototype and then infer the types based on the context. Hint: You should probably have a copy of the PE specification nearby.
Due to alignment issues, our routine is located at 10BB2
and has the following disassembly:
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
31
32
33
34
35
36
37
38
39
40
| sub_10BB2:
mov eax, [esp+4]
push ebx
push esi
mov esi, [eax+3Ch]
add esi, eax
movzx eax, word ptr [esi+14h]
xor ebx, ebx
cmp [esi+6], bx
push edi
lea edi, [eax+esi+18h]
jbe short loc_10BEB
loc_10BCE:
push [esp+0Ch+arg_4]
push edi
call ds:dword_169A4
test eax, eax
pop ecx
pop ecx
jz short loc_10BF3
movzx eax, word ptr [esi+6]
add edi, 28h
inc ebx
cmp ebx, eax
jb short loc_10BCE
loc_10BEB:
xor eax, eax
loc_10BED:
pop edi
pop esi
pop ebx
retn 8
loc_10BF3:
mov eax, edi
jmp short loc_10BED
|
The PE file format and offsets have been described in detail here: http://www.sunshine2k.de/reversing/tuts/tut_pe.htm
Other useful sites are:
Firstly, we notice that the program contains the statement retn 8
, which means it takes two arguments that are passed on the stack. Thus, its abstract prototype looks as follows:
1
| sub_10BB2(param1, param2);
|
The first parameter seems to be a pointer that fetches a value from the offset 0x3C
. Looking at the PE file format specification, we see the offset at 0x3C
of the DOS header structure contains a pointer to the PE header structure. Therefore, we assume that param1 points to a DOS header file structure.
We refine the function prototype to:
1
| sub_10BB2(PIMAGE_DOS_HEADER param1, param2);
|
Below, the disassembly including the provisional decompilation is provided:
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
31
32
33
34
35
36
37
38
39
40
41
| mov eax, [esp+4] // function has first argument at esp+4, which means it is located on the stack
push ebx
push esi
mov esi, [eax+3Ch] // long offsetpeheader = PIMAGE_DOS_HEADER->lfanew;
add esi, eax // PIMAGE_NT_HEADERS pNtHeaders = (dosheaderptr + offsetpeheader); // calculate the effective address of the PE header beginning
movzx eax, word ptr [esi+14h] // word sizeOptHeader = pNtHeaders->FileHeader.SizeOfOptionalHeader;
xor ebx, ebx // int countSections = 0;
cmp [esi+6], bx // word numSections = pNtHeaders->FileHeader.NumberOfSections
push edi // save edi for later
lea edi, [eax+esi+18h] // int* sectionTable = pNtHeaders->OptionalHeader + sizeOptHeader; AND! the end address is immediately the beginning of the new
data structure, i.e. the first section! SectionTable is located at offset(OptionalHeader) + SizeOfOptionalHeader
jbe short loc_10BEB // when we have no sections (countSections == numSections) , go to exit routine
loc_10BCE:
push dword ptr [esp+14h]
push edi
call ds:dword_169A4 // dword_169A4(sectionTable, param2)
test eax, eax // check if returned 0
pop ecx // pop value from stack to ecx
pop ecx // pop value from stack to ecx
jz short loc_10BF3 // if eax == 0 , jump
movzx eax, word ptr [esi+6] // reload number of Header
add edi, 28h // sectionTable += 0x28; // one section table has a size of 0x28 bytes (40 bytes)
inc ebx // countSections += 1;
cmp ebx, eax // if countSections < numSections goto loc_10BCE:
jb short loc_10BCE
loc_10BEB:
xor eax, eax // int* retVal = 0;
loc_10BED:
// clean up stack (stdcall convention)
pop edi
pop esi
pop ebx
retn 8 // function has two parameters, 2 x 4 bytes
loc_10BF3:
mov eax, edi // int* retVal = sectionTable;
jmp short loc_10BED
|
Finally, we arrive at the following decompiled function:
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
| sub_10BB2(PIMAGE_DOS_HEADER dosHeaderPtr, param2) {
long offsetpeheader = PIMAGE_DOS_HEADER->lfanew;
PIMAGE_NT_HEADERS ntHeadersPtr = (dosHeaderPtr + offsetpeheader); // calculate the effective address of the PE header beginning
word sizeOptHeader = ntHeadersPtr->FileHeader.SizeOfOptionalHeader;
int countSections = 0;
word numSections = ntHeadersPtr->FileHeader.NumberOfSections;
int* sectionTablePtr = ntHeadersPtr->OptionalHeader + sizeOptHeader;
while (countSections < numSections) {
int retVal = dword_169A4(sectionTablePtr, param2); // call unknown function
if (retVal == 0) {
return sectionTablePtr;
}
sectionTablePtr += 0x28; // get the next sectionTable
countSections += 1; // set the number of processed sections
}
return 0; // we have not found the target section
}
|