
/**
 * @file tests.c
 *
 * Implementace nekolika testu, ktere maji demonstrovat vlastnosti chranenych
 *  procesu. Konkretne se jedna o tyto testy:
 *  - pokus o ziskani pristupu k objektu ciloveho procesu s ruznym opravnenim
 *  - pokus o ziskani ruznych pristupu k vlaknum ciloveho procesu.
 * 
 *  Pritom test skonci uspechem, pokud je pristup k danemu objektu s danym
 *  opravnenim odepren. Pokud se podari handle ziskat, je test povazovan za
 *  neuspesny. Nepodari-li se handle ziskat, ale duvodem neni odepreni pristupu,
 *  test konci chybou.
 */

#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h>
#include "../../shared/common.h"
#include "tests.h"

// Definice pristupovach prav pro proces. Pri testu pristupu k objektu procesu
// se postupne zkousi ziskat tato opravneni.
static const DWORD ProcessAccessRights [] = {
  SYNCHRONIZE,
  WRITE_DAC,
  WRITE_OWNER,
  READ_CONTROL,
  PROCESS_CREATE_PROCESS,
  PROCESS_CREATE_THREAD,
  PROCESS_DUP_HANDLE,
  PROCESS_QUERY_INFORMATION,
  PROCESS_QUERY_LIMITED_INFORMATION,
  PROCESS_SET_INFORMATION,
  PROCESS_SET_QUOTA,
  PROCESS_SUSPEND_RESUME,
  PROCESS_TERMINATE,
  PROCESS_VM_OPERATION,
  PROCESS_VM_READ,
  PROCESS_VM_WRITE
};

// Nazvy jednotlivych testu pro proces. Jsou pojmenovany podle opravneni, ktera
// se snaz k cilovemu procesu ziskat.
static const PCHAR ProcessAccessRightsStrings [] = {
  "SYNCHRONIZE",
  "WRITE_DAC",
  "WRITE_OWNER",
  "READ_CONTROL",
  "PROCESS_CREATE_PROCESS",
  "PROCESS_CREATE_THREAD",
  "PROCESS_DUP_HANDLE",
  "PROCESS_QUERY_INFORMATION",
  "PROCESS_QUERY_LIMITED_INFORMATION",
  "PROCESS_SET_INFORMATION",
  "PROCESS_SET_QUOTA",
  "PROCESS_SUSPEND_RESUME",
  "PROCESS_TERMINATE",
  "PROCESS_VM_OPERATION",
  "PROCESS_VM_READ",
  "PROCESS_VM_WRITE"
};

// Seznam pristupovych prav k objektu vkakna. Pri testovani pristupu k vlaknum
// (potencialne chraneneho) procesu se program snazi postupne ziskat k vlaknum
// ciloveho procesu handle postupne s temito pravy.
static const DWORD ThreadAccessRights [] = {
  SYNCHRONIZE,
  WRITE_DAC,
  WRITE_OWNER,
  READ_CONTROL,
  THREAD_DIRECT_IMPERSONATION,
  THREAD_GET_CONTEXT,
  THREAD_IMPERSONATE,
  THREAD_QUERY_INFORMATION,
  THREAD_QUERY_LIMITED_INFORMATION,
  THREAD_SET_CONTEXT,
  THREAD_SET_INFORMATION,
  THREAD_SET_LIMITED_INFORMATION,
  THREAD_SET_THREAD_TOKEN,
  THREAD_SUSPEND_RESUME,
  THREAD_TERMINATE
};

// Nazvy jednotlivych testu vlaken. Jmenuji se podle toho, s jakym pristupem
// se program snazi k vlaknu ziskat handle.
static const PCHAR ThreadAccessRightsStrings [] = {
  "SYNCHRONIZE",
  "WRITE_DAC",
  "WRITE_OWNER",
  "READ_CONTROL",
  "THREAD_DIRECT_IMPERSONATION",
  "THREAD_GET_CONTEXT",
  "THREAD_IMPERSONATE",
  "THREAD_QUERY_INFORMATION",
  "THREAD_QUERY_LIMITED_INFORMATION",
  "THREAD_SET_CONTEXT",
  "THREAD_SET_INFORMATION",
  "THREAD_SET_LIMITED_INFORMATION",
  "THREAD_SET_THREAD_TOKEN",
  "THREAD_SUSPEND_RESUME",
  "THREAD_TERMINATE"
};



/** Testuje pristup k chranenemu procesu.
 *
 *  @param PID PID ciloveho procesu.
 *  @param Access Maska pristupovych prav, ktera budou od noveho handle
 *  vyzadovana.
 *  @param Success Adresa promenne, kterou funkce v pripade, ze test neskonci
 *  chybou, vyplni hodnotou indikujici jeho uspech ci neuspech.
 *
 *  @return Funkce vraci FALSE, pokud test skonci chybou. Jinak vraci TRUE.
 */
static BOOL TestOpenProcess(DWORD PID, DWORD Access, PBOOL Success)
{
  HANDLE hProcess = 0;
  BOOL Ret = FALSE;
  DebugMsg(("tests.c: TestOpenProcess(PID=%u; Access=%x; Success=0x%p)\n", PID, Access, Success));

  *Success = FALSE;
  hProcess = OpenProcess(Access, FALSE, PID);
  Ret = hProcess != NULL;
  if (!Ret) {
    Ret = (GetLastError() == ERROR_ACCESS_DENIED);
    *Success = Ret;
  } else CloseHandle(hProcess);

  DebugMsg(("tests.c: TestOpenProcess(-):%u, *Result=%u\n", Ret, *Success));
  return Ret;
}


/** Testuje pristup k vlaknu potencialne chraneneho procesu.
 *
 *  @param TID TID ciloveho vlakna.
 *  @param Access Maska pristupovych prav, ktera budou od noveho handle
 *  vyzadovana.
 *  @param Success Adresa promenne, kterou funkce v pripade, ze test neskonci
 *  chybou, vyplni hodnotou indikujici jeho uspech ci neuspech.
 *
 *  @return Funkce vraci FALSE, pokud test skonci chybou. Jinak vraci TRUE.
 */
static BOOL TestOpenThread(DWORD TID, DWORD Access, PBOOL Success)
{
  HANDLE hThread = 0;
  BOOL Ret = FALSE;
  DebugMsg(("tests.c: TestOpenThread(TID=%u; Access=%x; Success=0x%p)\n", TID, Access, Success));

  *Success= FALSE;
  hThread = OpenThread(Access, FALSE, TID);
  Ret = hThread != NULL;
  if (!Ret) {
    Ret = (GetLastError() == ERROR_ACCESS_DENIED);
    *Success = Ret;
  } else CloseHandle(hThread);

  DebugMsg(("tests.c: TestOpenThread(-):%u, *Result=%u\n", Ret, *Success));
  return Ret;
}


/** Otestuje, jaka opravneni lze k cilovemu procesu (a jeho vlaknum) ziskat.
 *
 *  Procedure postupne zkousi ziskat handle k objektu procesu s ruznym opravnenim.
 *  Po skonceni teto faze provadi podobny ukon s jeho vlakny.
 *
 *  @param ProcessId PID ciloveho procesu.
 */
VOID DoProcessTests(DWORD ProcessId)
{
  DWORD i = 0;
  THREADENTRY32 te;
  HANDLE hSnap = INVALID_HANDLE_VALUE;
  BOOL TestRes = FALSE;
  BOOL NoError = FALSE;
  DebugMsg(("tests.c: DoProcessTests(ProcessId=%u)\n", ProcessId));

  printf("Testy procesu pro %u:\n", ProcessId);
  for (i = 0; i < sizeof(ProcessAccessRights) / sizeof(DWORD); ++i) {
    NoError = TestOpenProcess(ProcessId, ProcessAccessRights[i], &TestRes);
    printf("*  %s\t%s\n", ProcessAccessRightsStrings[i], NoError ? (TestRes ? "Uspech" : "Neuspech") : "<Chyba>");
  }

  hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  if (hSnap != INVALID_HANDLE_VALUE) {
    te.dwSize = sizeof(te);    
    if (Thread32First(hSnap, &te)) {
      do {
      if (te.th32OwnerProcessID == ProcessId)
        DoThreadTests(te.th32ThreadID);

      } while (Thread32Next(hSnap, &te));
    }

    CloseHandle(hSnap);
  }

  DebugMsg(("tests.c: DoProcessTests(-)\n"));
  return;
}


/** Otestuje, jaka opravneni lze ziskat k cilovemu vlaknu potencialne chraneneho
 *  procesu.
 *
 *  Rutina se pokousi ziskat k cilovemu vlaknu handle s ruznym opravnenim.
 *
 *  @param ThreadId TID ciloveho vlakna.
 */
VOID DoThreadTests(DWORD ThreadId)
{
  DWORD i = 0;
  BOOL TestRes = FALSE;
  BOOL NoError = FALSE;
  DebugMsg(("tests.c: DoThreadTests(ThreadId=%u)\n", ThreadId));

  printf("Testy vlakna pro %u:\n", ThreadId);
  for (i = 0; i < sizeof(ThreadAccessRights) / sizeof(DWORD); ++i) {
    NoError = TestOpenThread(ThreadId, ThreadAccessRights[i], &TestRes);
    printf("*  %s\t%s\n", ThreadAccessRightsStrings[i], NoError ? (TestRes ? "Uspech" : "Neuspech") : "<Chyba>");
  }

  DebugMsg(("tests.c: DoThreadTests(-)\n"));
  return;
}
