This blog post presents my solution to exercise 7 on page 35 from the book Practical Reverse Engineering by Bruce Dang, Alexandre Gazet and Elias Bachaalany (ISBN: 1118787315). The book is my first contact with reverse engineering, so take my statements with a grain of salt. All code snippets are on GitHub. For an overview of my solutions consult this progress page.

Problem Statement

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.

Here you can see the complete function. In the following I’m doing a walk-through of the code, based on the PE offset reference. After that

Walk-Through

▶ First Function Parameter

The function does not have a function prologue. Instead it starts with copying the first function parameter to EAX:

mov eax, [esp+4]

From the hint, and the way the offset work out later on, I infer that this parameter is a pointer to a PE file. So EAX now points to the start of an executable, which is the IMAGE_DOS_HEADER.

▶ Offset to Start of PE Header

Lines 2 and 3 just save EBX and ESI on the stack, so the registers can be used locally. Line 4 adds 0x3C to EAX and stores the result in ESI:

push ebx
push esi
mov esi, [eax+3Ch]

EAX is a pointer to an IMAGE_DOS_HEADER, at offset 3C we have the member e_lfanew, which gives the offset to the real PE header. So ESI = IMAGE_DOS_HEADER.e_lfanew.

▶ Position of PE Header

add esi, eax

ESI holds the offset to the PE header. By adding the start of the PE file still in EAX we get the position of the PE header. The header is a struct called IMAGE_NT_HEADER, so ESI = PIMAGE_NT_HEADER.

▶ Return if there aren’t any Sections

xor ebx, ebx
cmp [esi+6], bx
...
...
jbe short loc_0_10BEB

Line 7 sets EBX=0. In line 8 we get the member of IMAGE_NT_HEADER at offset 6. Subtracting the 4 byte for IMAGE_NT_HEADER.Signature we land 2 bytes into the IMAGE_FILE_HEADER, which is the member IMAGE_FILE_HEADER.NumberOfSections. Apart from headers, a PE file consists of sections such as .text or .data. IMAGE_FILE_HEADER.NumberOfSections indicates how many sections there are. The above snippet will make a jump to loc_0_10BEB if the number of sections is zero (or less). As will be shown later, loc_0_10BEB returns NULL.

▶ Get Start of Section Table

push edi
lea edi, [eax+esi+18h]

The LEA instruction is used as a fast way to add three values in one instruction. ESI is still a pointer to the IMAGE_NT_HEADER. At offset 0x18h in this struct we find the IMAGE_OPTIONAL_HEADER. The size of this header is in EAX = IMAGE_FILE_HEADER.SizeOfOptionalHeader. By adding the size of the optional header to the start of IMAGE_OPTIONAL_HEADER we jump over this header and end up at the start of the section table. The section table consists of IMAGE_FILE_HEADER.NumberOfSections sections of type IMAGE_SECTION_HEADER.

In summary: Lines 1-10 calculate the position of the first IMAGE_SECTION_HEADER (elements of the section table), and store the result in EDI.

▶ Call Another Subroutine

loc_0_10BCE:
push [esp+0Ch+8]
push edi
call ds:dword_0_169A4

The routine doesn’t use the EBP. It therefore has to retrieve its function parameters in relation to the ESP. The function already pushed 12 bytes on the stack, that’s why the second function parameter is now at [esp+0Ch+8]. The value is pushed on the stack, along with EDI which is the start of the section table. The call is therefore

int r = some_function(image_section_header, arg2);

Without reversing this function too, it’s hard to tell what it does. It might for instance compare the name of the section header to arg2.

▶ Check Return Value

test eax, eax
pop ecx
pop ecx
jz short loc_0_10BF3

The unknown subroutine returns a double word. Line 17 an 20 check if the return value is zero. If this is the case, the subroutine jumps to loc_0_10BF3. I’ll talk about this section later. For now, assume that the return value was not zero, which might indicate that the “search” in dword_0_169A4 was not successful.

The two pop instructions in lines 18 and 19 simply clean up the stack – dword_0_169A4 is obviously using the CDECL calling convention.

▶ Iterate

Line 21, as in line 8, retrieves the number of sections IMAGE_FILE_HEADER.NumberOfSections. Line 23 increments EBX, which is still 0 from the instruction in line 7, by one. EBX is the loop counter. Line 24 and 25 are the test of a loop. Line 22 points EDI to the next image section header. So these lines form a loop:

Summary

The function iterates over all image section headers of a PE file. It returns the first such section header for which an unknown function ds:dword_0_169A4 returns zero, if no such section exists, the function returns NULL.

The second parameter of our function is passed to ds:dword_0_169A4 and might be the criterion applied to the search of the section header. The unknown function might for instance compare the name in the image section headers to the second argument of our function.