#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <WbemIdl.h>
#include <TlHelp32.h>

//#include "vld.h"
#include "..\ActionEventEngine\monitor.h"

#include "process.h"
#include "Events.h"

// for poc
#include "..\ProcessInjector\ProcessInjector.h"

#pragma comment(lib, "wbemuuid.lib")

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

int wildcardCompare(const unsigned char *wild, const unsigned char *string);
void get_process_list_toolhelp(PRUNTIME pRuntime, PPROCESS_LIST_ELEMENT* list);

/* wmi */
void addToUniqueByNameList(PRUNTIME pRuntime, LPSTR strProcessName, DWORD dwProcessId);
void addToUniqueByPidList(PRUNTIME pRuntime, LPSTR strProcessName, DWORD dwProcessId);
void removeFromUniqueByNameList(PRUNTIME pRuntime, LPSTR strProcessName, DWORD dwProcessId);
void removeFromUniqueByPidList(PRUNTIME pRuntime, LPSTR strProcessName, DWORD dwProcessId);

HRESULT STDMETHODCALLTYPE SinkQueryInterface(IWbemObjectSink * This, REFIID riid, void** ppv)
{
	PRUNTIME pRuntime = ((Sink*)This)->pRuntime;
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	// (!memcmp(rguid1, rguid2, sizeof(GUID) TODO
	//if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IWbemObjectSink))
	
	/*OLECHAR asdf[50];
	StringFromGUID2(&IID_IUnknown, asdf, 50);
	OUTPUTDEBUG(asdf);*/

	if (!API_MEMCMP(riid, pStrings->strIID_IUnknown, sizeof(GUID)) || !API_MEMCMP(riid, pStrings->strIID_IWbemObjectSink, sizeof(GUID)))
	{
		*ppv = (Sink*)This;
		This->lpVtbl->AddRef(This);
		return WBEM_S_NO_ERROR;
	}
	else
		return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE SinkAddRef(IWbemObjectSink *this)
{
	// Increment the reference count (count member).
	++((Sink*)this)->m_lRef;
	
	//printf("addref: %d\n", ((Sink*)this)->m_lRef);

	// We're supposed to return the updated count.
	return(((Sink*)this)->m_lRef);
}

ULONG STDMETHODCALLTYPE SinkRelease(IWbemObjectSink *this)
{
	// Decrement the reference count.
	--((Sink*)this)->m_lRef;

	// If it's now zero, we can free IExample.
	if (((Sink*)this)->m_lRef == 0)
	{
		return(0);
	}

	//printf("release: %d\n", ((Sink*)this)->m_lRef);

	// We're supposed to return the updated count.
	return(((Sink*)this)->m_lRef);
}

HRESULT STDMETHODCALLTYPE SinkIndicateDeletion(IWbemObjectSink * This, long lObjectCount, IWbemClassObject** apObjArray)
{
	PRUNTIME pRuntime = ((Sink*)This)->pRuntime;
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	OUTPUTDEBUG(pStrings->strClose);
	
	HRESULT hr = S_OK, hr2 = S_OK;
	VARIANT vtProp;
	API_VARIANTINIT(&vtProp);
	
	//BYTE asdf[17];
	//memcpy(asdf, &IID_IWbemClassObject, 16);
	//asdf[16] = 0x0;
	//HEXDUMP("Wbem", asdf, 16);

	for (int i = 0; i < lObjectCount; i++)
	{
		hr = apObjArray[i]->lpVtbl->Get(apObjArray[i], API_SYSALLOCSTRING(pStrings->strTargetInstance), 0, &vtProp, 0, 0);

		if (!FAILED(hr))
		{
			IUnknown* str = vtProp.punkVal;
			hr = str->lpVtbl->QueryInterface(str, pStrings->strIID_IWbemClassObject, (void**)(&apObjArray[i]));
			if (SUCCEEDED(hr))
			{
				VARIANT name, pid;
				API_VARIANTINIT(&name);
				API_VARIANTINIT(&pid);

				hr = apObjArray[i]->lpVtbl->Get(apObjArray[i], pStrings->strLName, 0, &name, NULL, NULL);
				hr2 = apObjArray[i]->lpVtbl->Get(apObjArray[i], pStrings->strProcessId, 0, &pid, 0, 0);
				
				if (SUCCEEDED(hr) && SUCCEEDED(hr2))
				{
					if ((name.vt == VT_NULL) || (name.vt == VT_EMPTY) || (pid.vt == VT_NULL) || (pid.vt == VT_EMPTY))
						OUTPUTDEBUG(pStrings->strEmpty_name);
					else
					{
						OUTPUTDEBUG("[*] Deletion %S %d\n", name.bstrVal, pid.uintVal);

						/*	prepare process structure and remove it from the lists:	*/

						size_t nameSize = API_WCSLEN(name.bstrVal);
						LPSTR strProcessName = API_ALLOC(pRuntime, nameSize + 1);
						API__SNPRINTF_S(strProcessName, nameSize + 1, _TRUNCATE, pStrings->strConvertFormat, name.bstrVal);
						strProcessName[nameSize] = 0x0;

						/* a] */
						removeFromUniqueByPidList(pRuntime, strProcessName, pid.uintVal);

						/* b] */
						removeFromUniqueByNameList(pRuntime, strProcessName, pid.uintVal);

						//API_FREE(pRuntime, strProcessName);
												

					} // else
				} // if (SUCCEEDED(hr) && SUCCEEDED(hr2))
				//API_VARIANTCLEAR(&name);
				//API_VARIANTCLEAR(&pid);
			}
		}
		//API_VARIANTCLEAR(&vtProp);
	}

	return WBEM_S_NO_ERROR;
}

HRESULT STDMETHODCALLTYPE SinkIndicateCreation(IWbemObjectSink * This, long lObjectCount, IWbemClassObject** apObjArray)
{
	PRUNTIME pRuntime = ((Sink*)This)->pRuntime;
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	OUTPUTDEBUG(pStrings->strCreate);

	HRESULT hr = S_OK, hr2 = S_OK;
	VARIANT vtProp;
	API_VARIANTINIT(&vtProp);

	//BYTE asdf[17];
	//memcpy(asdf, &IID_IWbemClassObject, 16);
	//asdf[16] = 0x0;
	//HEXDUMP("Wbem", asdf, 16);

	for (int i = 0; i < lObjectCount; i++)
	{
		hr = apObjArray[i]->lpVtbl->Get(apObjArray[i], API_SYSALLOCSTRING(pStrings->strTargetInstance), 0, &vtProp, 0, 0);

		if (!FAILED(hr))
		{
			IUnknown* str = vtProp.punkVal;
			hr = str->lpVtbl->QueryInterface(str, pStrings->strIID_IWbemClassObject, (void**)(&apObjArray[i]));
			if (SUCCEEDED(hr))
			{
				VARIANT name, pid;
				API_VARIANTINIT(&name);
				API_VARIANTINIT(&pid);

				hr = apObjArray[i]->lpVtbl->Get(apObjArray[i], pStrings->strLName, 0, &name, NULL, NULL);
				hr2 = apObjArray[i]->lpVtbl->Get(apObjArray[i], pStrings->strProcessId, 0, &pid, 0, 0);

				if (SUCCEEDED(hr) && SUCCEEDED(hr2))
				{
					if ((name.vt == VT_NULL) || (name.vt == VT_EMPTY) || (pid.vt == VT_NULL) || (pid.vt == VT_EMPTY) )
						OUTPUTDEBUG(pStrings->strEmpty_name);
					else
					{

						OUTPUTDEBUG("[*] Creation %S %d\n", name.bstrVal, pid.uintVal);


						/*	prepare process structure and add it to the lists: */

						size_t nameSize = API_WCSLEN(name.bstrVal);
						LPSTR strProcessName = API_ALLOC(pRuntime, nameSize + 1);
						API__SNPRINTF_S(strProcessName, nameSize + 1, _TRUNCATE, pStrings->strConvertFormat, name.bstrVal);
						strProcessName[nameSize] = 0x0;

						/* a] */
						addToUniqueByPidList(pRuntime, strProcessName, pid.uintVal);

						/* b] */
						addToUniqueByNameList(pRuntime, strProcessName, pid.uintVal);

						//API_FREE(pRuntime, strProcessName);

						
					} // else
				} // if (SUCCEEDED(hr) && SUCCEEDED(hr2))
				//API_VARIANTCLEAR(&name);
				//API_VARIANTCLEAR(&pid);
			}
		}
		//API_VARIANTCLEAR(&vtProp);
	}

	return WBEM_S_NO_ERROR;
}

HRESULT STDMETHODCALLTYPE SinkSetStatus(__RPC__in IWbemObjectSink * This, LONG lFlags, HRESULT hResult, BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) {

	return 0;
}


void stop_process_creation_callback(PRUNTIME pRuntime)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	HRESULT hres = pVariables->pSvcCreation->lpVtbl->CancelAsyncCall(pVariables->pSvcCreation, pVariables->pStubSinkCreation);

	/* cleanup */
	pVariables->pSvcCreation->lpVtbl->Release(pVariables->pSvcCreation);
	pVariables->pLocCreation->lpVtbl->Release(pVariables->pLocCreation);

	pVariables->pUnsecAppCreation->lpVtbl->Release(pVariables->pUnsecAppCreation);
	pVariables->pStubUnkCreation->lpVtbl->Release(pVariables->pStubUnkCreation);

	pVariables->pSinkCreation->lpVtbl->Release(pVariables->pSinkCreation);
	pVariables->pStubSinkCreation->lpVtbl->Release(pVariables->pStubSinkCreation);

	//API_COUNINITIALIZE();

	API_FREE(pRuntime, pVariables->pSinkCreation);
}

void stop_process_deletion_callback(PRUNTIME pRuntime)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	HRESULT hres = pVariables->pSvcDeletion->lpVtbl->CancelAsyncCall(pVariables->pSvcDeletion, pVariables->pStubSinkDeletion);

	/* cleanup */
	pVariables->pSvcDeletion->lpVtbl->Release(pVariables->pSvcDeletion);
	pVariables->pLocDeletion->lpVtbl->Release(pVariables->pLocDeletion);

	pVariables->pUnsecAppDeletion->lpVtbl->Release(pVariables->pUnsecAppDeletion);
	pVariables->pStubUnkDeletion->lpVtbl->Release(pVariables->pStubUnkDeletion);

	pVariables->pSinkDeletion->lpVtbl->Release(pVariables->pSinkDeletion);
	pVariables->pStubSinkDeletion->lpVtbl->Release(pVariables->pStubSinkDeletion);

	//API_COUNINITIALIZE();

	API_FREE(pRuntime, pVariables->pSinkDeletion);
}

void start_process_creation_callback(PRUNTIME pRuntime)
{

	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	HRESULT hres;


	/* 1] init COM */
	hres = API_COINITIALIZEEX(0, COINIT_MULTITHREADED); //COINITBASE_MULTITHREADED
	if (FAILED(hres))
	{
		OUTPUTDEBUG("[*] Fail init %d\n", hres);
		return;
	}

	/* 2] set general COM security levels */
	hres = API_COINITIALIZESECURITY(
		NULL,
		-1,				// COM negotiates service
		NULL,			// Authentication services
		NULL,			// Reserved
		RPC_C_AUTHN_LEVEL_DEFAULT,		// Default authentication
		RPC_C_IMP_LEVEL_IMPERSONATE,	// Default impersonation
		NULL,							// Authentication info
		EOAC_NONE,						// Additional capabilities
		NULL							// Reserved		
		);

	/*if (FAILED(hres))
	{
		API_COUNINITIALIZE();
		return;
	}*/
	OUTPUTDEBUG("[*] security %d\n", hres);


	/* 3] obtain the initial locator to WMI */
	//IWbemLocator *pLoc = NULL;
	hres = API_COCREATEINSTANCE(pStrings->strCLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, pStrings->strIID_IWbemLocator, (LPVOID *)&pVariables->pLocCreation);

	if (FAILED(hres))
	{
		//API_COUNINITIALIZE();
		OUTPUTDEBUG("[*] fail locator wmi %d\n", hres);
		return;
	}

	/* 4] connect to WMI through the IWbemLocator::ConnectServer method */
	//IWbemServices *pSvc = NULL;

	/* connect to the local root\cimv2 namespace and obtain pointer */
	hres = pVariables->pLocCreation->lpVtbl->ConnectServer(
		pVariables->pLocCreation,
		API_SYSALLOCSTRING(pStrings->strROOT_CIMV2),
		NULL,
		NULL,
		NULL,
		0,
		NULL,
		NULL,
		&pVariables->pSvcCreation
		);

	if (FAILED(hres))
	{
		pVariables->pLocCreation->lpVtbl->Release(pVariables->pLocCreation);
		//API_COUNINITIALIZE();
		OUTPUTDEBUG("[*] fail connect server %d\n", hres);
		return;
	}

	//OUTPUTDEBUGRUNTIME(pRuntime, pStrings->strConnected);

	/* 5] set security levels on the proxy */
	hres = API_COSETPROXYBLANKET(
		(IUnknown *)pVariables->pSvcCreation,				// indicates the proxy to set
		RPC_C_AUTHN_WINNT,
		RPC_C_AUTHZ_NONE,
		NULL,				// server principal name
		RPC_C_AUTHN_LEVEL_CALL,	//
		RPC_C_IMP_LEVEL_IMPERSONATE,
		NULL,				// client identity
		EOAC_NONE			// proxy capabilities
		);

	if (FAILED(hres))
	{
		pVariables->pSvcCreation->lpVtbl->Release(pVariables->pSvcCreation);
		pVariables->pLocCreation->lpVtbl->Release(pVariables->pLocCreation);
		OUTPUTDEBUG("[*] fail set proxy blanket pSvcCreation %d\n", hres);
	}

	/* 6] receive event notification */
	IWbemObjectSinkVtbl* Sink_Vtbl = (IWbemObjectSinkVtbl*)API_ALLOC(pRuntime, sizeof(IWbemObjectSinkVtbl));
	Sink_Vtbl->QueryInterface = &SinkQueryInterface;
	Sink_Vtbl->AddRef = SinkAddRef;
	Sink_Vtbl->Release = SinkRelease;
	Sink_Vtbl->Indicate = SinkIndicateCreation;
	Sink_Vtbl->SetStatus = SinkSetStatus;


	//IUnsecuredApartment* pUnsecApp = NULL;

	//BYTE asdf[17];
	//memcpy(asdf, &CLSID_UnsecuredApartment, 16);
	//asdf[16] = 0x0;
	//HEXDUMP("Wbem", asdf, 16);

	//memcpy(asdf, &IID_IUnsecuredApartment, 16);
	//asdf[16] = 0x0;
	//HEXDUMP("Wbem", asdf, 16);

	hres = API_COCREATEINSTANCE(pStrings->strCLSID_UnsecuredApartment, NULL, CLSCTX_LOCAL_SERVER, pStrings->strIID_IUnsecuredApartment, (void**)&pVariables->pUnsecAppCreation);

	pVariables->pSinkCreation = (Sink*)API_ALLOC(pRuntime, sizeof(Sink));
	pVariables->pSinkCreation->lpVtbl = Sink_Vtbl;
	pVariables->pSinkCreation->m_lRef = 0;
	pVariables->pSinkCreation->bDone = 0;
	pVariables->pSinkCreation->pRuntime = pRuntime;
	OUTPUTDEBUG(pStrings->strPSink_p, pVariables->pSinkCreation);

	//IUnknown* pStubUnk = NULL;
	HRESULT hRes = pVariables->pUnsecAppCreation->lpVtbl->CreateObjectStub(pVariables->pUnsecAppCreation, (IUnknown*)pVariables->pSinkCreation, &pVariables->pStubUnkCreation);

	//IWbemObjectSink* pStubSink = NULL;
	pVariables->pStubUnkCreation->lpVtbl->QueryInterface(pVariables->pStubUnkCreation, pStrings->strIID_IWbemObjectSink, (void**)&pVariables->pStubSinkCreation);

	/*	the ExecNotificationQUeryAsyny method will call
	the EventQuery::Indicate method when an event occurs */

	//__InstanceDeletionEvent __InstanceCreationEvent
	hres = pVariables->pSvcCreation->lpVtbl->ExecNotificationQueryAsync(
		pVariables->pSvcCreation,
		API_SYSALLOCSTRING(pStrings->strWQL),
		API_SYSALLOCSTRING(pStrings->strInstanceCreationEvent),
		//API_SYSALLOCSTRING(L"WQL"),
		//API_SYSALLOCSTRING(L"SELECT * FROM  __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'WIn32_Process'"),
		WBEM_FLAG_SEND_STATUS,
		NULL,
		pVariables->pStubSinkCreation
		);

	/* check for errors */
	if (FAILED(hres))
	{
		OUTPUTDEBUG(pStrings->strExecNotificationQueryAsync, hres);
		return;
	}

}

void start_process_deletion_callback(PRUNTIME pRuntime)
{
	
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	HRESULT hres;


	/* 1] init COM */
	//hres = API_COINITIALIZEEX(0, COINIT_MULTITHREADED); //COINITBASE_MULTITHREADED
	//if (FAILED(hres))
	//{
	//	return;
	//}

	/* 2] set general COM security levels */
	//hres = API_COINITIALIZESECURITY(
	//	NULL,
	//	-1,				// COM negotiates service
	//	NULL,			// Authentication services
	//	NULL,			// Reserved
	//	RPC_C_AUTHN_LEVEL_DEFAULT,		// Default authentication
	//	RPC_C_IMP_LEVEL_IMPERSONATE,	// Default impersonation
	//	NULL,							// Authentication info
	//	EOAC_NONE,						// Additional capabilities
	//	NULL							// Reserved		
	//	);

	//if (FAILED(hres))
	//{
	//	API_COUNINITIALIZE();
	//	return;
	//}

	/* 3] obtain the initial locator to WMI */
	//IWbemLocator *pLoc = NULL;
	hres = API_COCREATEINSTANCE(pStrings->strCLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, pStrings->strIID_IWbemLocator, (LPVOID *)&pVariables->pLocDeletion);

	if (FAILED(hres))
	{
		//API_COUNINITIALIZE();
		return;
	}

	/* 4] connect to WMI through the IWbemLocator::ConnectServer method */
	//IWbemServices *pSvc = NULL;

	/* connect to the local root\cimv2 namespace and obtain pointer */
	hres = pVariables->pLocDeletion->lpVtbl->ConnectServer(
		pVariables->pLocDeletion,
		API_SYSALLOCSTRING(pStrings->strROOT_CIMV2),
		NULL,
		NULL,
		NULL,
		0,
		NULL,
		NULL,
		&pVariables->pSvcDeletion
		);

	if (FAILED(hres))
	{
		pVariables->pLocDeletion->lpVtbl->Release(pVariables->pLocDeletion);
		//API_COUNINITIALIZE();
		return;
	}

	//OUTPUTDEBUGRUNTIME(pRuntime, pStrings->strConnected);

	/* 5] set security levels on the proxy */
	hres = API_COSETPROXYBLANKET(
		(IUnknown *)pVariables->pSvcDeletion,				// indicates the proxy to set
		RPC_C_AUTHN_WINNT,
		RPC_C_AUTHZ_NONE,
		NULL,				// server principal name
		RPC_C_AUTHN_LEVEL_CALL,	//
		RPC_C_IMP_LEVEL_IMPERSONATE,
		NULL,				// client identity
		EOAC_NONE			// proxy capabilities
		);

	if (FAILED(hres))
	{
		pVariables->pSvcDeletion->lpVtbl->Release(pVariables->pSvcDeletion);
		pVariables->pLocDeletion->lpVtbl->Release(pVariables->pLocDeletion);
	}

	/* 6] receive event notification */
	IWbemObjectSinkVtbl* Sink_Vtbl = (IWbemObjectSinkVtbl*)API_ALLOC(pRuntime, sizeof(IWbemObjectSinkVtbl));
	Sink_Vtbl->QueryInterface = &SinkQueryInterface;
	Sink_Vtbl->AddRef = SinkAddRef;
	Sink_Vtbl->Release = SinkRelease;
	Sink_Vtbl->Indicate = SinkIndicateDeletion;
	Sink_Vtbl->SetStatus = SinkSetStatus;


	//IUnsecuredApartment* pUnsecApp = NULL;

	//BYTE asdf[17];
	//memcpy(asdf, &CLSID_UnsecuredApartment, 16);
	//asdf[16] = 0x0;
	//HEXDUMP("Wbem", asdf, 16);

	//memcpy(asdf, &IID_IUnsecuredApartment, 16);
	//asdf[16] = 0x0;
	//HEXDUMP("Wbem", asdf, 16);

	hres = API_COCREATEINSTANCE(pStrings->strCLSID_UnsecuredApartment, NULL, CLSCTX_LOCAL_SERVER, pStrings->strIID_IUnsecuredApartment, (void**)&pVariables->pUnsecAppDeletion);

	pVariables->pSinkDeletion = (Sink*)API_ALLOC(pRuntime, sizeof(Sink));
	pVariables->pSinkDeletion->lpVtbl = Sink_Vtbl;
	pVariables->pSinkDeletion->m_lRef = 0;
	pVariables->pSinkDeletion->bDone = 0;
	pVariables->pSinkDeletion->pRuntime = pRuntime;
	OUTPUTDEBUG(pStrings->strPSink_p, pVariables->pSinkDeletion);

	//IUnknown* pStubUnk = NULL;
	HRESULT hRes = pVariables->pUnsecAppDeletion->lpVtbl->CreateObjectStub(pVariables->pUnsecAppDeletion, (IUnknown*)pVariables->pSinkDeletion, &pVariables->pStubUnkDeletion);

	//IWbemObjectSink* pStubSink = NULL;
	pVariables->pStubUnkDeletion->lpVtbl->QueryInterface(pVariables->pStubUnkDeletion, pStrings->strIID_IWbemObjectSink, (void**)&pVariables->pStubSinkDeletion);

	/*	the ExecNotificationQUeryAsyny method will call
	the EventQuery::Indicate method when an event occurs */

	//__InstanceDeletionEvent __InstanceCreationEvent
	hres = pVariables->pSvcDeletion->lpVtbl->ExecNotificationQueryAsync(
		pVariables->pSvcDeletion,
		API_SYSALLOCSTRING(pStrings->strWQL),
		API_SYSALLOCSTRING(pStrings->strInstanceDeletionEvent),
		//API_SYSALLOCSTRING(L"WQL"),
		//API_SYSALLOCSTRING(L"SELECT * FROM  __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'WIn32_Process'"),
		WBEM_FLAG_SEND_STATUS,
		NULL,
		pVariables->pStubSinkDeletion
		);

	/* check for errors */
	if (FAILED(hres))
	{
		OUTPUTDEBUG(pStrings->strExecNotificationQueryAsync, hres);
		return;
	}
	
}

void get_process_list_wmi(PRUNTIME pRuntime)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	// result code from COM calls 
	HRESULT hr = 0, hr2 = 0;

	// COM iface pointers
	IWbemLocator*	locator = NULL;
	IWbemServices*	services = NULL;
	IEnumWbemClassObject* results = NULL;

	BSTR resource = API_SYSALLOCSTRING(pStrings->strROOT_CIMV2);
	BSTR language = API_SYSALLOCSTRING(pStrings->strWQL);
	BSTR query = API_SYSALLOCSTRING(pStrings->strWin32_Process); //  L"SELECT * FROM Win32_Process"

	// init COM
	hr = API_COINITIALIZEEX(0, COINIT_MULTITHREADED);
	OUTPUTDEBUG("[*] CoInitiEx %d\n", hr);

	//hr = API_COINITIALIZESECURITY(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
	//OUTPUTDEBUG("[*] ComInitSecurity %d\n", hr);

	// connect to WMI
	hr = API_COCREATEINSTANCE(pStrings->strCLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, pStrings->strIID_IWbemLocator, (LPVOID *)&locator);

	OUTPUTDEBUG("[*] CoCreateInstance %d\n", hr);

	hr = locator->lpVtbl->ConnectServer(locator, resource, NULL, NULL, NULL, 0, NULL, NULL, &services);

	OUTPUTDEBUG("[*] ConnectServer %d\n", hr);

	hr = CoSetProxyBlanket(
		services,                //Proxy
		RPC_C_AUTHN_DEFAULT,          //Authentication service 
		RPC_C_AUTHZ_DEFAULT,          //Authorization service 
		COLE_DEFAULT_PRINCIPAL,       //Server principal name used 
		// by authentication service
		RPC_C_AUTHN_LEVEL_DEFAULT,    //Authentication level
		RPC_C_IMP_LEVEL_IMPERSONATE,  //Impersonation level
		COLE_DEFAULT_AUTHINFO,       //Client identity
		EOAC_DEFAULT                  //Capability flags
		);


	OUTPUTDEBUG("[*] CoSetProxyBlanket %d\n", hr);

	// issue a WMI query
	hr = services->lpVtbl->ExecQuery(services, language, query, WBEM_FLAG_FORWARD_ONLY, NULL, &results);


	OUTPUTDEBUG("[*] ExecQuery %d\n", hr);

	// list the query results
	if (results != NULL) {
		IWbemClassObject *result = NULL;
		ULONG returnedCount = 0;

		// enumerate the retrieved objects
		// class Win32_Process 
		while ((hr = results->lpVtbl->Next(results, WBEM_INFINITE, 1, &result, &returnedCount)) == S_OK) {
			VARIANT name;
			VARIANT pid;

			hr = result->lpVtbl->Get(result, pStrings->strLName, 0, &name, 0, 0);
			hr2 = result->lpVtbl->Get(result, pStrings->strProcessId, 0, &pid, 0, 0); //pStrings->strProcessId

			if (SUCCEEDED(hr) && SUCCEEDED(hr2))
			{

				//OUTPUTDEBUGRUNTIME(pRuntime, pStrings->strFormat_stuff, name.bstrVal, pid.uintVal);
				OUTPUTDEBUG(pStrings->strFormat_stuff, name.bstrVal, pid.uintVal);

				/*	prepare process structure and add it to the lists:
				- a] first list contains all the processes, pid is the key
				- b] second list contains a single entry for process name, process name is the key
				*/

				size_t nameSize = API_WCSLEN(name.bstrVal);
				LPSTR strProcessName = API_ALLOC(pRuntime, nameSize + 1);
				API__SNPRINTF_S(strProcessName, nameSize + 1, _TRUNCATE, pStrings->strConvertFormat, name.bstrVal);
				strProcessName[nameSize] = 0x0;

				/* a] */
				addToUniqueByPidList(pRuntime, strProcessName, pid.uintVal);

				/* b] */
				addToUniqueByNameList(pRuntime, strProcessName, pid.uintVal);

				API_FREE(pRuntime, strProcessName);
			}
			// release the current result object
			result->lpVtbl->Release(result);
		}
	}

	// release WMI COM interfaces
	results->lpVtbl->Release(results);
	services->lpVtbl->Release(services);
	locator->lpVtbl->Release(locator);

	// unwind everything else we've allocated
	//API_COUNINITIALIZE();

	//API_SYSFREESTRING(query);
	//API_SYSFREESTRING(language);
	//API_SYSFREESTRING(resource);
}

void addToUniqueByNameList(PRUNTIME pRuntime, LPSTR strProcessNameParam, DWORD dwProcessIdParam)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	API_ENTERCRITICALSECTION(&pVariables->pProcessListCriticalSectionUniqueByName);

	/* find whether process is already on the list */
	PPROCESS_LIST_ELEMENT findMe = NULL;

	size_t processNameLength = API_STRLEN(strProcessNameParam) +1;

	HASH_FIND_STR(pRuntime, pVariables->pProcessListUniqueByName, strProcessNameParam, findMe);
	
	if (findMe == NULL)
	{
		PPROCESS_LIST_ELEMENT process = (PPROCESS_LIST_ELEMENT)API_ALLOC(pRuntime, sizeof(PROCESS_LIST_ELEMENT));
		SecureZeroMemory(process, sizeof(PROCESS_LIST_ELEMENT));

		process->strProcessName = API_STRDUP(strProcessNameParam);
		process->dwNumberOfInstances = 1;

		HASH_ADD_STR(pRuntime, pVariables->pProcessListUniqueByName, strProcessName, process);


		/* there's a change in process list, call the callback */
		API_ENTERCRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);
		PMONITORED_PROCESS current = NULL, tmp = NULL;

		HASH_ITER(pRuntime, hh, pVariables->pMonitoredProcessList, current, tmp)
		{
			if (wildcardCompare(current->name, strProcessNameParam))
			{

				OUTPUTDEBUG("[*] %s: addTo callback for %s\n", __FUNCTION__, strProcessNameParam);

				PMONITOR_THREAD_PARAMETERS parameters = (PMONITOR_THREAD_PARAMETERS) API_ALLOC(pRuntime, sizeof(MONITOR_THREAD_PARAMETERS));
				parameters->pRuntime = pRuntime;
				parameters->args = current->args;

				DWORD dwThreadId;

				API_CREATETHREAD(
					NULL,                    // default security attributes
					0,                       // use default stack size  
					current->callback,       // thread function name
					parameters,           // argument to thread function 
					0,                       // use default creation flags 
					&dwThreadId);		     // returns the thread identifie

				//current->callback(pRuntime, current->args);

				/* remove from pMonitoredProcessList, the callback will put the onend */
				HASH_DELETE(pRuntime, hh, pVariables->pMonitoredProcessList, current);
				API_FREE(pRuntime, current);
			}
		}


		API_LEAVECRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);
	}
	else
		findMe->dwNumberOfInstances += 1;

	API_LEAVECRITICALSECTION(&pVariables->pProcessListCriticalSectionUniqueByName);
}

void addToUniqueByPidList(PRUNTIME pRuntime, LPSTR strProcessName, DWORD dwProcessIdParam)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);


	/* a] check uniqueness, although it's a pid, so it should be unique */
	API_ENTERCRITICALSECTION(&pVariables->pProcessListCriticalSection);

	PPROCESS_LIST_ELEMENT findMe = NULL;


	HASH_FIND(pRuntime, hh, pVariables->pProcessList, &dwProcessIdParam, sizeof(DWORD), findMe);

	if (findMe == NULL)
	{
		PPROCESS_LIST_ELEMENT process = (PPROCESS_LIST_ELEMENT)API_ALLOC(pRuntime, sizeof(PROCESS_LIST_ELEMENT));
		SecureZeroMemory(process, sizeof(PROCESS_LIST_ELEMENT));

		process->strProcessName = API_STRDUP(strProcessName);
		process->dwProcessId = dwProcessIdParam;

		//HandleInjection(process->strProcessName, process->dwProcessId);

		HASH_ADD(pRuntime, hh, pVariables->pProcessList, dwProcessId, sizeof(DWORD), process);
	}
	//else
	//	DebugBreak();

	API_LEAVECRITICALSECTION(&pVariables->pProcessListCriticalSection);
}

void removeFromUniqueByNameList(PRUNTIME pRuntime, LPSTR strProcessNameParam, DWORD dwProcessIdParam)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	API_ENTERCRITICALSECTION(&pVariables->pProcessListCriticalSectionUniqueByName);

	PPROCESS_LIST_ELEMENT findMe = NULL;

	size_t processNameLength = API_STRLEN(strProcessNameParam) + 1;

	HASH_FIND_STR(pRuntime, pVariables->pProcessListUniqueByName, strProcessNameParam, findMe);

	if (findMe != NULL)
	{
		if (findMe->dwNumberOfInstances > 0)
		{
			findMe->dwNumberOfInstances -= 1;
			
			if (findMe->dwNumberOfInstances == 0)
			{
				HASH_DELETE(pRuntime, hh, pVariables->pProcessListUniqueByName, findMe);
				// TODO: fix leak
				//API_FREE(pRuntime, findMe->strProcessName);
				API_FREE(pRuntime, findMe);

				/* there's a change in process list, call the callback */
				API_ENTERCRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);
				PMONITORED_PROCESS current, tmp;

				HASH_ITER(pRuntime, hh, pVariables->pMonitoredProcessList, current, tmp)
				{
					if (wildcardCompare(current->name, strProcessNameParam))
					{
						OUTPUTDEBUG("[*] %s: removeFrom callback for %s\n", __FUNCTION__, strProcessNameParam);
						
						PMONITOR_THREAD_PARAMETERS parameters = (PMONITOR_THREAD_PARAMETERS)API_ALLOC(pRuntime, sizeof(MONITOR_THREAD_PARAMETERS));
						parameters->pRuntime = pRuntime;
						parameters->args = current->args;


						//current->callback(pRuntime, current->args);
						DWORD dwThreadId;

						API_CREATETHREAD(
							NULL,                    // default security attributes
							0,                       // use default stack size  
							current->callback,       // thread function name
							parameters,           // argument to thread function 
							0,                       // use default creation flags 
							&dwThreadId);		     // returns the thread identifie

						/* remove from pMonitoredProcessList, the callback will put the onbegin */
						HASH_DELETE(pRuntime, hh, pVariables->pMonitoredProcessList, current);
						API_FREE(pRuntime, current);

					}
				}


				API_LEAVECRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);
			}
		}
	}
	//else
	//	DebugBreak();

	API_LEAVECRITICALSECTION(&pVariables->pProcessListCriticalSectionUniqueByName);
}


void removeFromUniqueByPidList(PRUNTIME pRuntime, LPSTR strProcessName, DWORD dwProcessIdParam)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	API_ENTERCRITICALSECTION(&pVariables->pProcessListCriticalSection);

	PPROCESS_LIST_ELEMENT findMe = NULL;

	HASH_FIND(pRuntime, hh, pVariables->pProcessList, &dwProcessIdParam, sizeof(DWORD), findMe);
	

	if (findMe != NULL)
	{
		HASH_DELETE(pRuntime, hh, pVariables->pProcessList, findMe);
		// TODO: fix leak
		//API_FREE(pRuntime, findMe->strProcessName);
		API_FREE(pRuntime, findMe);
	}
	//else
	//	DebugBreak();

	API_LEAVECRITICALSECTION(&pVariables->pProcessListCriticalSection);
}

/* EventsActionsEngine functions */

void* process_addwatch(PRUNTIME pRuntime, void* (WINAPI *callback)(void *), void *args, int flags, ...)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	API_ENTERCRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);

	PMONITORED_PROCESS process = (PMONITORED_PROCESS)API_ALLOC(pRuntime, sizeof(MONITORED_PROCESS));

	process->callback = (LPTHREAD_START_ROUTINE) callback;
	process->args = args;
	process->flags = flags;

	va_list va;
	va_start(va, flags);
	LPSTR strTmp = va_arg(va, CHAR*);
	process->name = API_STRDUP(strTmp);
	va_end(va);
	 
	OUTPUTDEBUG("[*] addwatch %s callback %p flags %d\n", process->name, process->callback, process->flags);
	HASH_ADD_PTR(pRuntime, pVariables->pMonitoredProcessList, name, process);

	API_LEAVECRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);

	return process;
}

void* process_delwatch(PRUNTIME pRuntime, void *entry)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	PMONITORED_PROCESS tmpProcess, process = (PMONITORED_PROCESS)entry;


	API_ENTERCRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);

	OUTPUTDEBUG("[*] searching %p\n", process->name);
	HASH_FIND_PTR(pRuntime, pVariables->pMonitoredProcessList, &process->name, tmpProcess);

	if (tmpProcess != NULL)
	{
		OUTPUTDEBUG("[*] deleting %p\n", tmpProcess->name);
		
		HASH_DEL(pRuntime, pVariables->pMonitoredProcessList, process);
	
		//TODO: fix leak
		//API_FREE(pRuntime, tmpProcess->name);
		API_FREE(pRuntime, tmpProcess);
	}
	//else
	//	DebugBreak();

	API_LEAVECRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);

	return NULL;
}

void monitored_process_dump(PRUNTIME pRuntime)
{
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	PMONITORED_PROCESS current, tmp;

	API_ENTERCRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);

	OUTPUTDEBUG("[*] Monitored process dump:\n");
	HASH_ITER(pRuntime, hh, pVariables->pMonitoredProcessList, current, tmp)
		OUTPUTDEBUG("\t%s %p\n", current->name, current->name);


	API_LEAVECRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);
}

/* toolhelp */
static int process_find(PPROCESS_LIST_ELEMENT head, LPSTR name);

DWORD WINAPI monitor_process_main(LPVOID lpParameter)
{
	PRUNTIME pRuntime = lpParameter;
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);


	while (TRUE)
	{
		PPROCESS_LIST_ELEMENT runningProcesses = NULL;
		PMONITORED_PROCESS monitoredProcessToDelete = NULL;


		/* fetch running process list */
		get_process_list_toolhelp(pRuntime, &runningProcesses);

		/* loop through monitored process list and check whether we've to do someting */
		API_ENTERCRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);

		PMONITORED_PROCESS current, tmp;
		HASH_ITER(pRuntime, hh, pVariables->pMonitoredProcessList, current, tmp)
		{

			BOOL bFirst = (!(current->flags & MONITOR_FLAG_NEG) && process_find(runningProcesses, current->name));
			BOOL bSecond = ((current->flags & MONITOR_FLAG_NEG) && !process_find(runningProcesses, current->name));

			if (  bFirst  ||  bSecond )
			{
				// hack for test
				if (current->flags == MONITOR_FLAG_NONE)
				{
					PPROCESS_LIST_ELEMENT listE, tmpE;
					HASH_ITER(pRuntime, hh, runningProcesses, listE, tmpE)
					{
						if (!strcmp(listE->strProcessName, "notepad.exe"))
							HandleInjection(listE->strProcessName, listE->dwProcessId);
					}
					

				}
				DWORD dwThreadId;

				PMONITOR_THREAD_PARAMETERS parameters = (PMONITOR_THREAD_PARAMETERS)API_ALLOC(pRuntime, sizeof(MONITOR_THREAD_PARAMETERS));
				parameters->pRuntime = pRuntime;
				parameters->args = current->args;

				/* start callback thread */
				API_CREATETHREAD(
					NULL,                    // default security attributes
					0,                       // use default stack size  
					current->callback,       // thread function name
					parameters,              // argument to thread function 
					0,                       // use default creation flags 
					&dwThreadId);		     // returns the thread identifier

				HASH_DEL(pRuntime, pVariables->pMonitoredProcessList, current);
				
			}
		}
	
		
		API_LEAVECRITICALSECTION(&pVariables->pMonitoredProcessCriticalSection);



		API_SLEEP(1000);
	}


	return 0;
}




static int process_find(PPROCESS_LIST_ELEMENT head, LPSTR name)
{
	PPROCESS_LIST_ELEMENT current, tmp;
	HASH_ITER(pRuntime, hh, head, current, tmp)
	{
		if (wildcardCompare(name, current->strProcessName))
			return 1;
	}
	return 0;
}


void get_process_list_toolhelp(PRUNTIME pRuntime, PPROCESS_LIST_ELEMENT* list)
{

	HANDLE hProcessSnap;
	PROCESSENTRY32 pe32;

	// take a snapshot of all the running processes
	hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hProcessSnap == INVALID_HANDLE_VALUE)
	{
		return;
	}

	// set the size of the structure
	pe32.dwSize = sizeof(PROCESSENTRY32);

	// get first process info
	if (!Process32First(hProcessSnap, &pe32))
	{
		CloseHandle(hProcessSnap);
		return;
	}

	// get info of all the running processes
	do
	{
		PPROCESS_LIST_ELEMENT findMe;
		HASH_FIND_STR(pRuntime, *list, pe32.szExeFile, findMe);
		
		if (findMe == NULL)
		{
			PPROCESS_LIST_ELEMENT process = (PPROCESS_LIST_ELEMENT)API_ALLOC(pRuntime, sizeof(PROCESS_LIST_ELEMENT));
			SecureZeroMemory(process, sizeof(PROCESS_LIST_ELEMENT));

			process->dwNumberOfInstances = 0;
			process->strProcessName = API_STRDUP(pe32.szExeFile);
			process->dwProcessId = pe32.th32ProcessID;

			HASH_ADD_STR(pRuntime, *list, strProcessName, process);
		}
		else
		{
			//OUTPUTDEBUG("[*] already got %s\n", findMe->strProcessName);
			findMe->dwNumberOfInstances += 1;
		}

	} while (Process32Next(hProcessSnap, &pe32));



	CloseHandle(hProcessSnap);

}

/* common */

int wildcardCompare(const unsigned char *wild, const unsigned char *string) {
	const unsigned char *cp = NULL, *mp = NULL;

	while ((*string) && (*wild != '*')) {
		if ((toupper((unsigned int)*wild) != toupper((unsigned int)*string)) && (*wild != '?')) {
			return 0;
		}
		wild++;
		string++;
	}

	while (*string) {
		if (*wild == '*') {
			if (!*++wild) {
				return 1;
			}
			mp = wild;
			cp = string + 1;
		}
		else if ((toupper((unsigned int)*wild) == toupper((unsigned int)*string)) || (*wild == '?')) {
			wild++;
			string++;
		}
		else {
			wild = mp;
			string = cp++;
		}
	}

	while (*wild == '*') {
		wild++;
	}
	return !*wild;
}

void monitor_process_start(PRUNTIME pRuntime)
{
	PVARIABLES_EVENTS pVariablesevent = pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables;

	/* wmi unique by pid */
	pVariablesevent->pProcessList = NULL;
	API_INITIALIZECRITICALSECTION(&pVariablesevent->pProcessListCriticalSection);

	/* wmi unique by name */
	pVariablesevent->pProcessListUniqueByName = NULL;
	API_INITIALIZECRITICALSECTION(&pVariablesevent->pProcessListCriticalSectionUniqueByName);


	/* toolhelp */
	//pVariablesevent->pProcessListToolhelp = NULL;
	//API_INITIALIZECRITICALSECTION(&pVariablesevent->pProcessListToolhelpCriticalSection);

	/* fetch initial process list */
	pVariablesevent->pMonitoredProcessList = NULL;
	API_INITIALIZECRITICALSECTION(&pVariablesevent->pMonitoredProcessCriticalSection);

	/* wmi init */
	//OutputDebugString("[*] GetProcessList\n");
	//get_process_list_wmi(pRuntime);

	//OutputDebugString("[*] Start creation callback\n");
	//start_process_creation_callback(pRuntime);

	//OutputDebugString("[*] start deletion callback\n");
	//start_process_deletion_callback(pRuntime);



	/* toolhelp :( */
	DWORD dwThreadId;

	/* start callback thread */
	API_CREATETHREAD(
		NULL,                    // default security attributes
		0,                       // use default stack size  
		monitor_process_main,       // thread function name
		pRuntime,              // argument to thread function 
		0,                       // use default creation flags 
		&dwThreadId);		     // returns the thread identifier

}