/*
 * @file: typehook.c
 *
 * Tento soubor implementuje funkce pro snadne monitorovani a blokovani
 * operaci nad ruznymi typy objektu pomoci metody DKOH (Direct Kernel
 * Object Hooking). 
 */
#include <ntddk.h>
#include "version.h"
#include "lists.h"
#include "driver.h"
#include "object_initializer.h"
#include "object_type.h"
#include "typehook.h"


// Uchovava cislo aktualni verze operacniho systemu
static ULONG VersionCode = VERSION_UNKNOWN;

// Seznam objektu reprezentujicich typy objektu s modifikovanymi metodami
// za ucelem monitorovani/blokovani
static PLIST TypeHookObjects = NULL;

// Struktura objektu TypeHook reprezentujiciho jeden typ objektu, ktery
// je monitorovan ci operacni nad nimz jsou blokovany
typedef struct _TYPEHOOK_OBJECT {
  // Propojeni vsech objektu TypeHook
  LIST_ENTRY Entry;
  // tRUE - operace nad objekty daneho typu jsou monitorovany. Jinak FALSE.
  BOOLEAN Active;
  // Typ objektu
  POBJECT_TYPE ObjectType;
  // Adresy modifikovanych metod
  HOOK_PROCEDURES New;
  // Adresy originalnich metod
  HOOK_PROCEDURES Old;
  // Adresy jednotlivych metod ve struktury OBJECT_TYPE_INITIALIZAER
  // prislusneho typu objektu
  HOOK_PROCEDURES Addresses;
} TYPEHOOK_OBJECT, *PTYPEHOOK_OBJECT;



/** Vytvori objekt TypeHook nad zadanym druhem objektu.
 *
 *  @pram ObjectType Typ objektu, nad kterym si volajici preje vytvorit
 *  objekt TypeHook.
 *  @param HookProcedures Adresy novych metod, kterym bude spravce objektu
 *  predavat riyeni misto metod aktualne uvedenzch ve strukture OBJECT_TYPE_INITIALIZER
 *  prislusneho objektu typu.
 *  @param HookObject Adresa, kam funkce v pripade uspechu zapise adresu struktury
 *  popisujici novy objekt TypeHook.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neuspech operace.
 */
NTSTATUS TypeHookObjectCreate(POBJECT_TYPE ObjectType, PHOOK_PROCEDURES HookProcedures, PVOID *HookObject)
{
  PTYPEHOOK_OBJECT TypeHookObject = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("typehook.c: TypeHookObjectCreate(ObjectType=0x%p; HookProcedures=0x%p; HookObject=0x%p)\n", ObjectType, HookProcedures, HookObject));

  TypeHookObject = ExAllocatePoolWithTag(NonPagedPool, sizeof(TYPEHOOK_OBJECT), POOL_TAG);
  if (TypeHookObject != NULL) {
    RtlZeroMemory(TypeHookObject, sizeof(TYPEHOOK_OBJECT));
    TypeHookObject->ObjectType = ObjectType;
    TypeHookObject->Active = FALSE;
    InitializeListHead(&TypeHookObject->Entry);
    TypeHookObject->New = *HookProcedures;
    Status = STATUS_SUCCESS;
  } else Status = STATUS_INSUFFICIENT_RESOURCES;

  *HookObject = TypeHookObject;

  KdPrint(("typehook.c: TypeHookObjectCreate(-):0x%x, *HookObject=0x%p\n", Status, *HookObject));
  return Status;
}



/** Aktivuje zadany objekt TypeHook.
 *
 *  Po aktivaci objektu TypeHook dojde k prepsani metod ve strukture
 *  OBJECT_TYPE_INITIALIZER prislusneho objektu typu adresami novych
 *  metod. Volajici ovladac tak muze monitorovat ci blokovat operace
 *  nad jednotlivymi objekty.
 *
 *  @param HookObject Adresy objektu TypeHook.
 *  @param OriginalProcedures Adresy originalnich metod. Volajici tyto
 *  metody muze volat behem vykonavani kodu svych metod, jejichz adresy
 *  jsou zapsany ve strukture OBJECT_TYPE_INITIALIZER prislusneho objektu
 *  typu.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neuspech
 *  operace.
 */
NTSTATUS TypeHookObjectActivate(PVOID HookObject, PHOOK_PROCEDURES OriginalProcedures)
{
  HOOK_PROCEDURES ProcedureAddresses;
  PVOID TypeInfo = NULL;
  PTYPEHOOK_OBJECT TypeHookObject = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("typehook.c: TypeHookObjectActivate(HookObject=0x%p; OriginalProcedures=0x%p)\n", HookObject, OriginalProcedures));

  TypeHookObject = (PTYPEHOOK_OBJECT)HookObject;
  if (!TypeHookObject->Active) {
    TypeHookObject->Active = TRUE;
    TypeInfo = OTGetTypeInfo(TypeHookObject->ObjectType);
    if (TypeInfo != NULL) {
      Status = STATUS_SUCCESS;
      switch (VersionCode) {
        case VERSION_W2K:
          ProcedureAddresses.CloseProcedure = &OTIW2KCloseProcedure(TypeInfo);
          ProcedureAddresses.OpenProcedure = &OTIW2KOpenProcedure(TypeInfo);
          ProcedureAddresses.OkayToCloseProcedure = &OTIW2KOkayToCloseProcedure(TypeInfo);
          ProcedureAddresses.DeleteProcedure = &OTIW2KDeleteProcedure(TypeInfo);
          break;
        case VERSION_WXP:
          ProcedureAddresses.CloseProcedure = &OTIWXPCloseProcedure(TypeInfo);
          ProcedureAddresses.OpenProcedure = &OTIWXPOpenProcedure(TypeInfo);
          ProcedureAddresses.OkayToCloseProcedure = &OTIWXPOkayToCloseProcedure(TypeInfo);
          ProcedureAddresses.DeleteProcedure = &OTIWXPDeleteProcedure(TypeInfo);
          break;
        case VERSION_W2K3:
          ProcedureAddresses.CloseProcedure = &OTIW2K3CloseProcedure(TypeInfo);
          ProcedureAddresses.OpenProcedure = &OTIW2K3OpenProcedure(TypeInfo);
          ProcedureAddresses.OkayToCloseProcedure = &OTIW2K3OkayToCloseProcedure(TypeInfo);
          ProcedureAddresses.DeleteProcedure = &OTIW2K3DeleteProcedure(TypeInfo);
          break;
        case VERSION_VISTA:
          ProcedureAddresses.CloseProcedure = &OTIVistaCloseProcedure(TypeInfo);
          ProcedureAddresses.OpenProcedure = &OTIVistaOpenProcedure(TypeInfo);
          ProcedureAddresses.OkayToCloseProcedure = &OTIVistaOkayToCloseProcedure(TypeInfo);
          ProcedureAddresses.DeleteProcedure = &OTIVistaDeleteProcedure(TypeInfo);
          break;
        case VERSION_W7:
          ProcedureAddresses.CloseProcedure = &OTIW7CloseProcedure(TypeInfo);
          ProcedureAddresses.OpenProcedure = &OTIW7OpenProcedure(TypeInfo);
          ProcedureAddresses.OkayToCloseProcedure = &OTIW7OkayToCloseProcedure(TypeInfo);
          ProcedureAddresses.DeleteProcedure = &OTIW7DeleteProcedure(TypeInfo);
          break;
        default:
          Status = STATUS_NOT_SUPPORTED;
          break;
      }

      if (NT_SUCCESS(Status)) {
        TypeHookObject->Addresses = ProcedureAddresses;
        TypeHookObject->Old.CloseProcedure = *(PVOID *)ProcedureAddresses.CloseProcedure;
        TypeHookObject->Old.OpenProcedure = *(PVOID *)ProcedureAddresses.OpenProcedure;
        TypeHookObject->Old.OkayToCloseProcedure = *(PVOID *)ProcedureAddresses.OkayToCloseProcedure;
        TypeHookObject->Old.DeleteProcedure = *(PVOID *)ProcedureAddresses.DeleteProcedure;
        *OriginalProcedures = TypeHookObject->Old;
        *(PVOID *)ProcedureAddresses.CloseProcedure = TypeHookObject->New.CloseProcedure;
        *(PVOID *)ProcedureAddresses.OpenProcedure = TypeHookObject->New.OpenProcedure;
        *(PVOID *)ProcedureAddresses.OkayToCloseProcedure = TypeHookObject->New.OkayToCloseProcedure;
        *(PVOID *)ProcedureAddresses.DeleteProcedure = TypeHookObject->New.DeleteProcedure;
        lListTailInsertLock(TypeHookObjects, &TypeHookObject->Entry);
      } else TypeHookObject->Active = FALSE;
    } else TypeHookObject->Active = FALSE;
  } else TypeHookObject->Active = FALSE;

  KdPrint(("typehook.c: TypeHookObjectActivate(-):0x%x", Status));
  return Status;
}


/** Odstrani zadany objekt TypeHook z pameti.
 *
 *  Rutina musi byt volana pouze na neaktivni objekty TypeHook. Je parova
 *  k funkce TypeHookObjectCreate.
 *
 *  @param HookObject Adresa objektu TypeHook, ktery ma rutina znicit.
 */
VOID TypeHookObjectDestroy(PVOID HookObject)
{
  PTYPEHOOK_OBJECT TypeHookObject = NULL;
  KdPrint(("typehook.c: TypeHookObjectDestroy(HookObject=0x%p)\n", HookObject));

  TypeHookObject = (PTYPEHOOK_OBJECT)HookObject;
  ASSERT(!TypeHookObject->Active);
  ExFreePoolWithTag(TypeHookObject, POOL_TAG);

  KdPrint(("typehook.c: TypeHookObjectDestroy(-)\n"));
  return;
}



/** Deaktivuje zadany objekt TypeHook.
 *
 *  Rutina modifikuje metody ve strukture OBJECT_TYPE_INITIALIZER prislusneho
 *  druhu objektu na jejich puvodni adresy. Tato rutina je neverejna.
 *
 *  @param HookObject Adresa objektu TypeHook.
 *  @param Remove Pokud je tato promenna nastavena na TRUE, funkce zaroven
 *  odstrani yadanz objekt TzpeHook ye seynamu techto objektu. Pokud je
 8  hodnota FALSE, k odstranovani nedochazi.
 */
static VOID _TypeHookObjectDeactivate(PTYPEHOOK_OBJECT TypeHookObject, BOOLEAN Remove)
{
  KLOCK_QUEUE_HANDLE QHandle;
  KdPrint(("typehook.c: TypeHookObjectDeactivate(TypeHookObject=0x%p; Remove=%u)\n", TypeHookObject, Remove));
  
  if (Remove) {
    lListLock(TypeHookObjects, &QHandle);
    TypeHookObject->Entry.Blink->Flink = TypeHookObject->Entry.Flink;
    TypeHookObject->Entry.Flink->Blink = TypeHookObject->Entry.Blink;
    InitializeListHead(&TypeHookObject->Entry);
    lListUnlock(TypeHookObjects, &QHandle);
  }

  *(PVOID *)(TypeHookObject->Addresses.CloseProcedure) = TypeHookObject->Old.CloseProcedure;
  *(PVOID *)(TypeHookObject->Addresses.OpenProcedure) = TypeHookObject->Old.OpenProcedure;
  *(PVOID *)(TypeHookObject->Addresses.OkayToCloseProcedure) = TypeHookObject->Old.OkayToCloseProcedure;
  *(PVOID *)(TypeHookObject->Addresses.DeleteProcedure) = TypeHookObject->Old.DeleteProcedure;

  TypeHookObject->Active = FALSE;
    
  KdPrint(("typehook.c: TypeHookObjectDeactivate(-)\n"));
  return;
}



/** Deaktivuje zadany objekt TypeHook.
 *
 *  Tato rutina je, na rozdil od _TypeHookObjectDeactivate, viditelna
 *  pro ostatni modulz ovladace. Nejedna se o interni funkci. Pri jejim
 *  volani dojde vzdy k odebrani zadaneho objektu TypeHook ze spojoveho
 *  seznamu techto objektu.
 *
 *  @param HookObject Adresa objektu TypeHook, ktery si volajici preje
 *  deaktivovat.
 */
VOID TypeHookObjectDeactivate(PVOID HookObject)
{
  KdPrint(("typehook.c: TypeHookObjectDeactivate(HookObject=0x%p)\n", HookObject));
  
  _TypeHookObjectDeactivate((PTYPEHOOK_OBJECT)HookObject, TRUE);

  KdPrint(("typehook.c: TypeHookObjectDeactivate(-)\n"));
  return;
}



/** Deaktivuje a uvolni zadany objekt TypeHook.
 *
 *  Tato rutina je neverejna a vola se pri uklidove fazi modulu na kazdy
 *  aktivni objekt TypeHook.
 *
 *  @param Item Prvek spojoveho seznamu objektu TypeHook. Jedna se o
 *  adresu postupne kazdeho z techto objektu pritomnych pri uklidove
 *  fazi v seznamu.
 */
static VOID _TypeHookListDestroyItem(PVOID Item)
{
  KdPrint(("typehook.c: _TypeHookListDestroyItem(Item=0x%p)\n", Item));

  _TypeHookObjectDeactivate((PTYPEHOOK_OBJECT)Item, FALSE);
  TypeHookObjectDestroy(Item);

  KdPrint(("typehook.c: _TypeHookListDestroyItem(-)\n"));
  return;
}


/** Inicializuje modul pro praci s objekty TypeHook.
 *
 *  Rutina zjisti cislo verze operacniho systemu a inicializuje seznam
 *  (prazdny) pro objekty TypeHook.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neuspech
 *  operace.
 */
NTSTATUS TypeHookInit(VOID)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("typehook.c: TypeHookInit()\n"));

  VersionCode = GetVersionCode();
  if (VersionCode != VERSION_UNKNOWN) {
    TypeHookObjects = lListCreate(NonPagedPool, _TypeHookListDestroyItem);
	if (TypeHookObjects != NULL)
      Status = STATUS_SUCCESS;
	else Status = STATUS_UNSUCCESSFUL;
  } else Status = STATUS_NOT_SUPPORTED;

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



/** Provede uklid po modulu pro praci s objekty TypeHook.
 *
 *  Rutina deaktivuje vsechny objekty TypeHook zbyvajici ve spojovem
 *  seznamu a uvolni je z pameti.
 */
VOID TypeHookFinit(VOID)
{
  KdPrint(("typehook.c: TypeHookFinit()\n"));
  
  lListDestroyLock(TypeHookObjects);

  KdPrint(("typehook.c: TypeHookFinit(-)\n"));
  return;
}
