
/** @file notify.c
 *
 * Implementace notifikacnich rutin, ktere system vola pri ruznych udalostech
 * tykajicich se procesu, vlaken, knihoven DLL a ovladacu jadra. Tyto rutiny
 * jednotlive udalosti zaznamenaji, aby je ovladac pozdeji mohl predat aplikaci.
 *
 * Zaznamenavani je implementovano pomoci dokumentovanych funkci, ktere jadro poskytuje.
 * Poouzite rozhrani lze vyuzit pouze na monitorovani, nikoliv na aktivni ovlivnovani
 * prave probihajicich udalosti, napriklad na zablokovani spusteni noveho procesu.
 */

#include <ntddk.h>
#include "events.h"
#include "notify_events.h"
#include "notify.h"



/** Rutina je vyvolana pri vytvareni noveho ci zaniku existujiciho proceu.
 *
 * @param ParentId Cislo PID rodice (validni pri vytvareni noveho procesu).
 * @param ProcessId Identifikator PID nove vznikajiciho, nebo prave zanikajiciho procesu.
 * @param Create Logicka hodnota urcuje, zda doslo k vytvoreni noveho (TRUE) ci zaniku
 *   existujiciho procesu (FALSE).
 */
VOID NotifyProcess(IN HANDLE ParentId, HANDLE ProcessId, IN BOOLEAN Create)
{
  PEVENT Event = NULL;
  LARGE_INTEGER SysTime;
  LARGE_INTEGER LocTime;
  KdPrint(("notify.c: NotifyProcess(ParentId=0x%p; ProcessId=0x%p; Create=%u)\n", ParentId, ProcessId, Create));

  // Zjistime aktualni cas udalosti a prevedeme do spravne casove zony
  KeQuerySystemTime(&SysTime);
  ExSystemTimeToLocalTime(&SysTime, &LocTime);

  // Vytvorime novou udalost popisujici vznik/zanik procesu
  Event = ProcessEventCreate(&LocTime, ParentId, PsGetCurrentProcessId(), PsGetCurrentThreadId(), ProcessId, Create);
  if (Event != NULL)
    // Podari-li se, ulozime ji do seznamu udalosti
    EventAddToListLock(Event);

  KdPrint(("notify.c: NotifyProcess(-)\n"));
  return;
}


/** Sytem tuto proceduru vyvola prave tehdy, kdyz vznika ci zanika nejake vlakno.
 *
 * @param ProcessId Identifikator procesu, kteremu vlakno patri.
 * @param ThreadId Identifikator prislusneho vlakna.
 * @param Create Urcuje, zda byla rutina volana pri vytvareni noveho vlakna, nebo
 *   behem zaniku jiz existujiciho vlakna.
 */
VOID NotifyThread(IN HANDLE ProcessId, IN HANDLE ThreadId, IN BOOLEAN Create)
{
  PEVENT Event = NULL;
  LARGE_INTEGER SysTime;
  LARGE_INTEGER LocTime;
  KdPrint(("notify.c: NotifyThread(ProcessId=0x%p; ThreadId=0x%p; Create=%u)\n", ProcessId, ThreadId, Create));

  // Zjistime aktualni cas udalosti a prevedeme do spravne casove zony
  KeQuerySystemTime(&SysTime);
  ExSystemTimeToLocalTime(&SysTime, &LocTime);

  // Vytvorime novou udalost popisujici vznik/zanik procesu  
  Event = ThreadEventCreate(&LocTime, ThreadId, ProcessId, PsGetCurrentThreadId(), PsGetCurrentProcessId(), Create);
  if (Event != NULL)
    // Podari-li se, ulozime ji do seznamu udalosti
    EventAddToListLock(Event);

  KdPrint(("notify.c: NotifyThread(-)\n"));
  return;
}


/** System tuto rutinu vyvola behem nacitani noveho modulu do pameti.
 *
 * System rutine preda rizeni jak pri nacitani modulu do privatni casti adresoveho
 * prostoru procesu, tak pri nacitani noveho ovladace do jadra.
 *
 * @param FullImageName Cele jmeno souboru modulu.
 * @param ProcessId Identifikator procesu, do jehoz prostoru se modul nacita.
 *   Je-li procedura volana v dusledku nacitani noveho ovladace, tento parametr
 *   obsahuje hodnotu NULL.
 * @param ImageInfo Struktura s ruznymi informacemi o modulu (basova adresa, velikost...).
 */
VOID NotifyImage(IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo)
{
  PEVENT Event = NULL;
  LARGE_INTEGER SysTime;
  LARGE_INTEGER LocTime;
  KdPrint(("notify.c: NotifyImage(FullImageName=\"%S\"; ProcessId=0x%p; ImageInfo=0x%p)\n", FullImageName->Buffer, ProcessId, ImageInfo));

  // Zjistime aktualni cas udalosti a prevedeme do spravne casove zony
  KeQuerySystemTime(&SysTime);
  ExSystemTimeToLocalTime(&SysTime, &LocTime);

  // Vytvorime novou udalost popisujici vznik/zanik procesu  
  Event = ImageEventCreate(&LocTime, FullImageName, ProcessId, PsGetCurrentThreadId(), ImageInfo->ImageBase, (ULONG)ImageInfo->ImageSize);
  if (Event != NULL)
    // Podari-li se, ulozime ji do seznamu udalosti
    EventAddToListLock(Event);

  KdPrint(("notify.c: NotifyImage(-)\n"));
  return;
}


/** Zaregistruje notifikacni rutiny.
 *
 * Funkce je volana behem inicializace ovladace a pokud, selze, selze i
 * nacteni driveru do jadra.
 *
 * Instruuje system, aby volal urcite rutiny naseho ovladace pri ruznych
 * udalostech tykajicich se procesu, vlaken a modulu.
 */
NTSTATUS NotifyInit(VOID)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("notify.c: NotifyInit()\n"));

  // Zaregistrujeme rutinu pro sbirani udalosti tykajicich se procesu
  Status = PsSetCreateProcessNotifyRoutine(NotifyProcess, FALSE);
  if (NT_SUCCESS(Status)) {
    // Zaregistrujeme rutinu pro sbirani udalosti tykajicich se vlaken
    Status = PsSetCreateThreadNotifyRoutine(NotifyThread);
    if (NT_SUCCESS(Status)) {
      // Registrace rutiny pro zachytavani udalosti tykajicich se souboru PE.
      Status = PsSetLoadImageNotifyRoutine(NotifyImage);
      if (!NT_SUCCESS(Status)) {
        // V pripade neuspechu je nutne odregistrovat, co bylo zaregistrovano
        PsRemoveCreateThreadNotifyRoutine(NotifyThread);
        PsSetCreateProcessNotifyRoutine(NotifyProcess, TRUE);
      }
    } else { 
      // V pripade neuspechu je nutne odregistrovat, co bylo zaregistrovano
      PsSetCreateProcessNotifyRoutine(NotifyProcess, TRUE);
    }
  }

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


/** Odregistruje notifikacni rutiny.
 *
 * Rutina je volana behem odstranovani ovladace z jadra.
 */
VOID NotifyFinit(VOID)
{
  KdPrint(("notify.c: NotifyFinit()\n"));

  PsRemoveLoadImageNotifyRoutine(NotifyImage);
  PsRemoveCreateThreadNotifyRoutine(NotifyThread);
  PsSetCreateProcessNotifyRoutine(NotifyProcess, TRUE);

  KdPrint(("notify.c: NotifyFinit(-)\n"));
  return;
}
