{"id":18967370,"url":"https://github.com/whokilleddb/helloworlddriver","last_synced_at":"2025-07-03T13:35:00.389Z","repository":{"id":204428421,"uuid":"711795073","full_name":"whokilleddb/HelloWorldDriver","owner":"whokilleddb","description":"Get started with writing your first ever Windows Driver","archived":false,"fork":false,"pushed_at":"2023-11-04T08:08:41.000Z","size":771,"stargazers_count":32,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-16T08:10:50.257Z","etag":null,"topics":["driver-development","kernel","kernel-driver","windows"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/whokilleddb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-10-30T07:37:04.000Z","updated_at":"2025-04-10T08:13:29.000Z","dependencies_parsed_at":"2023-11-04T09:21:47.979Z","dependency_job_id":null,"html_url":"https://github.com/whokilleddb/HelloWorldDriver","commit_stats":null,"previous_names":["whokilleddb/helloworlddriver"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whokilleddb%2FHelloWorldDriver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whokilleddb%2FHelloWorldDriver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whokilleddb%2FHelloWorldDriver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whokilleddb%2FHelloWorldDriver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/whokilleddb","download_url":"https://codeload.github.com/whokilleddb/HelloWorldDriver/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249716874,"owners_count":21315068,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["driver-development","kernel","kernel-driver","windows"],"created_at":"2024-11-08T14:42:04.702Z","updated_at":"2025-04-19T14:44:37.592Z","avatar_url":"https://github.com/whokilleddb.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HelloWorldDriver\n\nThis repository details how to start writing your first-ever Windows Driver. The main motivation for this has been to overcome the dread of venturing into the Kernel space and gradually learn Windows Kernel Exploitation techniques. \n\n## Prerequisite\n\nFor building and testing the Driver we need to have a couple of things:\n\n-  Two Windows Machines: One to develop the driver on, and another to test it on (this better be a Virtual Machine)\n- On the Development machine - follow the steps listed [here](https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) to have the right SDK and WDK.\n- Have the `DebugView` tool from SysInternals on the Test Machine\n- On the test machine, turn off Integrity checks and turn on test signing. This can be done by running the following commands from an Admin prompt and then rebooting the system:\n```\nbcdedit.exe -set loadoptions DISABLE_INTEGRITY_CHECKS\nbcdedit.exe -set TESTSIGNING ON\n```\n\n\u003e If this command results in \"The value is protected by Secure Boot policy and cannot be modified or deleted\", then reboot the PC, go into BIOS settings, and disable Secure Boot. BitLocker may also affect your ability to modify this setting. ([link](https://learn.microsoft.com/en-us/windows-hardware/drivers/install/the-testsigning-boot-configuration-option))\n\u003e\n\u003e bcdedit.exe -set TESTSIGNING ON\n\n## Creating A WDM Project\n\nThe first thing we need to do is to start a WDM project in Visual Studio:\n- Open Visual Studio and Create a new Project\n- Search the \"Empty WDM Driver\" template and select it\n\n    ![Alt text](\u003cimgs/WDM template.png\u003e)\n\n- Give your project the name you want and choose the location where you want to create it. For this example, I chose the name `HelloWorldDriver`\n- You should have your template ready! \n\n    \u003e Note that under `Driver Files`, you will find a file called `HelloWorldDriver.inf`, which can be used, with some tweaking, to install the resultant driver, but since we won't be installing the driver using it, feel free to get rid of it. \n\n- Finally, under the `Source Files`, add a `HelloWorld.c`file to store the Driver code.\n\n    \u003e Also, for this project, I would recommend setting Warning Levels to `Wall` instead of `W4` by going to Project Properties-\u003eC/C++-\u003eGeneral-\u003eWarning Level\n\n## Code Walkthrough\n\nThe code for the driver looks as follows:\n\n```c\n#include \u003cntddk.h\u003e\n#define DRIVER_TAG 'bdwh'\n\nUNICODE_STRING g_RegPath;\n\nvoid UnloadMe(PDRIVER_OBJECT);\n\nNTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {\n    DbgPrint(\"HelloWorld from the Kernel Land!\\n\");\n    DbgPrint(\"Driver Object:\\t\\t0x%p\\n\", DriverObject);\n    DbgPrint(\"Registry Path:\\t\\t0x%p\\n\", RegistryPath);\n\n    // Allocate memory for variable\n    g_RegPath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, RegistryPath-\u003eLength, DRIVER_TAG);\n    if (g_RegPath.Buffer == NULL) {\n        DbgPrint(\"Error allocating memory!\\n\");\n        return STATUS_NO_MEMORY;\n    }\n\n    // Copy Registry Path\n    memcpy(g_RegPath.Buffer, RegistryPath-\u003eBuffer,RegistryPath-\u003eLength);\n    g_RegPath.Length = g_RegPath.MaximumLength = RegistryPath-\u003eLength;\n    DbgPrint(\"Parameter Key copy: %wZ\\n\", g_RegPath);\n\n    // Unload Function\n    DriverObject-\u003eDriverUnload = UnloadMe;\n    return STATUS_SUCCESS;\n}\n\nvoid UnloadMe(PDRIVER_OBJECT DriverObject) {\n    UNREFERENCED_PARAMETER(DriverObject);\n    if (g_RegPath.Buffer != NULL) {\n        ExFreePool(g_RegPath.Buffer);\n    }\n    DbgPrint(\"Bye Bye from HelloWorld Driver\\n\");\n}\n```\n\nThe PoC code outlines some basic concepts of Driver deployment including Memory Allocation, programming disciplines, etc. \n\nStart off with the _main_ function: `DriverEntry()`:\n\n```c\nNTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {\n    DbgPrint(\"HelloWorld from the Kernel Land!\\n\");\n    DbgPrint(\"Driver Object:\\t\\t0x%p\\n\", DriverObject);\n    DbgPrint(\"Registry Path:\\t\\t0x%p\\n\", RegistryPath);\n\n    // Allocate memory for variable\n    g_RegPath.Buffer = (PWSTR)ExAllocatePool2(POOL_FLAG_PAGED, RegistryPath-\u003eLength, DRIVER_TAG);\n    if (g_RegPath.Buffer == NULL) {\n        DbgPrint(\"Error allocating memory!\\n\");\n        return STATUS_NO_MEMORY;\n    }\n\n    // Copy Registry Path\n    memcpy(g_RegPath.Buffer, RegistryPath-\u003eBuffer,RegistryPath-\u003eLength);\n    g_RegPath.Length = g_RegPath.MaximumLength = RegistryPath-\u003eLength;\n    DbgPrint(\"Parameter Key copy: %wZ\\n\", g_RegPath);\n\n    // Unload Function\n    DriverObject-\u003eDriverUnload = UnloadMe;\n    return STATUS_SUCCESS;\n}\n```\n\nBefore we begin, there is one important thing to note:\n- Usually when we write Userland code, the standard include files are: `Windows.h`, `stdio.h`, etc.\n- Instead, for Drivers we include `ntddk.h` which defines the functions which we would be using in this PoC.\n\nMoving onto the `DriverEntry()` function - it is analogous to the `main()` function and serves as an entry point to the driver. It is responsible for initializing the driver. The entry point function has a strict function definition: \n\n- It must return a `NTSTATUS` to indicate if the function succeeded or failed, and in case of the latter, also provide some information about the cause of the failure. \n- The function name: `DriverEntry`. If you are writing a driver in C++, make sure that the function name is not mangled.\n- The function parameters:\n    - `PDRIVER_OBJECT DriverObject`: A pointer to a `DRIVER_OBJECT` structure that represents the driver's WDM driver object. Detailing the individual members of the structure is beyond the scope of this blog, but we will discuss some of them as we come across them.\n    - `PUNICODE_STRING RegistryPath`: A pointer to a UNICODE_STRING structure that specifies the path to the driver's Parameters key in the registry. The driver's Parameters key can contain configuration information for your driver.\n\nMoving onto the main function body, we are first greeted with a bunch of `DbgPrint()` functions which look very similar to our good friend `printf()`, and to be honest, it mostly works the same except for floating points and some IRQL stuff (Read more [here](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-dbgprint)). Unlike `printf()` which prints to the console, The `DbgPrint()` routine sends a message to the kernel debugger. We use the `DbgPrint()` function to print the address of the function parameters. \n\nNext up, we move to the memory allocation part of the driver. In the driver code, we have a global variable `g_RegPath` of the type `UNICODE_STRING` where we would store a copy of the Parameter key aka the `RegistryPath` so that we can share it among other functions. This is similar to the times in User-mode programming when we allocate memory on the heap to share structures across functions. Whereas in user mode, we use good ol' `malloc()`, in DriverLand we have the `ExAllocatePool2()` function.\n\nWe need to allocate memory for the `Buffer` part of the global variable which is large enough to hold the corresponding buffer of the `RegistryPath` variable. The [`ExAllocatePool2()`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-exallocatepool2) function allocates pool memory. The parameters of the function are as follows:\n- `POOL_FLAG_PAGED` - The [`POOL_FLAG`](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/pool_flags) which tells the function that we want the memory to be in a Paged Pool as we don't want it to live in the physical memory.\n- `RegistryPath-\u003eLength` - Number of bytes to copy \n- `DRIVER_TAG` - The pool tag, aka a non-zero character literal of one to four characters delimited by single quotation marks (for example, `hwdb`), that is associated with a dynamically allocated chunk of pool memory and can be used to identify the source of Pool Memory leaks. It is something that has trickled down from the now-deprecated `ExAllocatePoolWithTag()` function. The string is usually specified in reverse order (for example, `bdwh`) - aka Little Endian. Each ASCII character in the tag must be a value in the range 0x20 (space) to 0x7E (tilde). Each allocation code path should use a unique pool tag to help debuggers and verifiers identify the code path.\n\nAnd then, just like we do with `malloc()`, we check if the function failed, in which case it returns `NULL`, and if it did, we return `STATUS_NO_MEMORY`.\n\nIf the function succeeds, we fill out the `Length` and `MaximumLength` fields with the number of bytes in the buffer, use `memcpy()` to copy over the bytes to the allocated memory space, and use `DbgPrint()` with the `%wZ` format specifier to print out the copied string.\n\nThere is one final step before we exit: specifying an unload routine. Remember the `DriverObject` variable? It has a member called `Unload` which takes the address to a function that would be called when the driver is being unloaded. This allows us to perform some cleanup operations to prevent BSODs and deallocate any dynamic memory. Finally, we exit with `STATUS_SUCCESS`.\n\nHowever, this is not the end. We still have the unload routine. The unloading routine for our driver looks like this:\n\n```c\nvoid UnloadMe(PDRIVER_OBJECT DriverObject) {\n    UNREFERENCED_PARAMETER(DriverObject);\n    if (g_RegPath.Buffer != NULL) {\n        ExFreePool(g_RegPath.Buffer);\n    }\n    DbgPrint(\"Bye Bye from HelloWorld Driver\\n\");\n}\n```\n\nThe function definition for unload routines is also very strict. While the function name can vary, the rest of the function header should remain the same. Unload routines return nothing (understandably) and they only take in a pointer to the driver object.\n\nSince we do not use the pointer to the `DriverObject`, we use the `UNREFERENCED_PARAMETER` macro to get rid of the `C4100` warning, aka the Unused Variable warning. Next up, we check if the buffer we allocated is `NULL` or not, if not, we go ahead and free the memory using `ExFreePool()`, just like we do with `free()`. I would like to add one note here - try to be as paranoid as you can while writing Drivers. For example, even if we skip the `NULL` checks here, we do not even get any warning, but we still do because the memory might have been freed somewhere else (especially in larger code bases), so it is good to put checks in place to prevent a Double-Free like situation and cause a BSOD. \n\n## Compiling \n\nWe can easily compile the code using Visual Studio build options. Sticking to debug builds for now, we should have two files at the end of the build: \n-  HelloWorldDriver.pdb: The Debug symbols for the driver\n-  HelloWorldDriver.sys: The actual driver\n\nCopy these over to the target machine where we would be testing the driver and follow along. \n\n## Installing Driver\n\nTo install custom drivers, there are special functions for that. See [CmRegisterCallback()](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-cmregistercallback?redirectedfrom=MSDN), [CmRegisterCallbackEx()](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-cmregistercallbackex?redirectedfrom=MSDN) and [Filtering Registry Calls](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/filtering-registry-calls?redirectedfrom=MSDN) on MSDN.\n\nHowever, we can use the `Service Controller` binary(`sc.exe`) to do just that. Firstly, we need to create a Kernel-mode service with:\n\n```\nsc create HelloWorldDriver type= kernel binPath= C:\\MyDrivers\\HelloWorldDriver.sys\n```\n\n\u003e Note: The spaces after `type= ` and `binPath= ` are not a typing error, they MUST be there if the command is to run successfully.\n\nThe above command creates a kernel service with the name `HelloWorldDriver` which loads the driver located at `C:\\MyDrivers\\HelloWorldDriver.sys`.\n\nNow, load up `DebugView` with Admin privileges with \nand under the `Capture` tab, have the shown options selected. \n\n![](./imgs/DebugView.png)\n\nOnce the event capture is on, we can start the driver with:\n```\nsc start HelloWorldDriver\n```\n\nThis should start our driver and we should see the `DbgPrint()` statements pop up in the `DebugView` Window.\n\n![](./imgs/sc_start.png)\n\nNoticing the output in the `DebugView` we see a path to a registry key. Opening the path in the registry editor, we are greeted with this:\n\n![](./imgs/registry.PNG)\n\nThese registry options can be used to control the behavior of the Driver. \n\nFinally, we can unload the driver with:\n```\nsc stop HelloWorldDriver\n```\n\n![](./imgs/sc_stop.png)\n\nAs soon as we unload the driver, we again get to see the message from the Unload routine in the `DebugView()` window. \n\nThus, we finally loaded and unloaded our first Windows Driver yay!\n\n## Conclusion\n\nI know this blog/repo is like the literal `Hello World!` of Driver programming and there is still a lot to cover - including setting up Debuggers, advanced drivers, etc. In this blog post, I have tried to provide a very gentle introduction to Windows Driver Development and I will be following up with more posts on related topics in the future. Till next time, see you space cowboy. \n\n![](https://i.pinimg.com/originals/43/9d/e3/439de394ba9fe35b3319ad86cc97f311.gif)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhokilleddb%2Fhelloworlddriver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwhokilleddb%2Fhelloworlddriver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhokilleddb%2Fhelloworlddriver/lists"}