/**
 * @file protect.c
 *
 * Rozhrani pro aplikae bezici v uzivatelskem rezimu pro praci s ovladacem
 * pproc.sys. Dovoluje menit typ procesu (chraneny/nechraneny).
 */

#include <stdio.h>
#include <windows.h>
#include <Strsafe.h>
#include "..\..\shared\common.h"
#include "scmDrivers.h"
#include "protect.h"

// kody IOCTL pozadavku pro ovladac pproc.sys
// Nastavi proces jako chraneny
#define IOCTL_PPROC_PROTECT_PROCESS            CTL_CODE(FILE_DEVICE_UNKNOWN, 0x01, METHOD_BUFFERED, FILE_ANY_ACCESS)
// Nastavi proces jako normalni proces (odstrani priznak chranenho procesu)
#define IOCTL_PPROC_UNPROTECT_PROCESS          CTL_CODE(FILE_DEVICE_UNKNOWN, 0x02, METHOD_BUFFERED, FILE_ANY_ACCESS)
// Zjisti, zda cilovy proces je chraneny ci ne
#define IOCTL_PPROC_IS_PROTECTED               CTL_CODE(FILE_DEVICE_UNKNOWN, 0x03, METHOD_BUFFERED, FILE_ANY_ACCESS)


// Handle ke komunikacnimu zarizeni ovladace
static HANDLE hDevice = INVALID_HANDLE_VALUE;



/** Posle pozadavek na nastaveni ciloveho procesu jako chraneneho.
 *
 *  @param ProcessId PID ciloveho procesu.
 *
 *  @return Funkce vraci TRUE v pripade uspechu a FALSE v pripade neuspechu.
 */
BOOL ProtectProcess(HANDLE ProcessId)
{
  DWORD Dummy = 0;
  BOOL Ret = FALSE;
  DebugMsg(("protect.c: ProtectProcess(ProcessId=%u)\n", ProcessId));

  Ret = DeviceIoControl(hDevice, IOCTL_PPROC_PROTECT_PROCESS, &ProcessId, sizeof(HANDLE), NULL, 0, &Dummy, NULL);

  DebugMsg(("protect.c: ProtectProcess(-):%u\n", Ret));
  return Ret;
}


/** Odstrani priznak chraneneho procesu u zadaneho procesu.
 *
 *  @param ProcessId PID ciloveho procesu.
 *
 *  @return Funkce vrati TRUE v pripade uspechu a FALSE v pripade neuspechu.
 */
BOOL UnprotectProcess(HANDLE ProcessId)
{
	DWORD Dummy = 0;
	BOOL Ret = FALSE;
	DebugMsg(("protect.c: UnprotectProcess(ProcessId=%u)\n", ProcessId));

	Ret = DeviceIoControl(hDevice, IOCTL_PPROC_UNPROTECT_PROCESS, &ProcessId, sizeof(HANDLE), NULL, 0, &Dummy, NULL);

	DebugMsg(("protect.c: UnprotectProcess(-):%u\n", Ret));
	return Ret;
}


/** Zjisti, zda cilovy proces je chraneny proces, nebo ne.
 *
 *  @param ProcessId PID ciloveho procesu.
 *  @param Result Adresa promenne, kam funkce v pripade uspechu napise stav
 *  stav priznaku chraneneho procesu (TRUE pro chranene procesy a FALSE pro
 *  obycejne procesy). V pripade neuspechu je obsah teto promenne nedefinovan.
 *
 *  @return Vrati TRUE v pripade uspechu a FALSE v pripade neuspechu.
 */
BOOL IsProtectedProcess(HANDLE ProcessId, PBOOL Result)
{
	DWORD Dummy = 0;
	BOOL Ret = FALSE;
	DebugMsg(("protect.c: IsProtectedProcess(ProcessId=%u)\n", ProcessId));

	*Result = FALSE;
	Ret = DeviceIoControl(hDevice, IOCTL_PPROC_IS_PROTECTED, &ProcessId, sizeof(HANDLE), Result, sizeof(BOOL), &Dummy, NULL);

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



/** Inicializuje uzivatelskou cast rozhrani pro praci s ovladacem pproc.sys.
 *
 *  Rutina napred sestroji jmeno souboru ovladace (predpoklada se umisteni
 *  ovladace v adresari se spustitelnym souborem programu) a ovaldac nainstaluje
 *  a nacte do pameti jadra. Pokud se toto podari, pripoji s k jeho komunikacnimu
 *  zarizeni.
 *
 *  @return Funkce vrati TRUE v priapde uspechu a FALSE v pripade neuspechu.
 */
BOOL ProtectModuleInit(VOID)
{
  PCHAR ExeFileName = NULL;
  SIZE_T DriverFileNameLength = 0;
  PWCHAR DriverFileName = NULL;
  PCHAR ExePath = NULL;
  DWORD Index = 0;
  DWORD LastPos = 0;
  BOOL Ret = FALSE;
  DebugMsg(("protect.c: ProtectModuleInif()\n"));

  Ret = _get_pgmptr(&ExeFileName) == 0;
  while (ExeFileName[Index] != '\0') {
    if (ExeFileName[Index] == '\\')
      LastPos = Index;

    ++Index;
  }

  ExePath = AllocMem(LastPos + 1);
  Ret = ExePath != NULL;
  if (Ret) {
    ZeroMemory(ExePath, LastPos + 1);
    CopyMemory(ExePath, ExeFileName, LastPos);
    DriverFileNameLength = (LastPos + strlen("\\pproc.sys") + 1) * sizeof(WCHAR);
    DriverFileName = AllocMem(DriverFileNameLength);
    Ret = DriverFileName != NULL;
    if (Ret) {
      ZeroMemory(DriverFileName, DriverFileNameLength);
      Ret = StringCbPrintfW(DriverFileName, DriverFileNameLength, L"%S\\pproc.sys", ExePath) == S_OK;
      if (Ret) {
        scmInstallDriver(L"pproc", DriverFileName);
        Ret = scmLoadDriver(L"pproc");
        if (Ret) {
          hDevice = CreateFileW(L"\\\\.\\pproc", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
          Ret = hDevice != INVALID_HANDLE_VALUE;
          if (!Ret) {
            scmUnloadDriver(L"pproc");
            scmUninstallDriver(L"pproc");
          }
        } else scmUninstallDriver(L"pproc");
      }

      FreeMem(DriverFileName);
    }

    FreeMem(ExePath);
  }

  DebugMsg(("protect.c: ProtectModuleInit(-):%u\n", Ret));
  return Ret;
}


/** Uklidi po modulu pro praci s ovladacem pproc.sys.
 *
 *  Rutina zavre handle ke komunikacnimu zarizeni ovladace, ovladac uvolni
 *  z pameti jadra a odinstaluje.
 */
VOID ProtectModuleFinit(VOID)
{
  DebugMsg(("protect.c: ProtectModuleFinit()\n"));

  CloseHandle(hDevice);
  scmUnloadDriver(L"pproc");
  scmUninstallDriver(L"pproc");

  DebugMsg(("protect.c: ProtectModuleFinit(-)\n"));
  return;
}
