
/**
 * @file: driver.c
 *
 * Hlavni soubor ovladace syscallmon.sys zodpovedny za inicializaci
 * a uklid vsech jeho soucasti a za komunikaci s okolnim svetem (prevazne
 * s aplikacemi bezicimi v uzivatelskem rezimu).
 */

#include <ntddk.h>
#include "ioctls.h"
#include "cr0trick.h"
#include "ssdt.h"
#include "general_handler.h"
#include "syscall.h"
#include "driver.h"


/** Obsluha pozadavku IRP_MJ_CREATE.
 */
NTSTATUS DriverCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverCreate(DeviceObject=0x%p; Irp=0x%p)\n", DeviceObject, Irp));

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

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


/** Obsluha pozadavku IRP_MJ_CLOSE. Ovladac pri nem automaticky vypne
 *  zaznamenavani systemovych volani.
 */
NTSTATUS DriverClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverClose(DeviceObject=0x%p; Irp=0x%p)\n", DeviceObject, Irp));

  SyscallLoggingStop();
  IoCompleteRequest(Irp,IO_NO_INCREMENT);
  Status = STATUS_SUCCESS;

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


/** Obsluhuje pozadavky IRP_MJ_DEVICE_CONTROL.
 *
 *  Ovladac podporuje tri pozadavky:
 *  - IOCTL_SYSCALLMON_GET_EVENT - vrati jeden zaznam o systemovem
 *    volani ze seznamu.
 *  - IOCTL_SYSCALLMON_START - zacne zaznamenavat systemova volani.
 *  - IOCTL_SYSCALLMON_STOP - prestane monitorovat systemova volani.
 */
NTSTATUS DriverDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PIO_STACK_LOCATION IrpStack = NULL;
  PVOID OutBuffer = NULL;
  ULONG OutBufferLength = 0;
  ULONG ControlCode = 0;
  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_SYSCALLMON_GET_EVENTS:
    {
      PSYSCALL_RECORD sr = NULL;

      if (OutBufferLength >= sizeof(SYSCALL_RECORD)) {
        sr = GetSyscallRecord();
        if (sr != NULL) {
          Irp->IoStatus.Information = sizeof(SYSCALL_RECORD);
          RtlCopyMemory(OutBuffer, sr, sizeof(SYSCALL_RECORD));
          ExFreePoolWithTag(sr, POOL_TAG);
          Status = STATUS_SUCCESS;
        } else Status = STATUS_NO_MORE_ENTRIES;
      } else Status = STATUS_BUFFER_TOO_SMALL;
    }

      break;
    case IOCTL_SYSCALLMON_START:
      Status = SyscallLoggingStart();
      break;
    case IOCTL_SYSCALLMON_STOP:
      SyscallLoggingStop();
      Status = STATUS_SUCCESS;
      break;
    default:
      Status = STATUS_NOT_IMPLEMENTED;
      break;
  }

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

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


/** Inicializuje cast ovladace zodpovednou za komunikaci.
 *
 * Rutina vytvori komunikacni zarizeni a nasmeruje na nej symbolicky
 * okdaz, aby se k nemu dostaly i aplikace z uzivatelskeho rezimu.
 *
 * Protoze tento ovladac nelze z pameti uvolnit dynamicky, rutina nenastavuje
 * clen DriverUnload struktury DRIVER_OBJECT.
 *
 * @param DriverObject Adresa struktury DRIVER_OBJECT, ktera reprezentuje
 * ovladac syscallmon.sys.
 *
 * @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neuspech
 * operace.
 */
NTSTATUS DriverInit(PDRIVER_OBJECT DriverObject)
{
  UNICODE_STRING uDeviceName;
  UNICODE_STRING uSymLinkName;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverInit(DriverObject=0x%p)\n", DriverObject));

  RtlInitUnicodeString(&uDeviceName, DEVICE_NAME);
  Status = IoCreateDevice(DriverObject, 0, &uDeviceName, FILE_DEVICE_UNKNOWN, 0, TRUE, &DriverObject->DeviceObject);
  if (NT_SUCCESS(Status)) {
    RtlInitUnicodeString(&uSymLinkName, LINK_NAME);
    Status = IoCreateSymbolicLink(&uSymLinkName, &uDeviceName);
    if (NT_SUCCESS(Status)) {
      DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreate;
      DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;
      DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDeviceControl;
    } else IoDeleteDevice(DriverObject->DeviceObject);
  }

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


/** Odstrani symbolicky odkaz a komunikacni zarizeni ovladace.
 *
 * @Param DriverObject Adresa struktury DRIVER_OBJECT reprezetnujici
 * ovladac syscallmon.sys.
 */
VOID DriverFinit(PDRIVER_OBJECT DriverObject)
{
  UNICODE_STRING uLinkName;
  KdPrint(("driver.c: DriverFInit(DriverObject=0x%p)\n", DriverObject));
  
  RtlInitUnicodeString(&uLinkName, LINK_NAME);
  IoDeleteSymbolicLink(&uLinkName);
  IoDeleteDevice(DriverObject->DeviceObject);

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


/** Inicializuje cely ovladac.
 *
 * Tuto rutinu jadro zavola tesne po nacteni ovladace do pameti. Rutina
 * inicializuje mechanismus pro monitorovani systemovych volani a zajisti
 * vytvoreni komunikacniho zarizeni a symbolickeho odkazu.
 */
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegPath)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverEntry(DriverObject=0x%p; RegPath=\"0x%S\")\n", DriverObject, RegPath->Buffer));

  Status = SSDTInit();
  if (NT_SUCCESS(Status)) {
    Status = GeneralHandlerInit();
    if (NT_SUCCESS(Status)) {
      Status = DriverInit(DriverObject);
      if (NT_SUCCESS(Status)) {
        Status = SyscallLoggingInit();
        if (!NT_SUCCESS(Status)) {
          DriverFinit(DriverObject);
          GeneralHandlerFinit();
          SSDTFinit();
        }
      } else {
        GeneralHandlerFinit();
        SSDTFinit();
      }
    } else SSDTFinit();
  }

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