Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Driver Loader [DLoad] from Scratch

5.00/5 (30 votes)
2 Nov 2009CPOL7 min read 78.2K   7.9K  
A tool for loading device drivers
Project Poster Project Poster Project Poster

Introduction

Well, I cannot say that this is a regular article. It is rather a small manual and presentation of my tool: Driver Loader, "DLoad". One may say - there are a lot of device driver loaders on the net, but when I typed in Google search engine: "Driver Loader" - I got only one provided by OSR group on the first page. One can say, search deeper and there are a lot of console utils for this, I will answer: I have finished my school long time ago and now I am an old guy who doesn't have 10 hours of free time daily to waste on searching for such a simple util - I need it in a second, right away; and I don't like messing with console tools, what age we got? I want some nice GUI, intuitive and straightforward user interface (in future I will probably make my Driver Loader voice controlled). So why did I code this one? Because OSRLoader for some reasons doesn't meet my needs? Because I simply don't want to traverse the whole of Google just to find one? Anyways, this version here is the 3rd version of DLoad, in the package you will also find version 2, which is written in C++ Gtk+, while version 3 is in C# .NET. So, let's see what we have here. While writing this introduction, let me mention 2 things:

[1]. The code is organized in such a way that you can easily take some parts of it and adopt in your own app, rip it apart, mix and then put them back together.
[2]. What actually 3rd version can do:

  • Load driver with ZwSetSystemInformation
  • Load driver with NtLoadDriver
  • Load driver with Service Control Manager
  • Unload driver
  • Delete driver file
  • Delete driver registry entries
  • Usage of Thread Injection technique
  • Injection with RtlCreateUserThread
  • Injection with CreateRemoteThread
  • Injection with NtCreateThreadEx
  • LOAD Mode
  • UNLOAD Mode
  • Reboot System
  • Shutdown System
  • Any combination of the above functions

Ok, here we go. I am not going to explain DLoad v.2 code because it is provided: "just like this", "as an alternative", "maybe you like Gtk?", "whatever...". Besides it is outdated. Let's proceed.

Into the Code

I will explain things in their appearance order. Have a look at the GUI once again. There is no need for "Select Driver" button explanation I think... Ok.

  • ZwSetSystemInformation
  • NtLoadDriver
  • Service Control Manager

With these methods, you can load driver. ZwSetSystemInformation is pretty undocumented but has been used for a long long time. In fact, it is not the best method - it is the worst, but still it is. The worst because your driver gets into paged pool, you will not be able to unload it, nor will you be able to delete driver file until system reboot. Ok, how do we call this from C# code. The first thing is to have all native elements declared - for this "Native" class has been created, it looks like this:

C#
public class Native {
.......
        public const int NtCurrentProcess = -1;
        public const int NtCurrentThread = -2;
        public const long NT_SUCCESS = 0x00000000L;
        public const int STATUS_SUCCESS = 0;
		........
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct CLIENT_ID
        {
            int UniqueProcess;
            int UniqueThread;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct UNICODE_STRING
        {
            public ushort Length;
            public ushort MaximumLength;
            public string Buffer;
        }
		...............
        [DllImport("ntdll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        unsafe public static extern int ZwSetSystemInformation(
            int Value1,
            IntPtr Value2,
            int Value3
            );

I have included there elements needed by DLoad, you can in a very simple way extend the class for your needs, and create something like this famous "ntdll.h" on the net. Next, we have our "DLoad" namespace and "DriverLoader" class, which contains our methods. Like the previous class, you can use this one in your projects easily. Let's take a look at the function.

C++
namespace DLoad {

    public class DriverLoader {

        public static long ZwStyleLoader(
            String DriverPath
            )
        { // Load driver with 'ZwSetSystemInformation'
            bool en;
            int Status; // status
            String FullDriverPath = "\\??\\" + DriverPath + '\0'; // driver path
            Native.SYSTEM_LOAD_AND_CALL_IMAGE img; // structure initialization

            Status = Native.RtlInitUnicodeString( // initialization of unicode string
                out (img.ModuleName), // pointer to UNICODE_STRING
                FullDriverPath // PCWSTR
                );
            if (Status != Native.STATUS_SUCCESS) 
		return Native.STATUS_INITUNISTRING_FAILURE; 	// unicode string 
							// is not initialized

            Status = Native.RtlAdjustPrivilege( // setting privileges
                10, // int
                true, // BOOL
                Native.ADJUST_PRIVILEGE_TYPE.AdjustCurrentProcess, // BOOL
                out en //BOOL *
                );
            if (Status != Native.STATUS_SUCCESS) 
		return Native.STATUS_PRIVILEGES_NOT_SET; // privileges not sat

            IntPtr buffer = 
		Marshal.AllocCoTaskMem(Marshal.SizeOf(img)); // this will point to 
						// our structure like 'PVOID'
            Marshal.StructureToPtr(img, buffer, false); // structure to pointer
            // ^ that is the working example how PVOID stuff is converted to C# style
            // we are passing a pointer to structure to IntPtr which is actually a handle
            // previously allocating some memory, then we passing 'buffer' as IN parameter

                Status = Native.ZwSetSystemInformation(
                    Native.SystemLoadAndCallImage, // dword
                    buffer, // pvoid
                    Marshal.SizeOf(img) // ulong
                    );

            return Status; // just get status code to know what to do next
        }

/***************************************************************************************/

The coolest thing in this small piece of code is that, man, it is C#! C# for me is a more higher level than interpreted languages, like Perl for example, it is almost like real human language already. Just compare:

C++
static inline char * x_strcpy(char *dest, char *src)
{
	int	d1, d2, d3;
	__asm__ __volatile__ (
		"1:\tlodsb\n\t"
		"stosb\n\t"
		"testb %%al, %%al\n\t"
		"jne 1b\n\t"
		: "=&a" (d1), "=&S" (d2), "=&D" (d3)
		: "1" ((ulong) src), "2" ((ulong) dest));
	return dest;
}

and

C#
String.Copy

And, still here we got access to Native Windows API! :) Well, otherwise I would never code a thing in C# ;) Back to the subject. This is how it's done. In case of NtLoadDriver and Service control manager too, 3 methods you have in one single class. Now we call it:

C#
Status = DriverLoader.ZwStyleLoader(FileNamePath);
// or...
Status = DriverLoader.NtStyleLoader
	(FileNamePath, UnloadDriver, DeleteDriver, DeleteRegEntry, UnloadMode);
// or ..
DriverLoader.ScmStyleLoader
	(FileNamePath, UnloadDriver, DeleteDriver, DeleteRegEntry, UnloadMode);

Next thing: Exit Action.

  • Unload driver
  • Delete driver file
  • Delete driver registry entries

So, this is a standard action which will be performed after loading driver routine. When finally DriverEntry has been called, what do we do? For example, if your driver just prints "Hello World from driver!" and returns, you simply want it to be unloaded just after execution. So in such a case, you check 'Unload driver' and 'Delete driver registry entries'. If you want driver file to be deleted, you check 'Delete driver file'. And so on. But, if you are testing a more advanced driver, server for example, or driver working with IOCTLs, you don't want it to be unloaded just after execution, in such a case you need to uncheck every Exit Action options.

The next thing is the 'Injection' option - if you will check it, DLoad will expand presenting a new set of options:

  • Injection with RtlCreateUserThread
  • Injection with CreateRemoteThread
  • Injection with NtCreateThreadEx

Well, the first thing, injection function is implemented in a separate DLL which is built in DLoad itself. If you check 'use injection' DLoad will unpack this DLL into windows/ folder and import function from it. Here is a prototype:

C++
DWORD LoadDriverWithInjection(
				int ProcID,
				char *DriverPath,
				int LoadMode,
				BOOL UnloadDriverMode,
				BOOL DeleteDriverMode,
				BOOL DelDrvRegMode,
				int Mode,
				char *name,
				bool un_load
);

So if you will ever need such functionality - you got my DLL already. :)
You may ask, what for? Injection I mean? Well, actually I don't know for sure myself :P It is just here. Anyway, show me another driver loader which uses injection method for loading drivers? Ha! There is no such! And mine is significant by this. Ok, about functions.

[1]. RtlCreateUserThread - Should work on any windows against any type of process (Notepad, svchost, etc.)
[2]. CreateRemoteThread - I don't know what more to say about this one than has already been said
[3]. NtCreateThreadEx - This is the latest Windows specific function (Vista, Server 2008, etc.)

Usage from main app:

C++
[DllImport("DLoadDLL.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int LoadDriverWithInjection(
                      uint TargProc,
                      byte[] DriverPath,
                      int LoadMode,
                      Boolean UnloadDriverMode,
                      Boolean DeleteDriverMode,
                      Boolean DelDrvRegMode,
                      int Mode,
                      byte[] DriverName,
                      Boolean UN_LOAD
    );

And then in our main class, we call it:

C++
String TargetProcess = targ_proc_ent.Text;
uint ProcID = Native.GetPidByName(TargetProcess);
String DriverFile = Path.GetFileName(FileNamePath);
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] Test1 = encoding.GetBytes(DriverFile);
byte[] Test2 = encoding.GetBytes(FileNamePath);
StaTus = Native.LoadDriverWithInjection(
                ProcID,
                Test2,
                Native.NTLOADMODE,
                UnloadDriver,
                DeleteDriver,
                DeleteRegEntry,
                InjectionModeEx,
                Test1,
                UnloadMode
);

The next option, if you have checked 'use injection', is to select target process, by default it is Notepad.
Then we got 2 radio buttons:

  • LOAD Mode
  • UNLOAD Mode

So, default mode is LOAD mode, and DLoad v.2 [Gtk+] has this mode only. C# version has UNLOAD too. UNLOAD mode is responsible for ... unloading driver! :) simple. For example, if you have loaded driver, without quick unload (I was talking about this somewhere above) and now want to unload it, simply select your driver again (select driver file), check 'UNLOAD' and hit 'Execute' button. Driver will be unloaded. Here is one thing, while selecting method for unload, you can't select 'via ZwSetSystemInformation' and, method should be the same as when you were loading your driver, so if your driver was loaded with NtLoadDriver method, it should be unloaded with NtLoadDriver method (I mean method, not function xD). Hope it is clear.

And last 'widgets' we got here: buttons on the very bottom. From left to the right:
Execute, Info, Exit, Reboot, Shutdown.
Reboot and Shutdown buttons are responsible for rebooting or shutting down machine. They are using Native functions:

C++
void button_reboot_Click(object sender, EventArgs e)
        {
            bool en;
            int Status;
            Status = Native.RtlAdjustPrivilege(
                            19,
                            true,
                            Native.ADJUST_PRIVILEGE_TYPE.AdjustCurrentProcess,
                            out en
                            );

            Status = Native.NtShutdownSystem(
                            Native.SHUTDOWN_ACTION.ShutdownReboot
                            );
            if (Status != Native.STATUS_SUCCESS)
            {
                String Debug = String.Format("Failure, Status: {}", Status);
                status_output.Text = Debug;
            }
        } 

What makes them more effective: no wait time, no logout window. They are here because I need them here. :) Personally, I am testing drivers on vmware and sometimes there are situations when you need to reboot the machine. Something went wrong but you cannot unload driver coz it got stuck somewhere, or any other bad thing may have happened, or simply fast reboot was needed (I hate to wait until all this business with logging out is done). Off the subject, here is an interesting thing: There is a function, while you are coding Linux kernel modules, which shuts down the computer, without any logging out, in, probably, less than a second! Don't remember its name right now... KernelShutdown maybe? ;) Well, nothing more to explain. Read the code - it is the best self explanatory thing. Thanks for your attention, cheers.

Project Poster

History

  • 1st November, 2009: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)