Contents

Practical Reverse Engineering Exercise Solutions: Page 35 / Exercise 7

Contents

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.

../images/thumbnails/2017-07-30-practical-reverse-engineering-exercise-solutions-page-35-exercise-7-30-07-_2017_10-30-00.png

../images/thumbnails/2017-07-30-practical-reverse-engineering-exercise-solutions-page-35-exercise-7-29-07-_2017_18-38-05.png

../images/thumbnails/2017-07-30-practical-reverse-engineering-exercise-solutions-page-35-exercise-7-30-07-_2017_10-28-03.png

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
}