Thursday, 29 June 2017

Practical Reverse Engineering Exercise Solutions: ObFastDereferenceObject

First of all a quick reminder: This series of blog posts relates to exercises from the book Practical Reverse Engineering by Dang et al. Although it is called reverse engineering in general, it actually is mostly relevant to Microsoft Windows operating systems. This is simply due to the fact that Microsoft Windows is closed source in contrast to the Linux/Unix families, which means its source code is publicly available and so no reverse engineering endeavours are necessary.

Our next task is to decompile the ObFastDereferenceObject routine, with special consideration to be paid to its calling convention.
The most common calling conventions for functions are:

  • stdcall (arguments are pushed from right to left to the stack, the called function has to clean up the stack at the end of the routine)
  • cdecl (arguments are pushed from right to left to the stack, the calling function has to clean up the stack at the end of the routine)
  • fastcall (arguments are passed in registers ecx and/or edx first, the other arguments are pushed from right to the left onto the stack)
MSDN describes these and more conventions excellently: https://msdn.microsoft.com/en-us/library/984x0h58.aspx

Microsoft seems, however, not to have documented the function in question publicly. Yet, there is an unofficial resource at http://gate.upm.ro/os/LABs/Windows_OS_Internals_Curriculum_Resource_Kit-ACADEMIC/WindowsResearchKernel-WRK/WRK-v1.2/base/ntos/ob/fastref.c

It states:
NTKERNELAPI
VOID
FASTCALL
ObFastDereferenceObject (
    IN PEX_FAST_REF FastRef,
    IN PVOID Object
    )
/*++

Routine Description:
    This routine does a fast dereference if possible.

Arguments:
    FastRef - Rundown block to be used to dereference the object

Return Value:
    None.


We show the disassembly of the function of interest:


We notice two indicators that the function utilizes the fastcall convention: Firstly, the register value at edx is read without prior initialization. This means that arguments have been passed in these registers beforehand, thereby conforming to the fastcall convention. Secondly, the function name contains the keyword fast.
Moreover, the last line of the routine specifies that 4 bytes are removed from the stack (ret 4 instruction). This means that one of the two function arguments is passed via the stack rather via a register value. A quick glimpse into the disassembly shows that the value from [ebp+8] is read at the beginning of part +0x21. With fastcall, the first parameter(s) are passed in registers while the remaining ones are pushed on the stack. So we can infer that edx is the FastRef variable, while Object is saved at ebp+8.

According to https://www.nirsoft.net/kernel_struct/vista/EX_FAST_REF.html the PEX_FAST_REF datatype is defined as follows. 

typedef struct _EX_FAST_REF
{
     union
     {
          PVOID Object;
          ULONG RefCnt: 3;
          ULONG Value;
     };
} EX_FAST_REF, *PEX_FAST_REF;

Notice that thisdata structure is of type union. As I have learned C a couple of years ago and the type is not completely famililar to me any more, it is worthwhile to recapitulate its meaning. It basically allows to store multiple data types at the same memory location, so the meaning of the memory content varies depending on the referenced variable. While this can be memory-efficient, it is essential that the program always reads the variable that has been set for the last time, as it could otherwise contain invalid and potentially dangerous contents.

The Disassembly contains a somewhat peculiar function, namely lock cmpxchg dword ptr [edi],esi.

According to its name, cmpxchg seems to exchange something and its semantics are explained in detail at http://x86.renejeschke.de/html/file_module_x86_id_41.html.

Translating the instruction from above to C-pseudo code yields:
if (eax == [edi]) {
 [edi] = esi
}
else {
 eax = [edi]
}
The LOCK prefix means the instruction will be executed atomically and ensure that the processor has exclusive access to the memory region. It is thus a basic primitive for managing multi-processor / multi-threading environments and synchronization protocols. (see also http://x86.renejeschke.de/html/file_module_x86_id_159.html)


The first attempt to translate the function to C:

ObFastDereferenceObject (
    IN PEX_FAST_REF FastRef,
    IN PVOID Object
    )


edx = FastRef
[ebp+8] = Object

// Loop initialization

ecx = *FastRef
eax = *FastRef

goto loopcheck

loopbody:
esi = &(ecx->Object) + 1
edi = edx (FastRef)
eax = ecx
if (eax == [edi]) {
 [edi] = esi // eax will not be modified, i.e. afterwards we have eax==ecx
}
else {
 eax = [edi]
}

if (eax==ecx)
 goto finish //(the exchange operation actually was successful)
else  
 {
 ecx = eax (*FastRef)
 goto loopcheck
 }

loopcheck:
eax = eax XOR [ebp+8] 
if (eax < 7) // this means all higher bits are zero, i.e. 000000xxx and the least significant three bits are not all 1, i.e. 111 = 7 is not possible
 goto loopbody
else
 {
 ObDereferenceObject(Object)
 goto finish
 }

finish:
ret 4

No comments:

Post a Comment