
/**
 * @file: ssdtinfo.c
 *
 * Tento soubor implementuje nekolik funkci umoznujicich cteni informaci
 * o tabulkach systemovych volani.
 */

#include <ntddk.h>
#include "version.h"
#include "ssdtinfo.h"

// Uchovava adresu tabulky sytemovych volani pouze pro hlavni modul jadra
static PSERVICE_TABLE ServiceTable = NULL;
// Uchovava adresu stinove tabulky systemovych volani
static PSERVICE_TABLE ServiceTableShadow = NULL;

// Offset adresy stinove tabulky systemovych volani ve strukture ETHREAD
static ULONG SERVICE_TABLE_OFFSET = 0;

// Uchovava pocet podtabulek tabulky systemovych volani
static ULONG gNumberOfTables = 0;


/** Vrati adresu tabulky systemovych volani pouze pro hlavni modul jadra.
 *
 *  @return Funkce vrati adresu tabulky sytemovych volani pouze pro hlavni
 *  modul jadra. Pokud jeste modul nebzl inicialiyovan, rutina vrati NULL.
 */
PSERVICE_TABLE SSDTInfoGetServiceTable(VOID)
{
  return ServiceTable;
}


/** Vrati adresu stinove tabulky systemovych volani.
 *
 * @return Funkce vrati adresu stinove tabulky systemovych volani. Pokud
 * jeste nebzl modul dostatecne inicialiyovan, rutina vrati hodnotu NULL.
 */
PSERVICE_TABLE SSDTInfoGetServiceTableShadow(VOID)
{
  return ServiceTableShadow;
}



/** Zjisti offset adresy tabulky systemovych volani v ramci struktury
 *  ETHREAD.
 *
 *  Rutina sve rozhodnuti plne zaklada na verzi operacniho systemu.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neupsech
 *  operace.
 */
static NTSTATUS GetServiceTableOffset(VOID)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("ssdtinfo.c: GetServiceTableOffset()\n"));

  switch (GetVersionCode()) {
    case VERSION_W2K:
      SERVICE_TABLE_OFFSET = 0x0DC;
      gNumberOfTables = 4;
      break;
    case VERSION_WXP:
      SERVICE_TABLE_OFFSET = 0x0E0;
      gNumberOfTables = 4;
      break;
    case VERSION_W2K3:
      SERVICE_TABLE_OFFSET = 0x118;
      gNumberOfTables = 4;
      break;
    case VERSION_VISTA:
      SERVICE_TABLE_OFFSET = 0x12C;
      gNumberOfTables = 2;
      break;
    case VERSION_W7:
      SERVICE_TABLE_OFFSET = 0x0BC;
      gNumberOfTables = 2;
      break;
    default:
      SERVICE_TABLE_OFFSET = 0;
      Status = STATUS_NOT_IMPLEMENTED;
      break;
  }

  if (SERVICE_TABLE_OFFSET != 0)
    Status = STATUS_SUCCESS;

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



/** Zjisti zakladni informace o zadane tabulce systemovych volani.
 *
 *  @param TableType Tento parametr urcuje, o kterou tabulku systemovych
 *  volani se volajici zajima. Moznosti jsou nasledujici:
 *    @value SERVICE_TABLE_NORMAL Tabulka systemovych volani pouze pro hlavni modul jadra
 *    @value SERVICE_TABLE_SHADOW Stinova tabulka systemovych volani
 *  @param Buffer Adresa bloku pameti, kam funkce v pripade uspechu zapise
 *  informace o cilove tabulce systemovych volani.
 *  @param BufferLength Udava velikost pametoveho bloku, na jehoz pocatek
 *  odkazuje parametr Buffer.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neupsech
 *  operace.
 */
NTSTATUS SSDTInfoGetBasicInfo(ULONG TableType, PVOID Buffer, ULONG BufferLength)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("ssdtinfo.c: SSDTGetBasicInfo(TableType=%u; Buffer=0x%p; BufferLength=%u)\n", TableType, Buffer, BufferLength));

  switch(TableType) {
    case TABLE_TYPE_NORMAL:
      if (BufferLength >= sizeof(SERVICE_TABLE)) {
        RtlCopyMemory(Buffer, ServiceTable, sizeof(SERVICE_TABLE));
        Status = STATUS_SUCCESS;
      } else Status = STATUS_BUFFER_TOO_SMALL;
      break;
    case TABLE_TYPE_SHADOW:
      if (BufferLength >= sizeof(SERVICE_TABLE)) {
        RtlCopyMemory(Buffer, ServiceTableShadow, sizeof(SERVICE_TABLE));
        Status = STATUS_SUCCESS;
      } else Status = STATUS_BUFFER_TOO_SMALL;
      break;
    default:
      Status = STATUS_INVALID_PARAMETER;
      break;
  }

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


/** Zjisti obsah tabulky adres systemovych sluzeb zadane tabulky systemovych volani.
 *
 *  @param TableType Tento parametr urcuje, o kterou tabulku systemovych
 *  volani se volajici zajima. Moznosti jsou nasledujici:
 *    @value SERVICE_TABLE_NORMAL Tabulka systemovych volani pouze pro hlavni modul jadra
 *    @value SERVICE_TABLE_SHADOW Stinova tabulka systemovych volani
 *  @param Subtable Udava index podtabulky, jejiz tabulku adres chce zkoumat.
 *  @param Buffer Adresa bloku pameti, kam funkce v pripade uspechu zapise
 *  informace o cilove tabulce adres.
 *  @param BufferLength Udava velikost pametoveho bloku, na jehoz pocatek
 *  odkazuje parametr Buffer.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neupsech
 *  operace.
 */
NTSTATUS SSDTInfoGetAddressTable(ULONG TableType, ULONG Subtable, PVOID Buffer, ULONG BufferLength)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("ssdtinfo.c: SSDTGetAddressTable(TableType=%u;Subtable=%u; Buffer=0x%p; BufferLength=%u)\n", TableType, Subtable, Buffer, BufferLength));

  if (Subtable < gNumberOfTables) {
    switch (TableType) {
      case TABLE_TYPE_NORMAL:
        if (ServiceTable->Subtables[Subtable].ServiceTable != NULL) {
          if (BufferLength >= sizeof(ULONG32)*ServiceTable->Subtables[Subtable].NumberOfServices) {
            RtlCopyMemory(Buffer, ServiceTable->Subtables[Subtable].ServiceTable, sizeof(ULONG32)*ServiceTable->Subtables[Subtable].NumberOfServices);
            Status = STATUS_SUCCESS;
          } else Status = STATUS_BUFFER_TOO_SMALL;
        } else Status = STATUS_INVALID_PARAMETER;
        break;
      case TABLE_TYPE_SHADOW:
        if (ServiceTableShadow->Subtables[Subtable].ServiceTable != NULL) {
          if (BufferLength >= sizeof(ULONG32)*ServiceTableShadow->Subtables[Subtable].NumberOfServices) {
            RtlCopyMemory(Buffer, ServiceTableShadow->Subtables[Subtable].ServiceTable, sizeof(ULONG32)*ServiceTableShadow->Subtables[Subtable].NumberOfServices);
            Status = STATUS_SUCCESS;
          } else Status = STATUS_BUFFER_TOO_SMALL;
        } else Status = STATUS_INVALID_PARAMETER;
        break;
      default:
        Status = STATUS_NOT_IMPLEMENTED;
        break;
    }
  } else Status = STATUS_INVALID_PARAMETER;

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

/** Zjisti informace o tabulce citacu.
 *
 *  @param TableType Tento parametr urcuje, o kterou tabulku systemovych
 *  volani se volajici zajima. Moznosti jsou nasledujici:
 *    @value SERVICE_TABLE_NORMAL Tabulka systemovych volani pouze pro hlavni modul jadra
 *    @value SERVICE_TABLE_SHADOW Stinova tabulka systemovych volani
 *  @param Subtable Udava index podtabulky, jejiz tabulku citacu chce zkoumat.
 *  @param Buffer Adresa bloku pameti, kam funkce v pripade uspechu zapise
 *  informace o cilove tabulce citacu.
 *  @param BufferLength Udava velikost pametoveho bloku, na jehoz pocatek
 *  odkazuje parametr Buffer.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neupsech
 *  operace.
 */
NTSTATUS SSDTInfoGetCounterTable(ULONG TableType, ULONG Subtable, PVOID Buffer, ULONG BufferLength)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("ssdtinfo.c: SSDTGetCounterTable(TableType=%u; Subtable=%u; Buffer=0x%p; BufferLength=%u)\n", TableType, Subtable, Buffer, BufferLength));

  if (Subtable < gNumberOfTables) {
    switch (TableType) {
      case TABLE_TYPE_NORMAL:
        if (ServiceTable->Subtables[Subtable].CounterTable != NULL) {
          if (BufferLength >= sizeof(ULONG32)*ServiceTable->Subtables[Subtable].NumberOfServices) {
            RtlCopyMemory(Buffer, ServiceTable->Subtables[Subtable].CounterTable, sizeof(ULONG32)*ServiceTable->Subtables[Subtable].NumberOfServices);
            Status = STATUS_SUCCESS;
          } else Status = STATUS_BUFFER_TOO_SMALL;
        } else Status = STATUS_INVALID_PARAMETER;
        break;
      case TABLE_TYPE_SHADOW:
        if (ServiceTableShadow->Subtables[Subtable].CounterTable != NULL) {
          if (BufferLength >= sizeof(ULONG32)*ServiceTableShadow->Subtables[Subtable].NumberOfServices) {
            RtlCopyMemory(Buffer, ServiceTableShadow->Subtables[Subtable].CounterTable, sizeof(ULONG32)*ServiceTableShadow->Subtables[Subtable].NumberOfServices);
            Status = STATUS_SUCCESS;
          } else Status = STATUS_BUFFER_TOO_SMALL;
        } else Status = STATUS_INVALID_PARAMETER;
        break;
      default:
        Status = STATUS_NOT_IMPLEMENTED;
        break;
    }
  } else Status = STATUS_INVALID_PARAMETER;

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


/** Zjisti informace o tabulce argumentu.
 *
 *  @param TableType Tento parametr urcuje, o kterou tabulku systemovych
 *  volani se volajici zajima. Moznosti jsou nasledujici:
 *    @value SERVICE_TABLE_NORMAL Tabulka systemovych volani pouze pro hlavni modul jadra
 *    @value SERVICE_TABLE_SHADOW Stinova tabulka systemovych volani
 *  @param Subtable Udava index podtabulky, jejiz tabulku argumentu chce zkoumat.
 *  @param Buffer Adresa bloku pameti, kam funkce v pripade uspechu zapise
 *  informace o cilove tabulce argumentu.
 *  @param BufferLength Udava velikost pametoveho bloku, na jehoz pocatek
 *  odkazuje parametr Buffer.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neupsech
 *  operace.
 */
NTSTATUS SSDTInfoGetArgumentTable(IN ULONG TableType, IN ULONG Subtable, OUT PVOID Buffer, IN ULONG BufferLength)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("ssdtinfo.c: SSDTGetArgumentTable(TableType=%u; Subtable=%u; Buffer=0x%p; BufferLength=%u)\n", TableType, Subtable, Buffer, BufferLength));

  if (Subtable < gNumberOfTables) {
    switch (TableType) {
      case TABLE_TYPE_NORMAL:
        if (ServiceTable->Subtables[Subtable].ArgumentTable != NULL) {
          if (BufferLength >= sizeof(UCHAR)*ServiceTable->Subtables[Subtable].NumberOfServices) {
            RtlCopyMemory(Buffer, ServiceTable->Subtables[Subtable].ArgumentTable, sizeof(UCHAR)*ServiceTable->Subtables[Subtable].NumberOfServices);
            Status = STATUS_SUCCESS;
          } else Status = STATUS_BUFFER_TOO_SMALL;
        } else Status = STATUS_INVALID_PARAMETER;
        break;
      case TABLE_TYPE_SHADOW:
        if (ServiceTableShadow->Subtables[Subtable].ArgumentTable != NULL) {
          if (BufferLength >= sizeof(UCHAR)*ServiceTableShadow->Subtables[Subtable].NumberOfServices) {
            RtlCopyMemory(Buffer, ServiceTableShadow->Subtables[Subtable].ArgumentTable, sizeof(UCHAR)*ServiceTableShadow->Subtables[Subtable].NumberOfServices);
            Status = STATUS_SUCCESS;
          } else Status = STATUS_BUFFER_TOO_SMALL;
        } else Status = STATUS_INVALID_PARAMETER;
        break;
      default:
        Status = STATUS_NOT_IMPLEMENTED;
        break;
    }
  } else Status = STATUS_INVALID_PARAMETER;

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



/** Pokusi se najit adresu stinove tabulky systemovych volani.
 *
 *  Rutina uspeje pouze v pripade, ze ovladac bezi na podporovane verzi
 *  operacniho systemu a aktualni vlakno jiz provedlo nejake volani
 *  spadajici do oblasti grafickeho uzivatelskeho rozhrani.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neuspech
 *  operace.
 */
NTSTATUS SSDTInfoInitShadowTable(VOID)
{
  PETHREAD CurrentThread = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("ssdtinfo.c: SSDTInfoInitShadowTable()\n"));

  CurrentThread = PsGetCurrentThread();
  ServiceTableShadow = *(PSERVICE_TABLE*)((PCHAR)CurrentThread + SERVICE_TABLE_OFFSET);
  if (ServiceTableShadow == ServiceTable) {
    ServiceTableShadow = NULL;
    Status = STATUS_UNSUCCESSFUL;
  } else Status = STATUS_SUCCESS;

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



/** Inicializuje modul pro ziskavani informaci o tabulkach systemovych
 *  volani.
 *
 *  Rutina zjisti offset adresy tabulky systemovych volani v ramci struktury
 *  ETHREAD a najde adresu tabulky systemovych volani pouze pro hlavni modul
 *  jadra. K tomuto ucelu pouzije rutinu MmGetSystemRoutineAddress.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neuspech
 *  operace.
 */
NTSTATUS SSDTInfoInit(VOID)
{
  UNICODE_STRING uServiceTable;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("ssdtinfo.c: SSDTInfoInit()\n"));

  Status = GetServiceTableOffset();
  if (NT_SUCCESS(Status)) {
    RtlInitUnicodeString(&uServiceTable, SERVICE_TABLE_NAME);
    ServiceTable = MmGetSystemRoutineAddress(&uServiceTable);
    if (ServiceTable == NULL) {
      SERVICE_TABLE_OFFSET = 0;
      Status = STATUS_NOT_FOUND;
    }
  }

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


/** Provadi uklid po modulu pro ziskavani informaci o tabulkach systemovych
 *  volani.
 *
 *  Protoze tento modul pri sve inicializaci zadnym zpusobem nemeni datove
 *  struktury jadra, tato rutina vlastne nedela nic.
 */
VOID SSDTInfoFinit(VOID)
{
  KdPrint(("ssdtinfo.c: SSDTInfoFinit()\n"));

  KdPrint(("ssdtinfo.c: SSDTInfoFinit(-)\n"));
  return;
}
