
Full-Text Disassembler DLL Export Module for Kernel Mode.


I use the source code of NDISASM, the Netwide Disassembler portion of NASM, compiled into a user mode DLL, for use in various reversing projects that incorporate a disassembler component.  Recently I decided to recompile the code into a *kernel mode* DLL, to see what use might be made of it in a driver context.  The result may be of interest to some, perhaps as a self contained full-text disassembly module for testing or development (i.e. "playing"), or as an example of creating and using kernel mode export drivers.

It would seem that for most rootkit applications a full-text disassembler is overkill, anything you might want to test for in text mode can be tested on an opcode byte basis (i.e "far jmp" vs 0xEA).  For hooking and many other uses the critical requirement is in determining where instruction opcode boundaries occur.  A simple length disassembly engine such as Z0mbie's LDE/XDE or Microsoft's Detours disasm engine would likely suffice. Mind you I've never seen the MS source used outside of Detours itself...

The LDE/XDE from Z0mbie is often used, however it doesn't recognize certain opcode combinations, the LOCK prefix being one of them.  I'm not sure if the ADE32 engine is any different.  Since LOCK is so prevalent in the kernel, this partial disassembler engine may be unsuitable to use for certain types of kernel code analysis.  

As a matter of reference, there is an article here that mentions the opcode limitations of the XDE engine, and a fix for the issue of the LOCK (0xF0) prefix:

User-mode program tracing and an application to encrypted execution engine
by Zeljko Vrba
http://www.core-dump.com.hr/documents/cryptexec.pdf
http://www.core-dump.com.hr/software/cryptexec.tar.gz


The full-text disassembly module, SysDasm.sys, is created with a single export, which acts as a wrapper around the NDISASM internal disasm routine.  This export-only driver is loaded from another driver, either by linking to it explicitly, or by loading it with ZwSetSystemInformation using the SystemLoadImage class.  

In this type of export module, the DriverEntry routine is never called but exists so the file is compiled correctly as a .sys driver. If you want to design such a Kernel Mode DLL with functional entry/exit routines, you can add PRIVATE exports declared as DllInitialize/DllUnload. For more on this see for example
DLLs in Kernel Mode by Tim Roberts
http://www.wd-3.com/archive/KernelDlls.htm


The easiest way to use such a kernel mode DLL is to include its .LIB file when compiling the driver which will communicate with it, and to declare the functions you want to import with EXTERN_C DECLSPEC_IMPORT. When the driver is loaded by the system, this second module is loaded as a required kernel DLL and the functions can then be called directly by name. The DLL is unloaded by the system when the driver closes.

Here is the declaration for including the SysDasm.sys export:

[code]
EXTERN_C DECLSPEC_IMPORT int
Disasm(IN unsigned char * inBuffer,     // opcode bytes to decode
	  OUT unsigned char * outBuffer,   // return disassembly text
	  IN long Address);                // instruction address
// (returns opcode length)
[/code]


The second method to make use of a kernel mode DLL is to load and unload it with ZwSetSystemInformation and the SystemLoadImage and SystemUnloadImage classes. You can then "walk" the returned IMAGE_EXPORT_DIRECTORY of the module to retrieve the function address(es).  

I'll document here the updated structure definitions of SYSTEM_LOAD_IMAGE and SYSTEM_UNLOAD_IMAGE. Originally described by Gary Nebbet in his classic Windows NT Native API document, they were later corrected by him to the following definition.

[code]
	"In the SYSTEM_LOAD_IMAGE structure, the member named "Unknown" is in fact
	a pointer to the same type of data that is pointed to by the DriverSection
	member of a DRIVER_OBJECT structure, and this member should appear in the
	SYSTEM_UNLOAD_IMAGE structure."
	See Newsgroup: comp.os.ms-windows.programmer.nt.kernel-mode (2000/04/06)

typedef struct _SYSTEM_LOAD_IMAGE {			// Information Class 26
    IN UNICODE_STRING ModuleName;
    OUT PVOID ModuleBase;
    OUT PVOID ModuleSection;
    OUT PVOID EntryPoint;
    OUT PVOID ExportDirectory;
} SYSTEM_LOAD_IMAGE, *PSYSTEM_LOAD_IMAGE;

typedef struct _SYSTEM_UNLOAD_IMAGE {		// Information Class 27
    IN PVOID ModuleSection;
} SYSTEM_UNLOAD_IMAGE, *PSYSTEM_UNLOAD_IMAGE;
[/code]

Loading and unloading of the module can be accomplished with:

[code]
----------------------------------------------
SYSTEM_LOAD_IMAGE		LoadImage;

	//const WCHAR DriverName[] = L"\\??\\C:\\SysDasm.sys";
    RtlInitUnicodeString(&LoadImage.ModuleName, driver_name);

	NTSTATUS Status;
	Status = ZwSetSystemInformation(
	  SystemLoadImage,
	  &LoadImage,
	  sizeof(LoadImage));

	if (!NT_SUCCESS(Status))	{
		DbgPrint("SystemLoadImage failed with Error %x\n", Status);
		return 0; 
	}
----------------------------------------------

SYSTEM_UNLOAD_IMAGE     UnloadImage;

	// Set ModuleSection value to that returned in LoadImage.ModuleSection
	UnloadImage.ModuleSection = LoadImage.ModuleSection;

	NTSTATUS Status;
	Status = ZwSetSystemInformation(
	  SystemUnloadImage,
	  &UnloadImage,
	  sizeof(UnloadImage));
----------------------------------------------
[/code]	  

	  
As an example of using the SysDasm module, I have created a console application which will disassemble code in one of two ways:

1. By NT Function Name - any kernel or HAL export resolved by MmGetSystemRoutineAddress.
2. By Address - any valid user or kernel mode instruction address.

Disassembly will stop at the number of instructions specified (optional), or until a RET is reached.  The complete disassembly data is returned to user mode via a shared MDL buffer.  To demonstrate the 2 methods of loading the disassembler module (LINK vs LOAD), I created 2 separate exe/driver packages which are identical except for the details of loading and calling the export function.  

To put the whole thing to some use, I put in a FAR JMP (0xEA) detection which will follow the jump and return the disassembly of the diverted code. This being inspired by Greg Hoglund's nice MIGBOT.sys example of hooking NT functions with a FAR JMP detour, thanks... :D
See:
KCode Patching
https://www.rootkit.com/newsread.php?newsid=152


Here is an example output after running MIGBOT, which hooks the beginning of the function NtDeviceIoControlFile. The hook detour code is isolated by the "FAR JMP" markers:
	
[code]
C:\SysDasm\ >kdasm df NtDeviceIoControlFile

 -------------------------------------------------------
         Disassembly of NtDeviceIoControlFile
 -------------------------------------------------------

#   Address (Len)        Opcode                   Disassembly

1   804A9678  7  eae8de3a810800                   jmp 8:813adee8
 ---------------------
 FAR JMP disassembly

2   813ADEE8  1  55                               push ebp
3   813ADEE9  2  8bec                             mov ebp,esp
4   813ADEEB  2  6a01                             push byte +1
5   813ADEED  3  ff752c                           push dword [ebp+2c]
6   813ADEF0  7  ea80964a800800                   jmp 8:804a9680
 ---------------------
 FAR JMP disassembly

7   804A9680  3  ff7528                           push dword [ebp+28]
8   804A9683  3  ff7524                           push dword [ebp+24]
9   804A9686  3  ff7520                           push dword [ebp+20]
10  804A9689  3  ff751c                           push dword [ebp+1c]
11  804A968C  3  ff7518                           push dword [ebp+18]
12  804A968F  3  ff7514                           push dword [ebp+14]
13  804A9692  3  ff7510                           push dword [ebp+10]
14  804A9695  3  ff750c                           push dword [ebp+c]
15  804A9698  3  ff7508                           push dword [ebp+8]
16  804A969B  5  e86e7b0000                       call 804b120e
17  804A96A0  1  5d                               pop ebp
18  804A96A1  3  c22800                           ret 28
[/code]

You can of course recompile the NDISASM files as a regular user mode DLL as well. They were slightly modified from the original 0.98.38 version for output syntax and to remove unneeded functionality (sync function).  At some point I'd like to try to incorporate Symbol information in the output, but for now it provides an effective and complete disassembly engine for whatever uses that might suggest. Comments are welcomed.

The complete VC++ source for the SysDasm Disassembler DLL module and the 2 driver/exe examples are here. 

http://woodmann.net/kayaker/

Regards,
Kayaker
