
/**
 * @file: queryobject.c
 *
 * Tento modul obsahuje rutiny, ktere dovoluji zistak informace o objektech
 * exektuvy. Vsechny rutiny stoji na nativni funkci NtQueryObject, ktera
 * tyto moznosti nabizi. Neni implementovana zadna ochrana proti zatuhnuti
 * pri ziskavani jmena ciloveho objektu.
 */

#include <windows.h>
#include "..\shared\common.h"
#include "queryobject.h"



// Uchovava adresu rutiny NtQueryObject
static NTQUERYOBJECT *_NtQueryObject = NULL;
// Uchovava adresu rutiny NtQuerySystemInformation
static NTQUERYSYSTEMINFORMATION *_NtQuerySystemInformation = NULL;



/** Zjisti informace o vsech handle v systemu.
 *
 * Rutina k tomuto ucelu pouzije funkci NtQuerySystemInformation.
 *
 * @param HandleInfo Adresa, kam funkce v pripade uspechu zapise adresu
 * struktury SYSTEM_HANDLE_INFORMATION_EX s informacemi o kazdem handle
 * v systemu.
 *
 * @return Rutina vrati TRUE v pripade uspechu a FALSE jinak.
 */
BOOL EnumHandles(PSYSTEM_HANDLE_INFORMATION_EX *HandleInfo)
{
  BOOL Ret = FALSE;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  ULONG Size = 20;
  PSYSTEM_HANDLE_INFORMATION_EX shi = NULL;
  DebugMsg(("EnumHandles(HandleInfo=0x%p)\n", HandleInfo));

  *HandleInfo = AllocMem(Size);
  if (*HandleInfo != NULL) {
    Status = _NtQuerySystemInformation(SystemExtendedHandleInformation, *HandleInfo, Size, NULL);
    Ret = Status == STATUS_INFO_LENGTH_MISMATCH;
    while (Status == STATUS_INFO_LENGTH_MISMATCH && Ret) {
      if (shi != NULL)
        FreeMem(shi);

	  Size = Size * 2;
	  shi = (PSYSTEM_HANDLE_INFORMATION_EX)AllocMem(Size);
	  Ret = shi != NULL;
	  if (Ret) {
        Status = _NtQuerySystemInformation(SystemExtendedHandleInformation, shi, Size, NULL);
	    Ret = (Status == STATUS_SUCCESS || Status == STATUS_INFO_LENGTH_MISMATCH);
	  }
    }

    if (Ret) {
      *HandleInfo = shi;
    } else if (shi != NULL) {
      FreeMem(shi);
	  DebugMsg(("EnumHandles(): NtQuerySystemInformation failed: 0x%x\n", status));
    }
  } else DebugMsg(("EnumHandles(): Memory allocation failed\n"));

  DebugMsg(("EnumHandles(-):%u\n", Ret));
  return Ret;
}



/** Ziska handle k zadanemu procesu.
 *
 * @param ProcessId PID ciloveho procesu s opravnenim k duplikaci handle.
 *
 * @return Funkce vrati handle ciloveho procesu s opravnenim PROCESS_DUP_HANDLE.
 * V pripade neuspechu vrati NULL.
 */
HANDLE OpenTargetProcess(DWORD ProcessId)
{
  HANDLE Ret = NULL;
  DebugMsg(("OpenTargetProcess(ProcessId=%d)\n", ProcessId));

  Ret = OpenProcess(PROCESS_DUP_HANDLE, FALSE, ProcessId);
  if (Ret == NULL)
    DebugMsg(("OpenTargetProcess(): OpenProcess failed. Error %u\n", GetLastError()));

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


/** Duplikuje dane handle z ciloveho procesu.
 *
 * Duplikace neprobiha destruktivne; zdrojove handle neni uzavreno.
 * Zduplikovane handle ma stejna opravneni jako handle zdrojove.
 *
 * @param hSourceProcess Procesu, ze ktereho si volajici preje 
 * duplikovat.
 * @param hSourceObject Handle objektu, ktere si volajici
 *  preje zkopirovat do sveho procesu.
 *
 * @return V pripade uspechu funkce vrati hodnotu zduplikovaneho handle.
 * V pripade neuspechu vrati NULL.
 */
HANDLE DuplicateObject(HANDLE hSourceProcess, HANDLE hSourceObject)
{
  HANDLE Ret = NULL;
  BOOL Res = FALSE;
  DebugMsg(("DuplicateObject(hSourceProcess=0x%p; hSourceObject=0x%p)\n", hSourceProcess, hSourceObject));

  Res = DuplicateHandle(hSourceProcess, hSourceObject, GetCurrentProcess(), &Ret, GENERIC_READ, FALSE, DUPLICATE_SAME_ACCESS);
  if (!Res)
    DebugMsg(("DuplicateObject(): DuplicateHandle failed. Error %u\n", GetLastError()));

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


/** Zrusi handle procesu ziskane pomoci rutiny OpenTargetProcess.
 *
 * @param hTargetProcess Handle procesu, ktere si volajici preje zrusit.
 */
VOID CloseTargetProcess(HANDLE hTargetProcess)
{
  DebugMsg(("CloseTargetProcess(hTargetProcess=0x%p)\n", hTargetProcess));

  CloseHandle(hTargetProcess);

  DebugMsg(("CloseTargetProcess(-)\n"));
  return;
}



/** Ziska zakladni informace o objektu.
 *
 * @param hObject Handle objektu.
 * @param BasicInfo Adresa struktury OBJECT_BASIC_INFORMATION, kam funkce
 * v pripade uspechu zapise zakladni informace o objektu.
 *
 * @return Funkce vraci TRUE v pripade uspechu a FALSE v pripade neuspechu.
 */
BOOL QueryObjectBasicInfo(HANDLE hObject, POBJECT_BASIC_INFORMATION BasicInfo)
{
  BOOL Ret = FALSE;
  NTSTATUS status = 0xC0000001;
  DebugMsg(("QueryObjectBasicInfo(hObject=0x%p; BasicInfo=0x%p)\n", hObject, BasicInfo));

  status = _NtQueryObject(hObject, ObjectBasicInformation, BasicInfo, sizeof(OBJECT_BASIC_INFORMATION), NULL);
  Ret = status == 0;
  if (!Ret)
    DebugMsg(("QueryObjectBasicInfo(): NtQueryObject failed with status 0x%x\n", status));

  DebugMsg(("QueryObjectBasicInfo(-):%u\n", Ret));
  return Ret;
}



/** Vrati informace o typu ciloveho objektu.
 *
 * @param hObject Handle ciloveho objektu.
 * @param TypeInfo Adresa struktury OBJECT_TYPE_INFORMATION, kterou funkce
 * v pripade uspechu vyplni daty o typu objektu.
 * @param BufferLength Adresa promenne, ktera na vstupu udava velikost bufferu,
 * na ktery ukazuje adresa TypeInfo. Na vystupu tato rutina udava pocet bajtu
 * zapsanych do tohoto bufferu.
 *
 * @return Funkce vraci TRUE v pripade uspechu a FALSE v pripade neuspechu.
 */
BOOL QueryObjectTypeInfo(HANDLE hObject, POBJECT_TYPE_INFORMATION TypeInfo, PULONG BufferLength)
{
  BOOL Ret = FALSE;
  NTSTATUS status = 0xC0000001;
  DebugMsg(("QueryObjectTypeInfo(hObject=0x%p; TypeInfo=0x%p; BufferLength=0x%p)\n",
    hObject, TypeInfo, BufferLength));

  status = _NtQueryObject(hObject, ObjectTypeInformation, TypeInfo, *BufferLength, BufferLength);
  Ret = status == 0;
  if (!Ret)
    DebugMsg(("QueryObjectTypeInfo(): NtQueryObject failed with status 0x%x\n", status));
    
  DebugMsg(("QueryObjectTypeInfo(-):%u, *BufferLength=%u\n", Ret, *BufferLength));
  return Ret;
}



/** Vrati jmeno ciloveho objektu.
 *
 * @param hObject Handle ciloveho objektu.
 * @param NameInfo Adresa struktury OBJECT_NAME_INFORMATION, kterou funkce
 * v pripade uspechu vyplni jmenem objektu.
 * @param BufferLength Adresa promenne, ktera na vstupu udava velikost bufferu,
 * na ktery ukazuje adresa NameInfo. Na vystupu tato rutina udava pocet bajtu
 * zapsanych do tohoto bufferu.
 *
 * @return Funkce vraci TRUE v pripade uspechu a FALSE v pripade neuspechu.
 */
BOOL QueryObjectNameInfo(HANDLE hObject, POBJECT_NAME_INFORMATION NameInfo, PULONG BufferLength)
{
  BOOL Ret = FALSE;
  NTSTATUS status = 0xC0000001;
  DebugMsg(("QueryObjectNameInfo(hObject=0x%p; NameInfo=0x%p; BufferLength=0x%p; *BufferLength=%u)\n",
    hObject, NameInfo, BufferLength, *BufferLength));

  status = _NtQueryObject(hObject, ObjectNameInformation, NameInfo, *BufferLength, BufferLength);
  Ret = status == 0;
  if (!Ret)
    DebugMsg(("QueryObjectNameInfo(): NtQueryObject failed with status 0x%x\n", status));
    
  DebugMsg(("QueryObjectNameInfo(-):%u, *BufferLength=%u\n", Ret, *BufferLength));
  return Ret;
}


/** Vrati informace o jmene, typu a zakladnich charakteristikach objektu
 *  a daneho handle.
 *
 *  Rutina ke splneni sveho ukolu pouziva vyse popsanych funkci QueryObjectBasicInfo,
 *  QueryObjectTypeInfo a QueryObjectNameInfo.
 *
 * @param hObject Handle ciloveho objektu.
 * @param BasicInfo Adresa promenne, kam funkce v pripade uspechu zapise
 * adresu struktury OBJECT_BASIC_INFORMATION se zakladnimi informacemi
 * o objektu a handle. Volajici musi tuto strukturu dealokovat pomoci
 * FreeMem, jakmile s ni jiz nebude pracovat.
 * @param TypeInfo Adresa promenne, kam funkce v pripade uspechu zapise
 * adresu struktury OBJECT_TYPE_INFORMATION s udaji o typu objektu.
 * Volajici musi tuto strukturu dealokovat pomoci FreeMem, jakmile 
 * s ni jiz nebude pracovat.
 * @param NameInfo Adresa promenne, kam funkce v pripade uspechu zapise
 * adresu struktury OBJECT_NAME_INFORMATION se jmenem objektu.
 * Volajici musi tuto strukturu dealokovat pomoci 
 * FreeMem, jakmile s ni jiz nebude pracovat.
 *
 * @return Funkce vraci TRUE v pripade uspechu a FALSE v pripade 
 * neuspechu.
 */
BOOL QueryObjectInfo(HANDLE hObject, POBJECT_BASIC_INFORMATION *BasicInfo, POBJECT_TYPE_INFORMATION *TypeInfo, POBJECT_NAME_INFORMATION *NameInfo)
{
  BOOL Ret = FALSE;
  POBJECT_BASIC_INFORMATION obi;
  POBJECT_TYPE_INFORMATION oti = NULL;
  POBJECT_NAME_INFORMATION oni = NULL;
  DebugMsg(("QueryObjectInfo(hObject=0x%p; BasicInfo=0x%p; TypeInfo=0x%p; NameInfo=0x%p)\n",
    hObject, BasicInfo, TypeInfo, NameInfo));

  obi = AllocMem(sizeof(OBJECT_BASIC_INFORMATION));
  Ret = obi != NULL;
  if (Ret) {
    Ret = QueryObjectBasicInfo(hObject, obi);
    if (Ret) {
      obi->TypeInfoSize = 65536;
      obi->NameInfoSize = 65536;
	  oti = (POBJECT_TYPE_INFORMATION)AllocMem(obi->TypeInfoSize);
	  oni = (POBJECT_NAME_INFORMATION)AllocMem(obi->NameInfoSize);
      Ret = (oti != NULL && oni != NULL);
	  if (Ret) {
        Ret = QueryObjectTypeInfo(hObject, oti, &obi->TypeInfoSize);
        if (Ret) {
          Ret = QueryObjectNameInfo(hObject, oni, &obi->NameInfoSize);
          if (Ret) {
            if (NameInfo != NULL)
              *NameInfo = oni;
          }

          if (TypeInfo != NULL)
            *TypeInfo = oti;
        }
      }

      if (BasicInfo != NULL)
        *BasicInfo = obi;
    }

    if (!Ret) {
      if (obi != NULL)
        FreeMem(obi);

	  if (oti != NULL)
        FreeMem(oti);

	  if (oni != NULL)
        FreeMem(oni);
    }
  }

  DebugMsg(("QueryObjectInfo(-):%u\n", Ret));
  return Ret;
}


/** Zjisti statistiky o poctech a vlastnostech jednotlivych typu
 *  objektu.
 *
 * @param TypeInfo Adresa, kam funkce v pripade uspechu zapise
 * adresu struktury OBJECT_TYPES_INFORMATION s informacemi o
 * objektech jednotlivych typu a jejich vlastnostech.
 *
 * @return Funkce vraci TRUE v pripade uspechu, FALSE v pripade neuspechu.
 */
BOOL QueryObjectTypesInfo(POBJECT_TYPES_INFORMATION *TypesInfo)
{
  ULONG StartBufferLength = 28;
  ULONG BufferLength = 0;
  BOOL Ret = FALSE;
  NTSTATUS status = 0xC0000001;
  DebugMsg(("QueryObjectTypesInfo(TypesInfo=0x%p)\n", TypesInfo));

  *TypesInfo = (POBJECT_TYPES_INFORMATION)AllocMem(StartBufferLength);
  Ret = *TypesInfo != NULL;
  if (Ret) {
    status = _NtQueryObject(NULL, ObjectTypesInformation, TypesInfo, StartBufferLength, &BufferLength);
    Ret = status == STATUS_INFO_LENGTH_MISMATCH;
    if (Ret) {
      *TypesInfo = NULL;
	  while (status == STATUS_INFO_LENGTH_MISMATCH && Ret) {
        DebugMsg(("QueryObjectTypeInfo(): TypeInfo buffer is too small. Reallocating to %u bytes\n", BufferLength));
	    if (*TypesInfo != NULL)
          FreeMem(*TypesInfo);

        *TypesInfo = (POBJECT_TYPES_INFORMATION)AllocMem(BufferLength);
	    Ret = *TypesInfo != NULL;
	    if (Ret)
          status = _NtQueryObject(NULL, ObjectTypesInformation, *TypesInfo, BufferLength, &BufferLength);
	  }

	  Ret = status == STATUS_SUCCESS;
	  if (!Ret) {
        if (*TypesInfo != NULL) {
	      FreeMem(*TypesInfo);
		  *TypesInfo = NULL;
	    }

        DebugMsg(("QueryObjectTypesInfo(): NtQueryObject failed with status 0x%x\n", status));
	  }
    } else DebugMsg(("QueryObjectTypesInfo(): NtQueryObject failed with status 0x%x\n", status));
  } else DebugMsg(("QueryObjectTypesInfo(): Memory allocation failed failed\n"));

  DebugMsg(("QueryObjectTypesInfo(-):%u\n", Ret));
  return Ret;
}



/** Inicializuje modul pro zjistovani informaci o objektech exekutivy
 *  a jejich typech.
 *
 *  Rutina nalezne adresu funkce NtQueryObject v knihovne ntdll.dll. Take
 *  se pokusi ziskat opravneni SeDebugPrivilege. Na selhani druhe operace
 *  ale nezavisi uspech inicializace.
 *
 *  @return Funkce vrati TRUE v pripade uspechu a FALSE v pripade 
 *  neuspechu.
 */
BOOL QueryObjectInit(VOID)
{
  BOOL Ret = FALSE;
  HMODULE hNtdll = NULL;
  DebugMsg(("QueryObjectInit()\n"));

  Ret = EnablePrivilege(L"SeDebugPrivilege");
  if (!Ret)
    DebugMsg(("QueryObjectInit(): Failed to enable Debug privilege\n"));

  hNtdll = GetModuleHandleW(L"ntdll.dll");
  Ret = hNtdll != NULL;
  if (Ret) {
    _NtQueryObject = (NTQUERYOBJECT *)GetProcAddress(hNtdll, "NtQueryObject");
	_NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION *)GetProcAddress(hNtdll, "NtQuerySystemInformation");
    Ret = (_NtQueryObject != NULL && _NtQuerySystemInformation != NULL);
	DebugMsg(("_NtQuerySystemInformation: 0x%p, _NtQueryObject: 0x%p\n", _NtQuerySystemInformation, _NtQueryObject));
	if (!Ret)
      DebugMsg(("QueryObjectInit(): GetProcAddress failed. Error %u\n", GetLastError()));
  } else DebugMsg(("QueryObjectInit(): GetModuleHandleW failed. Error %u\n", GetLastError()));

  DebugMsg(("QueryObjectInit(-):%u\n", Ret));
  return Ret;
}


/** Uklizeci funkce modulu pro zjistovani vlastnosti objektu exekutivy.
 */
VOID QueryObjectFinit(VOID)
{
  DebugMsg(("queryobject.c: QueryObjectFinit()\n"));

  _NtQueryObject = NULL;

  DebugMsg(("querobject.c: QueryObjectFinit(-)\n"));
  return;
}
