Initial analysis

When we open the file revloader.exe in a PE viewer we see that it’s a PE64 file. The file contains three unencrypted resources in the RCData directory.  We extract all three resource files to look at what’s in these files.

Resources of revloader.exe

Resource 101

This file is also a PE64 and contains some version information strings, which tells us that it’s DSEFix. DSEFix is a tool that can bypass the driver signature enforcement in Windows by using an exploit in a signed VirtualBox driver. By using DSEFix, you can disable DSE and load any driver you want. So it looks like we’ll solve this challenge by using an unsigned windows driver.

Resource 102

This is the driver itself. It’s also a PE64 and uses FLTMGR.SYS. In version information, we can see that it’s a windows driver with the internal name revhunt.sys. We can also see that the file is not signed, so it makes sense that revloader.exe is going to use DSEFix to load the revhunt.sys.

Resource 103

This is an INF file for the driver. We can see that it’s a mini file filter driver and that it uses the altitude 31337.

Revloader

Let’s start with revloader.exe to verify that it’s going to drop DSEFix driver and run DSEFix to disable windows driver signature enforcement. We can take a look at the import table to find important functions that might be used for this kind of work.

  • LookupPrivilegeValueW/AdjustTokenPrivilege: to acquire the missing rights to load a driver (“SeLoadDriverPrivilege”).

  • LoadResource/FindResource: to find a resource and map it to memory.

  • CreateFileW: to create or access a file on the disk.

  • WinExec: to execute a file.

  • FilterLoad/FilterUnload: to load and unload a mini filter driver.

Following cross-references to FilterLoad we can see that the function to load the driver is at 0x140001800. That function uses InstallHinfSectionW with arguments “DefaultInstall 132 <filename.inf>” to install the driver and then uses FilterLoad to load the driver. If we follow that function back, we can see that this function has been called from main function and we can see that there are a few more interesting calls:

We can see that the function at 0x140001FD0 will be called with argument 101, 102, and 103. That means this function is probably accessing the embedded resources. If we look into that function we will see that it uses FindResourceW, SizeofResource, LoadResource, CreateFileW and WriteFile. We can rename that function to DropResource. After these three calls, it will call WinExec.

That means that revloader is dropping the resources to current directory (resource 101, 102, 103) and then executing dsefix.exe (resource 101) after it loads the mini file filter driver (resource 102 + 103).

Let’s run revloader.exe to load the driver into kernel and see if it works.

We can see the “Welcome to revhunt x86-x64”. Now let’s analyze the driver and try to find the flag.

Revhunt

Opening revhunt.sys in IDA Pro we can see that in DriverEntry function there is a jump to 0x140007000. That looks like the real main function.

We can see that the driver is using some kernel anti-debugging functions like KdDebuggerNotPresent and KdDisableDebugger. That means if we attach a kernel debugger to our virtual machine, the function KdDisableDebugger will disconnect us. We would have to patch KdDebuggerNotPresent and KdDisableDebugger with NOP instruction or, since the driver is not signed, we can also patch the calls in the driver itself.

We can also see that the driver uses FltRegisterFilter and FltStartFiltering. Let’s take a look at definition of FltRegisterFilter:

NTSTATUS FltRegisterFilter(
_In_        PDRIVER_OBJECT   Driver,
_In_  const FLT_REGISTRATION *Registration,
_Out_       PFLT_FILTER      *RetFilter
);

FltRegisterFilter takes a FLT_REGISTRATION structure as a second argument that contains information (flags, callback routines, etc.) for the registration of the mini file filter driver. The second argument for FltRegisterFilter in this case is:

  --- -----------------------------
  1   lea     rdx, unk_140003140
  --- -----------------------------

We can go to IDA Pro and set a structure at 0x140003140 as FLT_REGISTRATION but before we do that we have to load the correct type library: Windows Driver Kit 8 (kernel mode) – wdk8_km. You can also take any newer DDK libraries. Now we can set the correct structure:

Using that we can quickly identify what each sub-function is used for. We can also see that the flags for the structure are set to 0x02, which is FLTFL_REGISTRATION_SUPPORT_NPFS_MSFS. According to MSDN that means that the driver supports named pipes and mailslot requests as normal file events.

Now let’s do the same with OperationRegistration and set a structure of FLT_OPERATION_REGISTRATION:

Checking quickly for PreOperation and PostOperation code, we can see that the really interesting code is going on in the PostOperation function, so let’s analyze that.

PostOperation callback

Before we start, we should set a new function definition in IDA Pro to the correct definition like in MSDN, it will make our analysis a lot easier (and if you use the Hex-Rays Decompiler it will decompile a lot better). So let’s change the function definition to:

typedef FLT_POSTOP_CALLBACK_STATUS ( *PFLT_POST_OPERATION_CALLBACK)(
_Inout_  PFLT_CALLBACK_DATA       Data,
_In_     PCFLT_RELATED_OBJECTS    FltObjects,
_In_opt_ PVOID                    CompletionContext,
_In_     FLT_POST_OPERATION_FLAGS Flags
);

__int64 __fastcall PostOperation(PFLT_CALLBACK_DATA
CallbackData, PCFLT_RELATED_OBJECTS FltObjects, PVOID
CompletionContext, FLT_POST_OPERATION_FLAGS Flags);

By loading the correct type libraries, we can simplify the analysis because that code looks easier to read:

We can see that the function is checking if the current FileObject is a mailslot event (FO_MAILSLOT), if the current file name of the object is 18 characters long, if “\gsrt.txt”, and if the current event is opening the file. If that’s the case, then it will use FltReadFile to read from the file and compare the content with encrypted data:

So let’s quickly decode that using a python script:

data = [0x7F, 0x56, 0x71, 0x4E, 0x61, 0x52, 0x7D, 0x43, 0x7B, 0x19, 0x70, 0x58, 0x7E,
0x37]
for x in range(0, 14):
if (x % 2 == 0):
data[x] ^= 0x13
else:
data[x] ^= 0x37
print str (bytearray(data))
$ python decoder.py
labyrenth.com

That means, we have to create a file named “\gsrt.txt” with the content “labyrenth.com” and then open the file. After that we can see there is another byte array initialized on the stack and decode it using the same technique as before:

Let us write that to our script and decode also that string.

data = [ 0x5C, 0x5F, 0x3D, 0x19, 0x3D, 0x17, 0x7C, 0x5C, 0x72, 0x4E, 0x3D, 0x19,
0x3D, 0x19, 0x5A, 0x17, 0x7E, 0x52, 0x72, 0x59, 0x33, 0x7E, 0x43, 0x74,
0x33, 0x58, 0x7D, 0x17, 0x2D, 0x0A, 0x33, 0x60, 0x7A, 0x59, 0x77, 0x58,
0x64, 0x44, 0x33, 0x0F, 0x33, 0x40, 0x7C, 0x42, 0x7F, 0x53, 0x33, 0x5F,
0x72, 0x41, 0x76, 0x17, 0x71, 0x52, 0x76, 0x59, 0x33, 0x52, 0x72, 0x44,
0x7A, 0x52, 0x61, 0x17, 0x71, 0x42, 0x67, 0x17, 0x7E, 0x5A, 0x7E, 0x5A,
0x78, 0x56, 0x6A, 0x17, 0x6A, 0x58, 0x66, 0x17, 0x72, 0x45, 0x76, 0x17,
0x67, 0x5F, 0x76, 0x17, 0x61, 0x52, 0x65, 0x52, 0x61, 0x44, 0x76, 0x45,
0x33, 0x5F, 0x76, 0x45, 0x76, 0x19, 0x19, 0x37]

for x in range(0, 104):
if (x % 2 == 0):
data[x] ^= 0x13
else:
data[x] ^= 0x37

print str(bytearray(data))

$ python decoder.py
Oh... okay....I mean IPC on &gt;= Windows 8 would have been easier but mmmmkay you are the reverser here.

That looks like a hint that tells us that we could have used Interprocess Communications (IPC) to do the same. If we scroll up again we will see that the sample was comparing the current FileObject.Flags with FO_MAILSLOT. That means if there was any Mailslot event it would have directly jumped to 0x140001405, which looks like an initialization function.

If we take a look into that function, we will see that it will allocate a memory address using ExAllocatePoolWithTag and save the result at 0x140004020, which I have named lpszBuffer.

If we take a look at the cross-references to lpszBuffer, we will see that it’s used in four functions: PostOperation, InitBuffer, 0x14000181C, and Unload. We will look into 0x14000181C a little later but now let’s go back to the PostOperation function and see what happens next.

After the InitBuffer call, it will check the filename of the FileObject again and compare it with “\pan.flag”. If that’s correct, it will call the function 0x14000181C and it will print the flag using DbgPrint. That means that the decrypting routine is at 0x14000181C and that lpszBuffer will contain the decoded flag.

Now let’s take a look into the function at 0x14000181C. I have renamed that function to TestFlag.

Analyzing flag testing function

At the beginning of the function we can see that it uses FltReadFile again to read the content of the given FileObject. We can also see that it reads 58 bytes. The first four bytes are compared with “PAN{“.

After that we can see first set of XOR data. It will take the next four bytes and XOR them with 0x1A1B1C1D and compare the result with 0x366C734A. It’s time to start writing our decoder.

key = "PAN{"
key += (hex(0x366C734A ^ 0x1A1B1C1D)[2:]).decode('hex')[::-1]

Looks good so far…

python decoder.py
PAN{Wow,

Let’s move on to the next characters. The next code segment is doing some shifting and XOR based on the result of KdDebuggerNotPresent. Function KdDebuggerNotPresent should return 1 if there is no debugger attached. We also have to reverse the order to get the correct key:

key += chr((0xB0 ^ 0xF0) &gt;&gt; 1)

The result is a space character (0x20 in hex). We can move on to the next character but we have to reverse the order again to get the correct character (instead of add, we have to use sub, etc.):

key += chr((0x56 ^0x20) - 4)

Now we just have to continue to slowly decode the whole flag but it gets trickier with a call to 0x140001AFC. At this function it will try to access characters at different offsets and do weirder calculations:

For example, at 0x140001B41 it will load the fourth character and subtract 0x20 from it and multiply it with 3 [rax+rax*2] and then it will compare it with 0xC3. Optimizer can do some pretty cool stuff sometimes.

After analyzing the whole function and its sub-functions, we get the following decoder script:

# 0x14000190A
key = "PAN{"
key += (hex(0x366C734A ^ 0x1A1B1C1D)[2:]).decode('hex')[::-1]
key += chr((0xB0 ^ 0xF0) &gt;&gt; 1)
key += chr((0x56 ^0x20) - 4)
key += (hex((0x8888999900001110 &amp; 0xFFFFFFFFFFFFFFF0) ^ 0x0A9FAFCEA72656775))[2:-
1].decode('hex')[::-1]
# 0x140001AFC
key += chr(0x20)
key += chr(0x67 - 0x20)
key += chr(0x72)
key += chr(0xfc ^ 0x99)
key += chr((0xc3 / 3) + 0x20)
key += chr(0x3b ^ 0x4f)
key += chr(0x20)
key += chr((0x71 - 0x0f) ^ 0x0f)
key += chr((0x81 ^ 0x21) - 0x31)
key += chr(0x46 + 0x30)
key += chr((1 ^ 0x58) + 0x0c)
key += chr(0x73)
# 0x140001BD4
key += chr(0x2c)
key += chr(0x20)
key += chr(0x1ac0 &gt;&gt; 6)
key += chr(0x328 / 8)
key += chr((0xe002 ^ 0xf2f2) / 0x30)
key += chr((0x9542 ^ 0x8942) &gt;&gt; 6)
key += chr(0x1f + 1)
key += chr(0x834 * 4 / 0x50)
key += chr((0x94dd ^ 0x83c1) / 0x33)
key += chr(0x1f + 1)
key += chr((0x1dd36c ^ 0x46) / 0x4142)
key += chr(0x328250 / 0x7373)
# 0x140001CD8
key += chr((0xb6 ^ 0x32) / 3)
key += chr(0x40 - 0x20)
key += chr((0xd7 ^ 0x44) - 0x23)
key += chr((0xb2 ^ 0x21) - 0x21)
key += chr(0x1bc / 4)
key += chr((0x8f25 ^ 0x8875) &gt;&gt; 4)
key += chr(((0x0d6 ^ 0x0e4) / 2) &lt;&lt; 2)
key += chr((0x62 ^ 0x42) &amp; 0xfe)
# 0x140001DB8
key += chr((0x70d49 ^ 0x48354) / 0x833)
key += chr((0x14c3a1 ^ 0x232221) / 0x8c40)
key += chr(0xA0 ^ 0x80)
# 0x140001E2C
key += chr(0x5c ^ 0x25)
key += chr((0x20f63 ^ 0x23) / 0x4c0)
key += chr((0x242067 ^ 0x12567) / 0x5100)
key += chr(((0x61 ^ 0x21) &gt;&gt; 6) + 0x20)
key += '}'

print key
python decoder.py

PAN{Wow, reverser! Great moves, keep it up, proud of you!}

Testing the flag

Now we’ll test the flag to confirm if everything is correct. We have to do following steps:

  • Create a file named “gsrt.txt” with content “labyrenth.com”

  • Open “gsrt.txt” to raise a FILE_OPEN event to initialize the buffer

    • We can also create a mailsot event instead of these two steps (we can use CreateMailslot + WriteFile)
  • Create a file named “pan.flag” with our flag “PAN{…}”

  • Open “pan.flag” to raise a FILE_OPEN event to call the analyzing function

  • Attach with DbgView to see the kernel debug output

The solution with creating a file is easier because we can make a batch file to do the job for us:

Solution.bat
echo PAN{Wow, reverser! Great moves, keep it up, proud of you!} &gt; c:\pan.flag
echo labyrenth.com&gt; c:\gsrt.txt
start notepad c:\gsrt.txt
start notepad c:\pan.flag

We run revloader.exe and run our solution batch file and see what happens:

And we can see in the last screenshot that the flag is correct and it gets printed out.