Categories > Coding > C++ >

C++ Kernel Mode Driver

Posts: 283

Threads: 48

Joined: May, 2022

Reputation: -4

Posted

This driver is not meant to be used in any real applications as it is not adequate for them ( easily detected ), it's meant to simply be a basic kernel mode driver which reads and writes from/to processes. This was an educational experience for me and hopefully for anyone who reads this and the code.

 

I had sort of forgot about this project, and decided to make a few changes to it and release it publicly. Note that this is my first kernel mode driver and it's meant to be a simple one, with basic features and it does not mitigate any detection vectors, with that being said, here's the github link, and some preview of the code:

#pragma once
#include "includes.h"


namespace major_functions
{
	/// <summary>
	/// IRP_MJ_CREATE Major Function
	/// </summary>
	/// <param name=""></param>
	/// <param name="irp_ptr"></param>
	/// <returns></returns>
	NTSTATUS mj_create( DEVICE_OBJECT*, IRP* irp_ptr )
	{
		util::log( "%s", "MJ_CREATE Major Function called." );

		// denying handle if connection with user-mode process already established
		if( driver_globals::connection_initialized )
		{
			util::log( "%s", "Cannot establish connection with 2 user-mode processes at once." );
			return STATUS_ACCESS_DENIED;
		}
		irp_ptr->IoStatus.Information = 0;
		irp_ptr->IoStatus.Status = STATUS_SUCCESS;

		IofCompleteRequest( irp_ptr, IO_NO_INCREMENT );
		return STATUS_SUCCESS;
	}
	/// <summary>
	/// IRP_MJ_CLOSE Major Function
	/// </summary>
	/// <param name=""></param>
	/// <param name="irp_ptr"></param>
	/// <returns></returns>
	NTSTATUS mj_close( DEVICE_OBJECT*, IRP* irp_ptr )
	{
		util::log( "%s", "MJ_CLOSE Major Function called." );
		// resetting for preparation for a new user-mode process to establish a connection to.
		driver_globals::connection_initialized = false;
		driver_globals::um_process.um_process = nullptr;
		driver_globals::um_process.um_process_id = 0;

		irp_ptr->IoStatus.Information = 0;
		irp_ptr->IoStatus.Status = STATUS_SUCCESS;

		IofCompleteRequest( irp_ptr, IO_NO_INCREMENT );
		return STATUS_SUCCESS;
	}
	/// <summary>
	/// This function is responsible for dispatching and initializing connections with user-mode processes. 
	/// </summary>
	/// <param name=""></param>
	/// <param name="irp_ptr"></param>
	/// <returns></returns>
	NTSTATUS mj_ioctl_dispatcher( DEVICE_OBJECT*, IRP* irp_ptr )
	{
		util::log( "%s", "MJ_IOCTL_HANDLER Major Function called." );

		const auto irp_stack_location = IoGetCurrentIrpStackLocation( irp_ptr );
		const auto ctl_code = irp_stack_location->Parameters.DeviceIoControl.IoControlCode;

		// check for valid control codes.
		if( ( ctl_code != ioctl_codes::read_code ) && ( ctl_code != ioctl_codes::write_code ) && ( ctl_code != ioctl_codes::init_connection_code ) )
		{
			util::log( "%s %i", "Invalid control code passed as DeviceIoControl parameter. Returning STATUS_INVALID_PARAMETER. Control Code: ", ctl_code );
			return STATUS_INVALID_PARAMETER;
		}

		util::log( "%s %i", "Io Control Code Passed: ", ctl_code );
		// initializing/establishing connection with user-mode process.
		if( ctl_code == ioctl_codes::init_connection_code )
		{
			if( driver_globals::connection_initialized )
			{
				util::log( "%s %i", "Connection with user-mode process already established." );
				return STATUS_ACCESS_DENIED;
			}
			util::log( "%s", "INITIALIZE_CONNECTION control code passed. Initializing connection with user-mode process." );
			
			const auto um_process_id = *static_cast< unsigned long* >( irp_ptr->AssociatedIrp.SystemBuffer );
			const auto um_process_lookup_res = PsLookupProcessByProcessId( reinterpret_cast< HANDLE >( um_process_id ), &driver_globals::um_process.um_process );

			if( um_process_lookup_res != STATUS_SUCCESS )
			{
				util::log( "%s %i", "PsLookupProcessByProcessId failed. Error code: ", um_process_lookup_res );
				return um_process_lookup_res;
			}
			
			driver_globals::um_process.um_process_id = um_process_id;
			driver_globals::connection_initialized = true;
			util::log( "%s", "Connection with user-mode process established. KM_READ and KM_WRITE now available." );
			return STATUS_SUCCESS;
		}

		if( !driver_globals::connection_initialized )
		{
			util::log( "%s %i", "Cannot use KM_READ or KM_WRITE until connection with user-mode process is established." );
			return STATUS_ACCESS_DENIED;
		}

		const auto& [ target_process_id, rw_length, write_to, read_from ] = *static_cast< structs::vmem_arg_t* > ( irp_ptr->AssociatedIrp.SystemBuffer );
		
		PEPROCESS target_process{ nullptr };
		const auto target_proc_lookup_res = PsLookupProcessByProcessId( reinterpret_cast< HANDLE >( target_process_id ), &target_process );

		if( target_proc_lookup_res != STATUS_SUCCESS )
		{
			util::log( "%s %i", "PsLookupProcessByProcessId call failed. Error code: ", ctl_code );
			return ctl_code;
		}

		if( ctl_code == ioctl_codes::read_code )
		{
			util::log( "%s", "KM_READ Called.\n" );
			return util::memory::KM_READ( target_process, read_from, write_to, rw_length );
		}

		util::log( "%s", "KM_WRITE Called.\n" );
		return util::memory::KM_WRITE( target_process, read_from, write_to, rw_length );
	}
	/// <summary>
	/// This function is responsible for cleaning up.
	/// </summary>
	/// <param name="driver_object_ptr"></param>
	void DriverUnload( DRIVER_OBJECT* driver_object_ptr )
	{
		util::log( "%s", "DriverUnload callback called. Cleaning up the driver." );
		IoDeleteSymbolicLink( &driver_globals::driver_device::dos_device_name );
		IoDeleteDevice( driver_object_ptr->DeviceObject );
		ObfDereferenceObject( &driver_globals::um_process.um_process );
		util::log( "%s", "DriverUnload call complete." );
	}

}

https://github.com/expressiongz/Kernel-Mode-Driver

  • 1

https://media.discordapp.net/attachments/1044764388546068510/1051935933836050482/Signature_4.png

Posts: 140

Threads: 28

Joined: Feb, 2021

Reputation: 4

Replied

@87922 it's an extension to an operating system's kernel

 

in an OS, you have:

 

BOOTLOADER - KERNEL - USERSPACE

 

The bootloader loads the kernel, the kernel manages memory allocation, system calls, etc.

The userspace is just programs, for example, the graphical window system on windows is userspace stuff.

 

making a kernel driver is like adding more code to the kernel.

 

@luxiferrwoo please correct me if I'm wrong

  • 0

Added

I could actually use this example to make  a ROBLOX exploit that doesn't get bothered by any antiviruses

  • 0

Contribute to Express, an open-source ROBLOX exploit written fully in C.

Users viewing this thread:

( Members: 0, Guests: 1, Total: 1 )