 
/**
 * @file: driver.c
 *
 * Hlavni soubor ovladace obinit.sys. Obsahuje rutiny volane jadrem pri
 * inicialiyaci a uvolnovani ovladace a mechanismus pro komunikaci s 
 * okolnim svetem.
 */

#include <ntddk.h>
#include "version.h"
#include "utils.h"
#include "lists.h"
#include "events.h"
#include "pushlock.h"
#include "object_header.h"
#include "object_initializer.h"
#include "object_type.h"
#include "object_directory.h"
#include "object.h"
#include "typehook.h"
#include "obinit_events.h"
#include "obinit_hooks.h"
#include "ioctls.h"
#include "driver.h"

// PID procesu, ktery odebira udalosti. Aktivity tohoto procesu nejsou
// monitorovany
static HANDLE _MonitorProcessId = NULL;


/** Vraci PID procesu, ktery prijima od ovladace udalosti.
 *
 *  @return Funkce vraci PID procesu, ktery prijima od ovladace udalosti.
 *  Pokud yadnz takovz proces jeste neexistuje, funkce vrati hodnotu NULL.
 */
HANDLE MonitorProcessId(VOID)
{
  return _MonitorProcessId;
}



/** Obsluhuje pozadavek IRP_MJ_CREATE.
 *
 *  Pri obsluze tohoto pozadavku rutina automaticky nastavi aktualni proces
 * jako proces, kterz prijima od ovladace udalosti. Teto ulohz se proces
 * zbavi zaslanim pozadavku IRP_MJ_CLOSE. Od ovladace muze v kazdem okamziku
 * prijimat udalosti nejvyse jeden proces.
 */
NTSTATUS DriverCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverCreate(DeviceObject=0x%p; Irp=0x%p)\n", DeviceObject, Irp));

  if (_MonitorProcessId == NULL) {
    _MonitorProcessId = PsGetCurrentProcessId();
    Status = STATUS_SUCCESS;
  } else Status = STATUS_ACCESS_DENIED;

  Irp->IoStatus.Status = Status;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);

  KdPrint(("driver.c: DriverCreate(-):0x%x\n", Status));
  return Status;
}


/** Obsluhuje pozadavky IRP_MJ_CLOSE.
 *
 * Funkce se aktualnimu procesu prestane chovat jako k procesu, ktery
 * od ovlaace prijima udalosti.
 */
NTSTATUS DriverClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverClose(DeviceObject=0x%p; Irp=0x%p)\n", DeviceObject, Irp));

  _MonitorProcessId = NULL;
  Status = STATUS_SUCCESS;
  Irp->IoStatus.Status = Status;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);

  KdPrint(("driver.c: DriverClose(-):0x%x\n", Status));
  return Status;
}


/** Obsluha pozadavku typu IRP_MJ_DEVICE_CONTROL.
 *
 *  Rutina umi uspesne obslouzit pouze pozadavky s kodem IOCTL_OBINIT_GET_EVENT,
 *  jejichz vystupni buffery naplni zaznamem prvni udalosti v seznamu udalosti.
 */
NTSTATUS DriverDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
  PIO_STACK_LOCATION IrpStack = NULL;
  PVOID OutBuffer = NULL;
  ULONG OutBufferLength = 0;
  PVOID InBuffer = NULL;
  ULONG InBufferLength = 0;
  ULONG ControlCode = 0;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverDeviceControl(DeviceObject=0x%p; Irp=0x%p)\n", DeviceObject, Irp));

  IrpStack = IoGetCurrentIrpStackLocation(Irp);
  OutBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
  OutBuffer = Irp->AssociatedIrp.SystemBuffer;
  ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
  Irp->IoStatus.Information = 0;
  switch (ControlCode)
  {
    case IOCTL_OBINIT_GET_EVENT: {
      KLOCK_QUEUE_HANDLE QHandle;
      PEVENT FirstEvent = NULL;
      PUM_EVENT UmEvent = NULL;

      EventListLock(&QHandle);
      FirstEvent = EventGetFirst();
	  if (FirstEvent != NULL) {
        if (OutBufferLength >= FirstEvent->Size - sizeof(EVENT) + sizeof(UM_EVENT)) {
          UmEvent = (PUM_EVENT)OutBuffer;
          UmEvent->Size = FirstEvent->Size - sizeof(EVENT) + sizeof(UM_EVENT);
          UmEvent->Time = FirstEvent->Time;
          UmEvent->Type = FirstEvent->Type;
          RtlCopyMemory(UM_EVENT_TO_DATA(UmEvent), EVENT_TO_DATA(FirstEvent), EVENT_DATA_SIZE(FirstEvent));
          Status = STATUS_SUCCESS;
          Irp->IoStatus.Information = UmEvent->Size;
          EventFree(FirstEvent);
        } else Status = STATUS_BUFFER_TOO_SMALL;

        if (!NT_SUCCESS(Status))
          EventInsertFirst(FirstEvent);
      } else Status = STATUS_NOT_FOUND;

      EventListUnlock(&QHandle);
      break;
    }
    default:
      KdPrint(("driver.c: DriverDeviceControl(): Invalid device control request\n"));
      Status = STATUS_NOT_IMPLEMENTED;
      break;
  }  

  Irp->IoStatus.Status = Status;
  IoCompleteRequest (Irp, IO_NO_INCREMENT);

  KdPrint(("driver.c: DriverDeviceControl(-):0x%x\n", Status));
  return Status;
}



/** Odstrani symbolicky odkaz a smaze objekt zarizeni urceny pro komunikaci
 *  ovladace s okolim.
 *
 *  @param DriverObject Adresa struktury DRIVER_OBJECT ovladace obinit.sys.
 */
VOID DriverFinit(IN PDRIVER_OBJECT DriverObject)
{
  UNICODE_STRING uSymLink;
  KdPrint(("driver.c: DriverFinit(Driverobject=0x%p)\n", DriverObject));

  RtlInitUnicodeString(&uSymLink, DRIVER_SYMLINK);
  IoDeleteSymbolicLink(&uSymLink);
  IoDeleteDevice(DriverObject->DeviceObject);

  KdPrint(("driver.c: DriverFinit(-)\n"));
  return;
}


/** Tuto rutinu jadro systemu vyvola tesne pred uvolnenim ovladace z jadra.
 *  Rutina zaridi uklid vsech soucasti ovladace.
 */
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
  KdPrint(("driver.c: DriverUnload(DriverObject=0x%p)\n", DriverObject));

  DriverFinit(DriverObject);
  ObinitHooksFinit();
  TypeHookFinit();
  OBFinit();
  DirObjectFinit();
  OTModuleFinit();
  OTIFinit();
  OHFinit();
  PushLockModuleFinit();
  EventsFinit();
  UtilsFinit();
  VersionFinit();

  KdPrint(("driver.c: DriverUnload(-)\n"));
  return;
}


/** Vytvori komunikacni zarizeni a nasmeruje na nej symbolicky odkaz, aby
 *  s ovladacem meohly komunikovat i aplikace bezici v uzivatelskem rezimu.
 *
 *  @param DriverObject Adresa struktury DRIVER_OBJECT reprezentujici ovladac
 *  obinit.sys.
 */
NTSTATUS DriverInit(IN PDRIVER_OBJECT DriverObject)
{
  UNICODE_STRING uDevice;
  UNICODE_STRING uSymLink;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverInit(DriverObject=0x%p)\n", DriverObject));

  RtlInitUnicodeString(&uDevice, DRIVER_DEVICE);
  Status = IoCreateDevice(DriverObject, 0, &uDevice, FILE_DEVICE_UNKNOWN, 0, FALSE, &DriverObject->DeviceObject);
  if (NT_SUCCESS(Status)) {
    RtlInitUnicodeString(&uSymLink, DRIVER_SYMLINK);
    Status = IoCreateSymbolicLink(&uSymLink, &uDevice);
    if (NT_SUCCESS(Status)) {
      DriverObject->DriverUnload = DriverUnload;
      DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreate;
      DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;
      DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDeviceControl;
      if (!NT_SUCCESS(Status)) {
        IoDeleteSymbolicLink(&uSymLink);
        IoDeleteDevice(DriverObject->DeviceObject);
      }
    } else IoDeleteDevice(DriverObject->DeviceObject);
  }

  KdPrint(("driver.c: DriverInit(-):0x%x\n", Status));
  return Status;
}


/** Tuto rutinu jadro Windows vola tesne po nacteni ovladace do jadra.
 *  Jejim ukolem je inicializovat vsechny soucasti ovladace vcetne mechanismu
 *  komunikace s okolnim svetem.
 */
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverEntry(DriverObject=0x%p; RegistryPath=\"%S\")\n", DriverObject, RegistryPath->Buffer));

  Status = VersionInit();
  if (NT_SUCCESS(Status)) {
    Status = UtilsInit();
	if (NT_SUCCESS(Status)) {
    Status = EventsInit();
	  if (NT_SUCCESS(Status)) {
	    Status = PushLockModuleInit();
	    if (NT_SUCCESS(Status)) {
	      Status = OHInit();
		  if (NT_SUCCESS(Status)) {
		    Status = OTIInit(DriverObject);
		    if (NT_SUCCESS(Status)) {
		      Status = OTModuleInit();
			  if (NT_SUCCESS(Status)) {
			    Status = DirObjectInit(DriverObject);
			    if (NT_SUCCESS(Status)) {
			      Status = OBInit();
				  if (NT_SUCCESS(Status)) {
                    Status = TypeHookInit();
				    if (NT_SUCCESS(Status)) {
                      Status = ObinitHooksInit();
					  if (NT_SUCCESS(Status)) {
				        Status = DriverInit(DriverObject);
				        if (!NT_SUCCESS(Status)) {
                          ObinitHooksFinit();
                          TypeHookFinit();
                          OBFinit();
                          DirObjectFinit();
                          OTModuleFinit();
                          OTIFinit();
                          OHFinit();
                          PushLockModuleFinit();
                          EventsFinit();
                          UtilsFinit();
                          VersionFinit(); 
				        }
					  } else {
                        TypeHookFinit();
                        OBFinit();
                        DirObjectFinit();
                        OTModuleFinit();
                        OTIFinit();
                        OHFinit();
                        PushLockModuleFinit();
                        EventsFinit();
                        UtilsFinit();
                        VersionFinit(); 
					  }
				    } else {
                      OBFinit();
                      DirObjectFinit();
                      OTModuleFinit();
                      OTIFinit();
                      OHFinit();
                      PushLockModuleFinit();
                      EventsFinit();
                      UtilsFinit();
                      VersionFinit(); 
				    }
				  } else {
                    DirObjectFinit();
                    OTModuleFinit();
                    OTIFinit();
                    OHFinit();
                    PushLockModuleFinit();
                    EventsFinit();
                    UtilsFinit();
                    VersionFinit(); 
				  }
			    } else {
                  OTModuleFinit();
                  OTIFinit();
                  OHFinit();
                  PushLockModuleFinit();
                  EventsFinit();
                  UtilsFinit();
                  VersionFinit(); 
			    }
			  } else {
                OTIFinit();
                OHFinit();
                PushLockModuleFinit();
                EventsFinit();
                UtilsFinit();
                VersionFinit(); 
			  }
		    } else {
              OHFinit();
              PushLockModuleFinit();
              EventsFinit();
              UtilsFinit();
              VersionFinit(); 
		    }
		  } else {
            PushLockModuleFinit();
            EventsFinit();
            UtilsFinit();
            VersionFinit(); 
		  }
	    } else {
          EventsFinit();
          UtilsFinit();
          VersionFinit(); 
	    }
	  } else {
        UtilsFinit();
		VersionFinit();
	  }
	} else VersionFinit();
  }

  KdPrint(("driver.c: DriverEntry(-):0x%x\n", Status));
  return Status;
}
