Next, we run the program and find that it creates a file and a service. Using procmon, we see that the program creates a file in C:\Windows\System32, and that it creates a service that uses that file as the executable. That file contains the kernel code that will be loaded by the OS.
We should next find the file that the program creates in order to analyze it and determine what the kernel code is doing. However, when we look in .
NtQueryDirectoryFile
. However, if we single-step over the call
function, we see that it goes to another section of the file that jumps to NtQueryDirectoryFile
. In IDA Pro, this call would have been labeled NtQueryDirectoryFile
, but the disassembler included in WinDbg is much less sophisticated. Ideally, we would have the file to view in IDA Pro while we are debugging, but we can’t find this file because it’s hidden.The PatchFunction
checks the eighth parameter, FileInformationClass
, and if it is any value other than 3, it returns NtQueryDirectoryFile
’s original return value. It also checks the return value from NtQueryDirectoryFile
and the value of the ninth parameter, ReturnSingleEntry
. PatchFunction
is looking for certain parameters. If the parameters don’t meet the criteria, then the functionality is exactly the same as the original NtQueryDirectoryFile
. If the parameters do meet the criteria, PatchFunction
will change the return value, which is what we’re interested in. To examine what happens during a call to PatchFunction
with the correct parameters, we set a breakpoint on PatchFunction
.
If we set a breakpoint on PatchFunction
, it will break every time the function is called, but we’re interested in only some of the function calls. This is the perfect time to use a conditional breakpoint so that the breakpoint is hit only when the parameters to PatchFunction
match our criteria. We set a breakpoint on PatchFunction
, but the breakpoint will be hit only if the value of ReturnSingleEntry
is 0, as follows:
RtlCompareMemory
at ❶.RtlCompareMemory
is eax
, which stores the offset at esi+5eh
at ❷, which is the offset to a filename. Earlier in our disassembly, we saw that esi
was FileInformation
, which contains the information filled in by NtQueryDirectoryFile
. Examining the documentation for NtQueryDirectoryFile
, we see that this is a FILE_BOTH_DIR_INFORMATION
structure, and that an offset of 0x5E is where the filename is stored as a wide character string. (We could also use WinDbg to tell us what is stored there.)To see what is stored at location esi+5eh
, we use the db
command, as shown in . This reveals that the filename is Installer.h.
RtlCompareMemory
stores the letters Mlwx, which reminds us of the driver Mlwx486.sys.PatchFunction
manipulates this field to hide certain files from the directory listing by moving the shows what the return value of NtQueryDirectoryFile
looks like for a directory that contains three files. There is one FILE_BOTH_DIR_INFORMATION
structure for each file. Normally, the first structure would point to the second, and the second would point to the third, but the rootkit has modified the structure so that the first structure points to the third, thereby hiding the middle structure. This trick ensures that any files that begin with Mlwx are skipped and hidden from directory listings.Having identified the program that is hiding files, we can try to obtain the original file used by the driver in order to perform additional analysis. There are several ways to do this:
Disable the service that starts the driver and reboot. When you reboot, the code won’t be running and the file won’t be hidden.
Extract the file from the resource section of the executable file that installed it.
Access the file even though it’s not available in the directory listing. The hook to NtQueryDirectoryFile
prevents the file from being shown in a directory listing, but the file still exists. For example, you could copy the file using the DOS command copy Mlwx486.sys NewFilename.sys
. The NewFilename.sys file would not be hidden.
All of these options are simple enough, but the first is the best because it disables the driver. With the driver disabled, you should first search your system for files beginning with Mlwx in case there are other files being hidden by the Mlwx486.sys driver. (There are none in this case.)
Opening Mlwx486.sys in IDA Pro, we see that it is very small, so we should analyze all of it to make sure that the driver isn’t doing anything else that we’re not aware of. We see that the DriverEntry
routine calls RtlInitUnicodeString
with KeServiceDescriptorTable
and NtQueryDirectoryFile
, and then calls MmGetSystemRoutineAddress
to find the offsets for those two addresses. It next looks for the entry in the SSDT for NtQueryDirectoryFile
and overwrites that entry with the address of the PatchFunction
. It doesn’t create a device, and it doesn’t add any function handlers to the driver object.