#pragma once
#include <Windows.h>
#include <vector>
#include <iostream>
#include <WinInet.h>
#include <sstream>
#include <string>
#include <urlmon.h>
#include <Psapi.h>
#pragma comment(lib, "urlmon.lib")
#pragma comment(lib, "Wininet.lib") 

using namespace std;
#ifndef typeof
#define typeof(x) decltype(x)
#endif // !typeof

#ifndef PROPERTY
#define PROPERTY(t,n) __declspec( property (put = property__set_##n, get = property__get_##n)) t n
#define READONLY_PROPERTY(t,n) __declspec( property (get = property__get_##n) ) t n
#define WRITEONLY_PROPERTY(t,n) __declspec( property (put = property__set_##n) ) t n
#define GET(t,n) t property__get_##n() 
#define SET(t,n) void property__set_##n(const t& value)
#endif // !PROPERTY

#ifndef ENCRYPT_SYSTEM_TYPE
#define ENCRYPT_SYSTEM_TYPE(type,name)\
unsigned long long _##name;\
PROPERTY(type, name);\
GET(type, name)\
{\
	unsigned long long tmpval = _##name ^ ((unsigned long long)&_##name);\
	type result;\
	memcpy(&result, &tmpval, sizeof(result));\
	return result;\
}\
SET(type, name)\
{\
	unsigned long long tmpval = 0;\
	memcpy(&tmpval, &value, sizeof(value));\
	_##name = tmpval ^ ((unsigned long long)&_##name);\
}
#endif // !ENCRYPT_SYSTEM_TYPE

#ifndef _IMAGE_DEBUG_DIRECTORY_RAW_
#define _IMAGE_DEBUG_DIRECTORY_RAW_
typedef struct _IMAGE_DEBUG_DIRECTORY_RAW {
	uint8_t format[4];
	uint8_t PdbSignature[16];
	uint32_t PdbDbiAge;
	uint8_t ImageName[256];
	const char* GuidString()
	{
		char buf[128];
		sprintf_s(buf, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
			this->PdbSignature[3], this->PdbSignature[2],
			this->PdbSignature[1], this->PdbSignature[0],
			this->PdbSignature[5], this->PdbSignature[4],
			this->PdbSignature[7], this->PdbSignature[6],
			this->PdbSignature[8], this->PdbSignature[9],
			this->PdbSignature[10], this->PdbSignature[11],
			this->PdbSignature[12], this->PdbSignature[13],
			this->PdbSignature[14], this->PdbSignature[15]
		);
		return buf;
	}
} IMAGE_DEBUG_DIRECTORY_RAW, * PIMAGE_DEBUG_DIRECTORY_RAW;
#endif
#pragma region TPattern

class TPattern
{
	std::string sMask;
	std::string sPattern;
	std::vector<uint8_t> bytes;
	bool bIdaStyle = false;

private:
	void Parse()
	{
		if (bIdaStyle)
		{
			ParsePatternIDA();
		}
		else
		{
			ParsePattern();
		}
	}
	void ParsePattern()
	{
		auto uLen = sMask.length();
		for (uint32_t i = 0; i < uLen; i++)
		{
			if (i < uLen && sPattern[i] == 0)
			{
				sPattern[i] = (char)0xFF;
			}
			if (sMask[i] == '?')
			{
				bytes.push_back(0);
			}
			else
			{
				auto sub = sPattern.substr(i, 1);
				auto byte = uint8_t(sub[0]);
				bytes.push_back(byte);
			}
		}
	}

	void ParsePatternIDA()
	{
		auto uLen = sPattern.length();
		sMask.clear();
		int b = 0;
		char x, byteBuf = 0;
		for (uint32_t i = 0; i < uLen; i++)
		{
			char c = (char)sPattern[i];
			if (c >= '0' && c <= '9')
			{
				x = (0x0F & c);
				byteBuf += (b) ? x : x << 4;
				b++;
			}
			else if (c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')
			{
				if (c > 0x46)
				{
					c -= 0x20;
				}
				x = c - 0x37;
				byteBuf += (b) ? x : x << 4;
				b++;
			}
			else if (c == '?')
			{
				if (sPattern[i + 1] == '?')
				{
					i++;
				}
				bytes.push_back(0);
				sMask += '?';
			}
			if (b > 1)
			{
				bytes.push_back(byteBuf);
				b = 0;
				byteBuf = 0;
				sMask += 'x';
			}
		}
	}
public:

	TPattern(const char* sPattern)
	{
		bIdaStyle = true;
		this->sPattern = sPattern;
		Parse();
	}

	TPattern(const char* sPattern, const char* sMask)
	{
		this->sMask = sMask;
		bytes.resize(this->sMask.length());
		memcpy(&bytes[0], sPattern, bytes.size());
		Parse();
	}

	std::vector<uint8_t> Get()
	{
		return bytes;
	}

	std::string GetMask()
	{
		return sMask;
	}

	size_t Size()
	{
		return bytes.size();
	}
};
static void* Scan(void* pAddress, size_t size, TPattern* pattern)
{
	auto pat = pattern->Get();
	auto mask = pattern->GetMask();
	uint8_t* pByte = (uint8_t*)pAddress;
	size_t j = 0;
	for (size_t i = 0; i < size; i++)
	{
		if (IsBadReadPtr(pByte + i, pat.size()))
		{
			continue;
		}
		if (pByte[i] == pat[j] || mask[j] == '?')
		{
			j++;
			if (j == pat.size() - 1)
			{
				return (void*)((uintptr_t)pByte + i - pat.size() + 2);
			}
		}
		else
		{
			j = 0;
		}
	}
	return nullptr;
}
static void* ScanPattern(const char* szModule, const char* sPattern)
{
	TPattern* pattern = new TPattern(sPattern);
	OutputDebugStringA(pattern->GetMask().c_str());
	MODULEINFO mi{ };
	GetModuleInformation(GetCurrentProcess(), GetModuleHandleA(szModule), &mi, sizeof(mi));

	LPVOID dwBaseAddress = mi.lpBaseOfDll;
	const auto dwModuleSize = mi.SizeOfImage;
	return Scan(dwBaseAddress, dwModuleSize - pattern->Size(), pattern);
}
template<typename T> T FindPattern(const char* szModule, std::string pattern, DWORD_PTR SizeOfImage = NULL)
{
#define INRANGE(x,a,b)    (x >= a && x <= b) 
#define getBits( x )    (INRANGE((x&(~0x20)),'A','F') ? ((x&(~0x20)) - 'A' + 0xa) : (INRANGE(x,'0','9') ? x - '0' : 0))
#define getByte( x )    (getBits(x[0]) << 4 | getBits(x[1]))
	HMODULE hModule = GetModuleHandleA(szModule);
	const char* pat = pattern.c_str();
	DWORD_PTR firstMatch = 0;
	DWORD_PTR rangeStart = (T)hModule;
	MODULEINFO miModInfo;
	if (!SizeOfImage)
		GetModuleInformation(GetCurrentProcess(), hModule, &miModInfo, sizeof(MODULEINFO));
	DWORD_PTR rangeEnd = SizeOfImage ? rangeStart + SizeOfImage : rangeStart + (DWORD_PTR)miModInfo.SizeOfImage;
	for (DWORD_PTR pCur = rangeStart; pCur < rangeEnd; pCur++)
	{
		if (!*pat)
			return (T)firstMatch;

		if (*(PBYTE)pat == '\?' || *(BYTE*)pCur == getByte(pat))
		{
			if (!firstMatch)
				firstMatch = pCur;

			if (!pat[2])
				return (T)firstMatch;

			if (*(PWORD)pat == '\?\?' || *(PBYTE)pat != '\?')
				pat += 3;

			else
				pat += 2;    //one ?
		}
		else
		{
			pat = pattern.c_str();
			firstMatch = 0;
		}
	}
	return NULL;
}
#pragma endregion

static char* TCHAR2char(const wchar_t* STR)
{
	int size = WideCharToMultiByte(CP_ACP, 0, STR, -1, NULL, 0, NULL, FALSE);
	char* str = new char[sizeof(char) * size];
	WideCharToMultiByte(CP_ACP, 0, STR, -1, str, size, NULL, FALSE);
	return str;

}
static wchar_t* Char2TCHAR(const char* _char)
{
	int iLength;
	iLength = MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, NULL, 0);
	wchar_t* tchar = new wchar_t[sizeof(wchar_t) * iLength];
	MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, tchar, iLength);
	return tchar;
}
static string wstring_to_string(wstring s)
{
	return string(TCHAR2char(s.c_str()));
}
static wstring string_to_wstring(string s)
{
	return wstring(Char2TCHAR(s.c_str()));
}
static char* ToHex(const char* data, size_t len)
{
	const char* key = "0123456789ABCDEF";
	char* result = new char[(len * 2) + 1];
	result[(len * 2)] = '\0';
	for (int i = 0; i < len; i++)
	{
		result[i * 2] = key[((unsigned char)data[i]) / 0x10];
		result[(i * 2) + 1] = key[((unsigned char)data[i]) % 0x10];
	}
	return result;
}
static char* ToHex(const BYTE* data, size_t len)
{
	const char* key = "0123456789ABCDEF";
	char* result = new char[(len * 2) + 1];
	for (int i = 0; i < len; i++)
	{
		result[i * 2] = key[((unsigned char)data[i]) / 0x10];
		result[(i * 2) + 1] = key[((unsigned char)data[i]) % 0x10];
	}
	result[len * 2] = '\0';
	return result;
}
static vector<BYTE> ParseHex(const char* hex)
{
	vector<BYTE> result = vector<BYTE>();
	int len = strlen(hex);
	BYTE* buffer = new BYTE[len];
	bool ish = true;
	BYTE val = 0;
	for (int i = 0; i < len; i++)
	{
		bool isval = false;
		BYTE v = 0;
		if (hex[i] >= '0' && hex[i] <= '9')
		{
			v = (hex[i] - '0');
			isval = true;
		}
		else if (hex[i] >= 'A' && hex[i] <= 'F')
		{
			v = 10 + (hex[i] - 'A');
			isval = true;
		}
		else if (hex[i] >= 'a' && hex[i] <= 'f')
		{
			v = 10 + (hex[i] - 'a');
			isval = true;
		}
		if (isval)
		{
			if (ish)
			{
				val = (v << 4) | val;
			}
			else
			{
				val = v | val;
				result.push_back(val);
				val = 0;
			}
			ish = !ish;
		}
	}
	return result;
}
static initializer_list<char>* ReadAllBytes(const char* filePath)
{
	HANDLE pFile;
	DWORD fileSize;
	char* buffer, * tmpBuf;
	DWORD dwBytesRead, dwBytesToRead, tmpLen;

	pFile = CreateFileA(filePath,
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if (pFile == INVALID_HANDLE_VALUE)
	{
		CloseHandle(pFile);
		return nullptr;
	}
	fileSize = GetFileSize(pFile, NULL);
	buffer = (char*)malloc(fileSize);
	if (!buffer)
	{
		return nullptr;
	}
	ZeroMemory(buffer, fileSize);
	dwBytesToRead = fileSize;
	dwBytesRead = 0;
	tmpBuf = buffer;
	do {
		if (!ReadFile(pFile, tmpBuf, dwBytesToRead, &dwBytesRead, NULL) || dwBytesRead == 0)
			break;
		dwBytesToRead -= dwBytesRead;
		tmpBuf += dwBytesRead;

	} while (dwBytesToRead > 0);
	CloseHandle(pFile);
	return new initializer_list<char>(&buffer[0], &buffer[fileSize]);
}
static BOOL WriteAllBytes(const char* filePath, char* buffer, int len)
{
	HANDLE pFile;
	char* tmpBuf;
	DWORD dwBytesWrite, dwBytesToWrite;

	pFile = CreateFileA(filePath,
		GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (pFile == INVALID_HANDLE_VALUE)
	{
		CloseHandle(pFile);
		return FALSE;
	}

	dwBytesToWrite = len;
	dwBytesWrite = 0;

	tmpBuf = buffer;

	do {

		WriteFile(pFile, tmpBuf, dwBytesToWrite, &dwBytesWrite, NULL);

		dwBytesToWrite -= dwBytesWrite;
		tmpBuf += dwBytesWrite;

	} while (dwBytesToWrite > 0);

	CloseHandle(pFile);

	return TRUE;
}
static BOOL WriteAllBytes(const char* filePath, BYTE* buffer, int len)
{
	return WriteAllBytes(filePath, (char*)buffer, len);
}
static BOOL WriteAllBytes(const char* filePath, vector<BYTE> buffer)
{
	return WriteAllBytes(filePath, (char*)buffer.data(), buffer.size());
}
static BOOL WriteAllBytes(const char* filePath, initializer_list<BYTE> buffer)
{
	return WriteAllBytes(filePath, (char*)buffer.begin(), buffer.size());
}
static BOOL WriteAllBytes(const char* filePath, initializer_list<char> buffer)
{
	return WriteAllBytes(filePath, (char*)buffer.begin(), buffer.size());
}
static BOOL WriteAllText(const char* filePath, const char* text)
{
	return WriteAllBytes(filePath, (char*)text, strlen(text));
}
static vector<BYTE> HttpRequest(const char* url)
{
#define MAXSIZE 4096
	vector<BYTE> result = vector<BYTE>();
	BYTE* val = new BYTE[MAXSIZE];
	ULONG len = 1;
	HINTERNET hSession = InternetOpenA(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
	if (hSession != NULL)
	{
		HINTERNET hHttp = InternetOpenUrlA(hSession, url, NULL, 0, INTERNET_FLAG_DONT_CACHE, 0);

		if (hHttp != NULL)
		{
			while (len > 0)
			{
				InternetReadFile(hHttp, val, MAXSIZE - 1, &len);
				if (len > 0)
				{
					int ldx = result.size();
					result.resize(result.size() + len);
					memcpy(&result.operator[](ldx), &val[0], len);
				}
			}
			InternetCloseHandle(hHttp);
			hHttp = NULL;
		}
		InternetCloseHandle(hSession);
		hSession = NULL;
	}
	return result;
}

static string WebRequest(const char* url)
{
#define BLOCK_READ 4096
	IStream* stream;
	HRESULT result = URLOpenBlockingStreamA(0, url, &stream, 0, 0);
	if (result != 0)
	{
		return "";
	}
	char buffer[BLOCK_READ];
	unsigned long bytesRead;
	stringstream ss;
	stream->Read(buffer, BLOCK_READ, &bytesRead);
	while (bytesRead > 0U)
	{
		ss.write(buffer, (long long)bytesRead);
		stream->Read(buffer, BLOCK_READ, &bytesRead);
	}
	stream->Release();
	return ss.str();
}


static BYTE* _get_jmp_address(BYTE* ptr)
{
	if (*ptr == 0xE9)
	{
		return (ptr + 5) + (*(UINT32*)&ptr[1]);
	}
	return ptr;
}
static int _get_index(BYTE* ptr)
{
	if (*(UINT16*)ptr == 0x20FF)
	{
		return 0;
	}
	if (*(UINT16*)ptr == 0x60FF)
	{
		return ptr[2] / sizeof(void*);
	}
	if (*(UINT16*)ptr == 0xA0FF)
	{
		return *(UINT32*)&ptr[2] / sizeof(void*);
	}
	return -1;
}
template<typename T>
static int GetVTableIndex(T funcaddr)
{
	void* proc = (void*&)funcaddr;
	BYTE* addr = _get_jmp_address((BYTE*)proc);
	for (int i = 0; i < 32; i++)
	{
		if (addr[i] == 0xFF)
		{
			auto dx = _get_index(&addr[i]);
			if (dx >= 0) return dx;
		}
	}
	return -1;
}
static POINT GetMousePos(HWND Hwnd)
{
	POINT p;
	GetCursorPos(&p);
	ScreenToClient(Hwnd, &p);
	return p;
}
static bool InRect(POINT point, POINT LOC, SIZE SIZ)
{
	int xf = point.x - LOC.x;
	int yf = point.y - LOC.y;
	return  xf >= 0 && xf <= SIZ.cx &&
		yf >= 0 && yf <= SIZ.cy;
}

