/**
 * @file: obinit_events.c
 *
 * Tento soubor implementuje funkce pro vytvareni udalosti specifickych
 * pro ovladac obinit.sys. Jedna se o udalosti vznikle pri monitorovani
 * operaci nad objekty typu Process, Thread, Section a Desktop.
 */
#include <ntddk.h>
#include "utils.h"
#include "driver.h"
#include "events.h"
#include "object_initializer.h"
#include "object_type.h"
#include "object.h"
#include "obinit_events.h"



/** V parametrech vrati cele jmeno objektu (vcetne cesty) a jmeno objektu typu.
 *
 *  Buffery pro ulozeno obou jmen jsou alokovany ze strankovane pameti a volajici
 *  je musi uvolnit, jakmile se jmeny nebude dale pracovat.
 *
 *  @param ObjectName Adresa struktury UNICODE_STRING, kterou rutina vyplni
 *  odkazem na retezec obsahujici cele jmeno objektu, nebo prazdny retezec, pokud
 *  objekt jmeno nema, nebo se jej nepodarilo ziskat. Pokud je hodnota parametru NULL,
 *  funkce plne jmeno objektu nebude zjistovat.
 *  @param ObjectTypeName Adresy struktury UNICODE_STRING, kterou rutina vyplni retezcem
 *  obsahujicim nazev typu objektu, nebo prazdnym retezcem, nepodari-li se nazev typu objektu
 *  zjistit. Pokud ma parametr hodnotu NULL, rutina se nepokousi jmeno typu objektu zjistovat.
 */
static VOID _GetObjectAndTypeNames(PVOID Object, PUNICODE_STRING ObjectName, PUNICODE_STRING ObjectTypeName)
{
  PWCHAR TmpBuf = NULL;
  POBJECT_TYPE ObjectType = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("obinit_events.c: _GetObjectAndTypeNames(Object=0x%p; ObjectName=0x%p; ObjectTypeName=0x%p)\n", Object, ObjectName, ObjectTypeName));
  
  if (ObjectName != NULL) {
    RtlZeroMemory(ObjectName, sizeof(UNICODE_STRING));
    Status = OBGetFullName(Object, ObjectName, NULL);
    if (!NT_SUCCESS(Status))
      RtlZeroMemory(ObjectName, sizeof(UNICODE_STRING));
  }

  if (ObjectTypeName != NULL) {
    RtlZeroMemory(ObjectTypeName, sizeof(UNICODE_STRING));
    ObjectType = OBGetObjectType(Object);
    if (ObjectType != NULL) {
      *ObjectTypeName = *OTGetName(ObjectType);
	  ObjectTypeName->MaximumLength = ObjectTypeName->Length;
	  TmpBuf = (PWCHAR)ExAllocatePoolWithTag(PagedPool, ObjectTypeName->Length + sizeof(WCHAR), POOL_TAG);
	  if (TmpBuf != NULL) {
        RtlZeroMemory(TmpBuf, ObjectTypeName->Length + sizeof(WCHAR));
		RtlCopyMemory(TmpBuf, ObjectTypeName->Buffer, ObjectTypeName->Length);
		ObjectTypeName->Buffer = TmpBuf;
	  } else RtlZeroMemory(ObjectTypeName, sizeof(UNICODE_STRING));

      OBReleaseObjectPointer(ObjectType);
	}
  }

  KdPrint(("obinit_events.c: _GetObjectAndTypenames(-)\n"));
  return;
}



/** Dosadi do zaznamu udalosti aktualni cas.
 *
 *  @param Event Adresa zaznamu udalosti.
 */
static VOID _FillEventTime(PEVENT Event)
{
  LARGE_INTEGER SysTime;
  LARGE_INTEGER LocTime;

  KeQuerySystemTime(&SysTime);
  ExSystemTimeToLocalTime(&SysTime, &LocTime);
  Event->Time = LocTime;

  return;
}



/** Vytvori udalost nesouci informace o vytvoreni handle k objektu monitorovaneho
 *  typu.
 *
 *  @param OpenReason Zpusob, kterym bylo handle vytvoreno.
 *  @param Process Adresa struktury procesu, kteremu patri cilova tabulka handle. Pokud ma
 *  tento parametr hodnotu NULL, tabulka handle patri procesu System (jedna se o tabulku handle
 *  jadra].
 *  @param Object Adresa objektu, ke kteremu bylo nove handle vytvoreno.
 *  @param GrantedAccess Maska pristupovych prav noveho handle.
 *  @param AccessMode Tetno parametr muze nabyvat tri hodnot:
 *    @value ACCESS_MODE_UNKNOWN Neni znamo, kdo handle vytvoril.
 *    @value ACCESS_MODE_KERNEL Handle vytvoril ovladac jadra.
 *    @param ACCESS_MODE_USER Handle vytvorila aplikace bezici v uzivatelskem rezimu.
 *  @param ReturnStatus Navratovy kod originalniho kodu metody.
 *
 *  @return Funkce vraci adresu noveho zaznamu udalosti v pripade uspechu a
 *  NULL v pripade neuspechu.
 */
PEVENT OpenEventCreate(OPEN_REASON OpenReason, PEPROCESS Process, PVOID Object, ACCESS_MASK GrantedAccess, ULONG AccessMode, NTSTATUS ReturnStatus)
{
  ULONG EventSize = 0;
  PEVENT_OPEN OpenEvent = NULL;
  PEVENT Ret = NULL;
  POBJECT_TYPE ObjectType = NULL;
  UNICODE_STRING ObjectName;
  UNICODE_STRING ObjectTypeName;
  KdPrint(("OpenEventCreate(OpenReason=%u; Process=0x%p; Object=0x%p; GrantedAccess=0x%x; AccessMode=%u; ReturnStatus=0x%x)\n",
      OpenReason, Process, Object, GrantedAccess, AccessMode, ReturnStatus));

  _GetObjectAndTypeNames(Object, &ObjectName, &ObjectTypeName);
  EventSize = sizeof(EVENT_OPEN) + ObjectName.Length + sizeof(WCHAR) + ObjectTypeName.Length + sizeof(WCHAR);
  Ret = EventAlloc(EVENT_TYPE_OPEN, EventSize);
  if (Ret != NULL) {
    _FillEventTime(Ret);
    OpenEvent = (PEVENT_OPEN)EVENT_TO_DATA(Ret);
    RtlZeroMemory(OpenEvent, EventSize);
    OpenEvent->AccessMode = AccessMode;
    OpenEvent->CurrentPID = PsGetCurrentProcessId();
    OpenEvent->CurrentTID = PsGetCurrentThreadId();
    OpenEvent->GratedAccess = GrantedAccess;
    OpenEvent->Object = Object;
    OpenEvent->OpenReason = OpenReason;
    if (Process != NULL)
      OpenEvent->ProcessId = GetProcessId(Process);
	else OpenEvent->ProcessId = (HANDLE)4;
    OpenEvent->ObjectNameLength = ObjectName.Length;
    OpenEvent->TypeNameLength = ObjectTypeName.Length;
    OpenEvent->ObjectNameOffset = sizeof(EVENT_OPEN);
    OpenEvent->TypeNameOffset = OpenEvent->ObjectNameOffset + ObjectName.Length + sizeof(WCHAR);
	RtlCopyMemory((PCHAR)OpenEvent + OpenEvent->ObjectNameOffset, ObjectName.Buffer, ObjectName.Length);
	RtlCopyMemory((PCHAR)OpenEvent + OpenEvent->TypeNameOffset, ObjectTypeName.Buffer, ObjectTypeName.Length);
  }

  if (ObjectName.Buffer != NULL)
    ExFreePoolWithTag(ObjectName.Buffer, POOL_TAG);

  if (ObjectTypeName.Buffer != NULL)
    ExFreePoolWithTag(ObjectTypeName.Buffer, POOL_TAG);

  KdPrint(("OpenEventCreate(-):0x%p\n", Ret));
  return Ret;
}


/** Vytvori zaznam udalosti o zruseni handle k objektu sledovaneho typu.
 *
 *  @param Process Adresa struktury procesu, kteremu patri cilova tabulka handle.
 *  Pokud je tato hodnota NULL, jedna se o tabulku handle procesu System, tedy
 *  tabulku handle jadra.
 *  @param Object Adresa objektu, jehoz handle bylo zruseno.
 *  @param AccessMode Tetno parametr muze nabyvat tri hodnot:
 *    @value ACCESS_MODE_UNKNOWN Neni znamo, kdo handle zrusil.
 *    @value ACCESS_MODE_KERNEL Handle zrusil ovladac jadra.
 *    @param ACCESS_MODE_USER Handle zrusila aplikace bezici v uzivatelskem rezimu.
 *  @param AccessMask Maska pristupovych prav daneho handle. Pokud ma tento parametr
 *  hodnotu 0, tato informace neni na dane verzi Windows znama.
 *
 *  @return Funkce vraci v pripade uspechu adresu noveho zaznamu udalosti. V pripade
 *  neuspechu vrati NULL.
 */
PEVENT CloseEventCreate(PEPROCESS Process, PVOID Object, ULONG AccessMode, ACCESS_MASK AccessMask)
{
  ULONG EventSize = 0;
  PEVENT Ret = NULL;
  UNICODE_STRING ObjectName;
  UNICODE_STRING ObjectTypeName;
  PEVENT_CLOSE CloseEvent = NULL;
  KdPrint(("CloseEventCreate(Process=0x%p; Object=0x%p; AccessMode=%u; AccessMask=0x%x)\n",
      Process, Object, AccessMode, AccessMask));

  _GetObjectAndTypeNames(Object, &ObjectName, &ObjectTypeName);
  EventSize = sizeof(EVENT_CLOSE) + ObjectName.Length + sizeof(WCHAR) + ObjectTypeName.Length + sizeof(WCHAR);
  Ret = EventAlloc(EVENT_TYPE_CLOSE, EventSize);
  if (Ret != NULL) {
    _FillEventTime(Ret);
    CloseEvent = (PEVENT_CLOSE)EVENT_TO_DATA(Ret);
	RtlZeroMemory(CloseEvent, EventSize);
	CloseEvent->AccessMode = AccessMode;
	CloseEvent->CurrentPID = PsGetCurrentProcessId();
	CloseEvent->CurrentTID = PsGetCurrentThreadId();
	CloseEvent->GrantedAccess = AccessMask;
	CloseEvent->Object = Object;
	if (Process != NULL)
	  CloseEvent->ProcessId = GetProcessId(Process);
	else CloseEvent->ProcessId = (HANDLE)4;
	CloseEvent->ObjectNameLength = ObjectName.Length;
	CloseEvent->TypeNameLength = ObjectTypeName.Length;
    CloseEvent->ObjectNameOffset = sizeof(EVENT_CLOSE);
    CloseEvent->TypeNameOffset = CloseEvent->ObjectNameOffset + ObjectName.Length + sizeof(WCHAR);
	RtlCopyMemory((PCHAR)CloseEvent + CloseEvent->ObjectNameOffset, ObjectName.Buffer, ObjectName.Length);
	RtlCopyMemory((PCHAR)CloseEvent + CloseEvent->TypeNameOffset, ObjectTypeName.Buffer, ObjectTypeName.Length);
  }

  if (ObjectName.Buffer != NULL)
    ExFreePoolWithTag(ObjectName.Buffer, POOL_TAG);

  if (ObjectTypeName.Buffer != NULL)
    ExFreePoolWithTag(ObjectTypeName.Buffer, POOL_TAG);

  KdPrint(("CloseEventCreate(-):0x%p\n", Ret));
  return Ret;
}


/** Vytvori zaznam o udalosti OkayToClose.
 *
 *  @param Process Adresa struktury procesu, kteremu patri cilova tabulka handle.
 *  Pokud je tato hodnota NULL, jedna se o tabulku handle procesu System, tedy
 *  tabulku handle jadra.
 *  @param Object Adresa objektu, jehoz handle bylo zruseno.
 *  @param Handle Hodnota handle, ktere se operace OkayToClose tyka.
 *  @param AccessMode Tetno parametr muze nabyvat tri hodnot:
 *    @value ACCESS_MODE_UNKNOWN Neni znamo, kdo operaci OkayToClose vyvolal.
 *    @value ACCESS_MODE_KERNEL Operaci OkayToClose vyvolal ovladac ci jina soucast jadra.
 *    @param ACCESS_MODE_USER Operaci OkayToClose vyvolala aplikace bezici v uzivatelskem rezimu.
 *  @param CloseAllowed Navratova hodnota volani originalniho kodu metody. TRUE znamena,
 *  ze zruseni handle je povoleno. FALSE znamena, ze dane handle zrusit nelze.
 *
 *  @return Funkce v pripade uspechu vrati adresu noveho zaznamu udalosti. V pripade neuspechu
 *  vrati NULL.
 */
PEVENT OkayToCloseEventCreate(PEPROCESS Process, PVOID Object, HANDLE Handle, ULONG AccessMode, BOOLEAN CloseAllowed)
{
  ULONG EventSize = 0;
  PEVENT Ret = NULL;
  UNICODE_STRING ObjectName;
  UNICODE_STRING ObjectTypeName;
  PEVENT_OKAYTOCLOSE OkayToCloseEvent = NULL;
  KdPrint(("OkayToCloseEventCreate(Process=0x%p; Object=0x%p; Handle=0x%p; AccessMode=%u; CloseAllowed=%u)\n",
      Process, Object, Handle, AccessMode, CloseAllowed));

  _GetObjectAndTypeNames(Object, &ObjectName, &ObjectTypeName);
  EventSize = sizeof(EVENT_OKAYTOCLOSE) + ObjectName.Length + sizeof(WCHAR) + ObjectTypeName.Length + sizeof(WCHAR);
  Ret = EventAlloc(EVENT_TYPE_OKAYTOCLOSE, EventSize);
  if (Ret != NULL) {
    _FillEventTime(Ret);
    OkayToCloseEvent = (PEVENT_OKAYTOCLOSE)EVENT_TO_DATA(Ret);
	RtlZeroMemory(OkayToCloseEvent, EventSize);
	OkayToCloseEvent->AccessMode = AccessMode;
	OkayToCloseEvent->CurrentPID = PsGetCurrentProcessId();
	OkayToCloseEvent->CurrentTID = PsGetCurrentThreadId();
	OkayToCloseEvent->Handle = Handle;
	OkayToCloseEvent->Object = Object;
    if (Process != NULL)
      OkayToCloseEvent->ProcessId = GetProcessId(Process);
	else OkayToCloseEvent->ProcessId = (HANDLE)4;
    OkayToCloseEvent->ObjectNameLength = ObjectName.Length;
    OkayToCloseEvent->TypeNameLength = ObjectTypeName.Length;
    OkayToCloseEvent->CloseAllowed = CloseAllowed;
    OkayToCloseEvent->ObjectNameOffset = sizeof(EVENT_OKAYTOCLOSE);
    OkayToCloseEvent->TypeNameOffset = OkayToCloseEvent->ObjectNameOffset + ObjectName.Length + sizeof(WCHAR);
	RtlCopyMemory((PCHAR)OkayToCloseEvent + OkayToCloseEvent->ObjectNameOffset, ObjectName.Buffer, ObjectName.Length);
	RtlCopyMemory((PCHAR)OkayToCloseEvent + OkayToCloseEvent->TypeNameOffset, ObjectTypeName.Buffer, ObjectTypeName.Length);
  }
	
  if (ObjectName.Buffer != NULL)
    ExFreePoolWithTag(ObjectName.Buffer, POOL_TAG);

  if (ObjectTypeName.Buffer != NULL)
    ExFreePoolWithTag(ObjectTypeName.Buffer, POOL_TAG);

  KdPrint(("OkayToCloseEventCreate(-):0x%p\n", Ret));
  return Ret;
}


/** Vytvori zaznam udalosti o odstraneni objektu.
 *
 *  @param Object Adresa objektu, ktery ma byt odstranen.
 *
 *  @return Funkce v pripade uspechu vrati adresu noveho zaznamu udalosti.
 *  V pripade neuspechu vraci hodnotu NULL.
 */
PEVENT DeleteEventCreate(PVOID Object)
{
  ULONG EventSize = 0;
  PEVENT Ret = NULL;
  UNICODE_STRING TypeName;
  PEVENT_DELETE DeleteEvent = NULL;
  KdPrint(("DeleteEventCreate(Object=0x%p)\n", Object));

  _GetObjectAndTypeNames(Object, NULL, &TypeName);
  EventSize = sizeof(EVENT_DELETE) + TypeName.Length + sizeof(WCHAR);
  Ret = EventAlloc(EVENT_TYPE_DELETE, EventSize);
  if (Ret != NULL) {
    _FillEventTime(Ret);
    DeleteEvent = (PEVENT_DELETE)EVENT_TO_DATA(Ret);
	RtlZeroMemory(DeleteEvent, EventSize);
	DeleteEvent->CurrentPID = PsGetCurrentProcessId();
	DeleteEvent->CurrentTID = PsGetCurrentThreadId();
	DeleteEvent->Object = Object;
	DeleteEvent->TypeNameLength = TypeName.Length;
	DeleteEvent->TypeNameOffset = sizeof(EVENT_DELETE);
	RtlCopyMemory((PCHAR)DeleteEvent + DeleteEvent->TypeNameOffset, TypeName.Buffer, TypeName.Length);
  }

  if (TypeName.Buffer != NULL)
    ExFreePoolWithTag(TypeName.Buffer, POOL_TAG);

  KdPrint(("DeleteEventCreate(-):0x%p\n", Ret));
  return Ret;
}
