Reversing XignCode3 Driver – Part 2 – Analyzing init functions

If you haven’t read the previous post you can find it here. Today we will continue analyzing some functions that DriverEntry is using.

On part 1 we identified the Dispatcher function of the driver as well as two functions that were initializing some variables for the driver (fn_InitDispatchMethodArray and fn_ObtainKernelFunctions). Let’s reverse each of them, and quickly analyze what are they doing. This will help us to understand the Dispatcher functions implemented on this driver.

What we will go through?

  1. Some basic initialization mechanism of this Driver
  2. Identify what it seems to be a custom structure used to index and store all available methods.
  3. Identify how function addresses are being located on memory. For example, ObRegisterCallbacks. We are going to be talking about this in the following posts.

fn_InitDispatchMethodArray (0x1400015F8)

The original code for this function can be found here: decompiled and asm. And the final result here (try not to spoil yourself yet).

We are going to see now in this function that a sort of custom structure is being initialized:

.text:0000000140001617                 mov     cs:dword_140009E40, 306h
.text:0000000140001621                 mov     cs:qword_140009E48, rax
.text:0000000140001628                 lea     rax, sub_14000101C
.text:000000014000162F                 mov     cs:qword_140009E58, rax
.text:0000000140001636                 lea     rax, sub_140001CC8
.text:000000014000163D                 mov     cs:qword_140009E68, rax

We can figure this out, because if we pay attention to the assembly, we will see that they are assigning first a int value to a particular address of memory, and then moving 8 bytes further to write a pointer to a function inside of the binary. We will call this structure IOCTLFunctionArray. An array? just keep reading =) This array will play an important role while dispatching a request.

The structure would be something like this:

typedef struct DispatcherStruct  {
    int Index;
    char padding[4];
    PVOID FnPtr;

And in IDA Pro:

00000000 DispatcherStruct struc ; (sizeof=0x10, mappedto_424)
00000000                                         ; XREF: .data:_IOCTLFunctionArray/r
00000000 Index           dd ?                    ; XREF: fn_InitDispatchMethodArray+1F/t
00000004 padding         db 4 dup(?)
00000008 FnPtr           dq ?
00000010 DispatcherStruct ends

This process is repeated multiple times on this function, 25 times exactly. That’s why we call it array, they are stored 25 times the same structure (with different values, of course) inside of an array.

The value 25 is also being stored in a variable I decided to rename from dword_14000A240 to FunctionsCount:

.text:000000014000186A                 mov     cs:FunctionsCount , 19h  

We will see later how this variable is used on the Dispatcher, but we may be able to try to guess it. Based on this function, we can deduse that the driver has a list of all available methods that can be invoked, and providing some kind of index value, it could be possible to invoke them.

The final result would be something like this. Note that some functions have been renamed since I have started to reverse them before. We will see some interesting ones on the following posts.

fn_ObtainKernelFunctions (0x140002A18)

The following function is simple. In order to continue with the initialization, the Driver needs the address of some particular routines:

By doing this, they can make sure that those functions are available on the running version of windows and obtain a pointer to them. They just need to store them in a variable and then use it to call any of those routines by casting it to the propery function definition. You can spot this easily also on the assembly function:

.text:0000000140002A1C                 lea     rdx, SourceString ; "ObGetFilterVersion"
.text:0000000140002A23                 lea     rcx, [rsp+38h+DestinationString] ; DestinationString
.text:0000000140002A28                 call    cs:RtlInitUnicodeString
.text:0000000140002A2E                 lea     rcx, [rsp+38h+DestinationString] ; SystemRoutineName
.text:0000000140002A33                 call    cs:MmGetSystemRoutineAddress
.text:0000000140002A39                 lea     rdx, aObregistercall ; "ObRegisterCallbacks"
.text:0000000140002A40                 mov     cs:qword_14000A288, rax
.text:0000000140002A47                 lea     rcx, [rsp+38h+DestinationString] ; DestinationString
.text:0000000140002A4C                 call    cs:RtlInitUnicodeString
.text:0000000140002A52                 lea     rcx, [rsp+38h+DestinationString] ; SystemRoutineName
.text:0000000140002A57                 call    cs:MmGetSystemRoutineAddress

There is not much difference between the original code and the final result. The code was kind of obvious for this function.

Next Steps

  • Analyze Dispatch function (fn_DriverIOCTLDispatcher)
  • Analyzing Registering Notify and Callback Routines (fn_InitRegistrationNotifyAndCallbackRoutines and fn_RegisterCreateProcessNotifyRoutine)