/*
 hook1 is basic driver with unload support, logs every action too DebugView
 enjoy your first lesson
*/


/*
 this inelegant extern here is because of microsoft of course :)
 we all want to code c99, however, microsoft compiler does not support it
 so, we're forced to use c++ and that's why we need this here
 because we still want c headers and c format of functions etc.
 it is still better to use c++ and use only features that we want 
 than to use microsft supported old ansi-c and be without these features
*/

extern "C"
{
#include <wdm.h>
#include "debug.h"

#define DEVICE_NAME             L"\\Device\\hook1"
#define DOS_DEVICE_NAME         L"\\DosDevices\\hook1"


NTSTATUS hook1_create(PDEVICE_OBJECT DeviceObject,PIRP Irp);
NTSTATUS hook1_close(PDEVICE_OBJECT DeviceObject,PIRP Irp);
NTSTATUS hook1_device_control(PDEVICE_OBJECT DeviceObject,PIRP Irp);
VOID hook1_unload(PDRIVER_OBJECT DriverObject);
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath);
} //extern "C"


/*
 dos device name is global variable because we use it in unload too,
 we can always make another RtlInitUnicodeString if we don't like global vars
*/

UNICODE_STRING dos_dev_name;


/*
 create function is called everytime CreateFile is called on our device 
*/

NTSTATUS hook1_create(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
  DbgMsg("hook1.cpp: hook1_create(DeviceObject:0x%.8X,Irp:0x%.8X)",DeviceObject,Irp);

  NTSTATUS status=STATUS_SUCCESS;
  Irp->IoStatus.Status=status;
  IoCompleteRequest(Irp,IO_NO_INCREMENT);

  DbgMsg("hook1.cpp: hook1_create(-):0x%.8X)",status);

  return status;
}


/*
 close function is called everytime CloseHandle is called on our device
 close is associated with IRP_MJ_CLOSE and it is NOT executed in the context 
 of the CloseHandle caller, if we want to make some cleanup in that context 
 we rather associate cleanup function with IRP_MJ_CLEANUP
*/

NTSTATUS hook1_close(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
  DbgMsg("hook1.cpp: hook1_close(DeviceObject:0x%.8X,Irp:0x%.8X)",DeviceObject,Irp);

  NTSTATUS status=STATUS_SUCCESS;
  Irp->IoStatus.Status=status;
  IoCompleteRequest(Irp,IO_NO_INCREMENT);

  DbgMsg("hook1.cpp: hook1_close(-):0x%.8X)",status);

  return status;
}


/*
 device control function is called everytime DeviceIoControl is called on our
 device, it is common way how user mode app communicate with driver
*/

NTSTATUS hook1_device_control(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
  DbgMsg("hook1.cpp: hook1_device_control(DeviceObject:0x%.8X,Irp:0x%.8X)",DeviceObject,Irp);

  NTSTATUS status=STATUS_SUCCESS;
  Irp->IoStatus.Status=status;
  IoCompleteRequest(Irp,IO_NO_INCREMENT);

  DbgMsg("hook1.cpp: hook1_device_control(-):0x%.8X)",status);

  return status;
}


/*
 unload is called when driver is being unloaded, if we do not implement unload
 function them our driver can't be unloaded dynamically
*/

VOID hook1_unload(PDRIVER_OBJECT DriverObject)
{    
  DbgMsg("hook1.cpp: hook1_unload(DriverObject:0x%.8X)",DriverObject);
  
  //cleanup everything our driver created - delete symlink and device 
  IoDeleteSymbolicLink(&dos_dev_name);
  IoDeleteDevice(DriverObject->DeviceObject);

  DbgMsg("hook1.cpp: hook1_unload(-)");
}


/*
 DriverEntry is common driver entry point
*/

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
  DbgMsg("hook1.cpp: DriverEntry(DriverObject:0x%.8X,RegistryPath:0x%.8X)",DriverObject,RegistryPath);

  UNICODE_STRING dev_name;
  RtlInitUnicodeString(&dev_name,DEVICE_NAME);
  RtlInitUnicodeString(&dos_dev_name,DOS_DEVICE_NAME); 

  /*
   if we want our driver to be accessible we need to create device for it, 
   one driver can have more devices 
  */
  PDEVICE_OBJECT dev_obj;
  NTSTATUS status=IoCreateDevice(DriverObject,0,&dev_name,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&dev_obj);

  if (NT_SUCCESS(status))
  {
    /*
     for some selected major functions we set handlers
    */
    DriverObject->MajorFunction[IRP_MJ_CREATE]          = hook1_create;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]           = hook1_close;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = hook1_device_control;
    DriverObject->DriverUnload                          = hook1_unload;

    /*
     this selects the method of IO, we use buffered IO as it is comfortable 
     and effective for smaller packets
    */
    dev_obj->Flags|=DO_BUFFERED_IO;

    /*
     if we want user mode application to communicate our driver we need to make 
     a dos device link
    */
    status=IoCreateSymbolicLink(&dos_dev_name,&dev_name);
    if (!NT_SUCCESS(status))
    {
      DbgMsg("hook1.cpp: DriverEntry.IoCreateSymbolicLink failed with status 0x%.8X",status);
      IoDeleteDevice(DriverObject->DeviceObject);
    }
  } else DbgMsg("hook1.cpp: DriverEntry.IoCreateDevice failed with status 0x%.8X",status);

  DbgMsg("hook1.cpp: DriverEntry(-):0x%.8X",status);
  return status;
}

