/*	Benjamin DELPY `gentilkiwi`
	http://blog.gentilkiwi.com
	benjamin@gentilkiwi.com
	Licence : http://creativecommons.org/licenses/by/3.0/fr/
*/
#include "kull_m_process.h"

NTSTATUS kull_m_process_getProcessInformation(PKULL_M_PROCESS_ENUM_CALLBACK callBack, PVOID pvArg)
{
	NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH;
	ULONG sizeOfBuffer;
	PSYSTEM_PROCESS_INFORMATION buffer = NULL, myInfos;

	for(sizeOfBuffer = 0x1000; (status == STATUS_INFO_LENGTH_MISMATCH) && (buffer = (PSYSTEM_PROCESS_INFORMATION) API(KERNEL32, LocalAlloc)(LPTR, sizeOfBuffer)) ; sizeOfBuffer <<= 1)
	{
		status = API(NTDLL, NtQuerySystemInformation)(SystemProcessInformation, buffer, sizeOfBuffer, NULL);
		if(!NT_SUCCESS(status))
			API(KERNEL32, LocalFree)(buffer);
	}
	
	if(NT_SUCCESS(status))
	{
		for(myInfos = buffer; callBack(myInfos, pvArg) && myInfos->NextEntryOffset ; myInfos = (PSYSTEM_PROCESS_INFORMATION) ((PBYTE) myInfos + myInfos->NextEntryOffset));
		API(KERNEL32, LocalFree)(buffer);
	}
	return status;
}

BOOL CALLBACK kull_m_process_callback_pidForName(PSYSTEM_PROCESS_INFORMATION pSystemProcessInformation, PVOID pvArg)
{
	if(((PKULL_M_PROCESS_PID_FOR_NAME) pvArg)->isFound = API(NTDLL, RtlEqualUnicodeString)(&pSystemProcessInformation->ImageName, ((PKULL_M_PROCESS_PID_FOR_NAME) pvArg)->name, TRUE))
		*((PKULL_M_PROCESS_PID_FOR_NAME) pvArg)->processId = (DWORD) pSystemProcessInformation->UniqueProcessId;
	return !((PKULL_M_PROCESS_PID_FOR_NAME) pvArg)->isFound;
}

BOOL kull_m_process_getProcessIdForName(LPCWSTR name, PDWORD processId)
{
	BOOL status = FALSE;
	UNICODE_STRING uName;
	KULL_M_PROCESS_PID_FOR_NAME mySearch = {&uName, processId, FALSE};
	
	API(NTDLL, RtlInitUnicodeString)(&uName, name);
	if(NT_SUCCESS(kull_m_process_getProcessInformation(kull_m_process_callback_pidForName, &mySearch)))
		status = mySearch.isFound;
	return status;;
}

NTSTATUS kull_m_process_getVeryBasicModuleInformations(PKULL_M_MEMORY_HANDLE memory, PKULL_M_MODULE_ENUM_CALLBACK callBack, PVOID pvArg)
{
	NTSTATUS status = STATUS_DLL_NOT_FOUND;
	PLDR_DATA_TABLE_ENTRY pLdrEntry;
	PEB Peb; PEB_LDR_DATA LdrData; LDR_DATA_TABLE_ENTRY LdrEntry;
#ifdef _M_X64
	PLDR_DATA_TABLE_ENTRY_F32 pLdrEntry32;
	PEB_F32 Peb32; PEB_LDR_DATA_F32 LdrData32; LDR_DATA_TABLE_ENTRY_F32 LdrEntry32;
#endif
	ULONG i;
	KULL_M_MEMORY_HANDLE  hBuffer = {KULL_M_MEMORY_TYPE_OWN, NULL};
	KULL_M_MEMORY_ADDRESS aBuffer = {NULL, &hBuffer};
	KULL_M_MEMORY_ADDRESS aProcess= {NULL, memory};
	PBYTE aLire, fin;
	UNICODE_STRING moduleName;
	PMINIDUMP_MODULE_LIST pMinidumpModuleList;
	PMINIDUMP_STRING pMinidumpString;
	KULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION moduleInformation;
	BOOL continueCallback = TRUE;
	moduleInformation.DllBase.hMemory = memory;

	switch(memory->type)
	{
	case KULL_M_MEMORY_TYPE_OWN:
		if(kull_m_process_peb(memory, &Peb, FALSE))
		{
			for(pLdrEntry  = (PLDR_DATA_TABLE_ENTRY) ((PBYTE) (Peb.Ldr->InMemoryOrderModulevector.Flink) - FIELD_OFFSET(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
				(pLdrEntry != (PLDR_DATA_TABLE_ENTRY) ((PBYTE) (Peb.Ldr) + FIELD_OFFSET(PEB_LDR_DATA, InLoadOrderModulevector))) && continueCallback;
				pLdrEntry  = (PLDR_DATA_TABLE_ENTRY) ((PBYTE) (pLdrEntry->InMemoryOrderLinks.Flink ) - FIELD_OFFSET(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks))
					)
				{
					moduleInformation.DllBase.address = pLdrEntry->DllBase;
					moduleInformation.SizeOfImage = pLdrEntry->SizeOfImage;
					moduleInformation.NameDontUseOutsideCallback = &pLdrEntry->BaseDllName;
					continueCallback = callBack(&moduleInformation, pvArg);
				}
				status = STATUS_SUCCESS;
		}
#ifdef _M_X64
		moduleInformation.NameDontUseOutsideCallback = &moduleName;
		if(continueCallback && NT_SUCCESS(status) && kull_m_process_peb(memory, (PPEB) &Peb32, TRUE))
		{
			status = STATUS_PARTIAL_COPY;

			for(pLdrEntry32  = (PLDR_DATA_TABLE_ENTRY_F32) ((PBYTE) (((PEB_LDR_DATA_F32 *) Peb32.Ldr)->InMemoryOrderModulevector.Flink) - FIELD_OFFSET(LDR_DATA_TABLE_ENTRY_F32, InMemoryOrderLinks));
				(pLdrEntry32 != (PLDR_DATA_TABLE_ENTRY_F32) ((PBYTE) (Peb32.Ldr) + FIELD_OFFSET(PEB_LDR_DATA, InLoadOrderModulevector))) && continueCallback;
				pLdrEntry32  = (PLDR_DATA_TABLE_ENTRY_F32) ((PBYTE) (pLdrEntry32->InMemoryOrderLinks.Flink ) - FIELD_OFFSET(LDR_DATA_TABLE_ENTRY_F32, InMemoryOrderLinks))
				)
			{
				moduleInformation.DllBase.address = (PVOID) pLdrEntry32->DllBase;
				moduleInformation.SizeOfImage = pLdrEntry32->SizeOfImage;
				moduleName.Length = pLdrEntry32->BaseDllName.Length;
				moduleName.MaximumLength = pLdrEntry32->BaseDllName.MaximumLength;
				moduleName.Buffer = (PWSTR) pLdrEntry32->BaseDllName.Buffer;
				continueCallback = callBack(&moduleInformation, pvArg);
			}
			status = STATUS_SUCCESS;
		}
#endif
		break;

	case KULL_M_MEMORY_TYPE_PROCESS:
		moduleInformation.NameDontUseOutsideCallback = &moduleName;
		if(kull_m_process_peb(memory, &Peb, FALSE))
		{
			aBuffer.address = &LdrData; aProcess.address = Peb.Ldr;
			if(kull_m_memory_copy(&aBuffer, &aProcess, sizeof(LdrData)))
			{
				for(
					aLire  = (PBYTE) (LdrData.InMemoryOrderModulevector.Flink) - FIELD_OFFSET(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks),
					fin    = (PBYTE) (Peb.Ldr) + FIELD_OFFSET(PEB_LDR_DATA, InLoadOrderModulevector);
					(aLire != fin) && continueCallback;
					aLire  = (PBYTE) LdrEntry.InMemoryOrderLinks.Flink - FIELD_OFFSET(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)
					)
				{
					aBuffer.address = &LdrEntry; aProcess.address = aLire;
					if(continueCallback = kull_m_memory_copy(&aBuffer, &aProcess, sizeof(LdrEntry)))
					{
						moduleInformation.DllBase.address = LdrEntry.DllBase;
						moduleInformation.SizeOfImage = LdrEntry.SizeOfImage;
						moduleName = LdrEntry.BaseDllName;
						if(moduleName.Buffer = (PWSTR) API(KERNEL32, LocalAlloc)(LPTR, moduleName.MaximumLength))
						{
							aBuffer.address = moduleName.Buffer; aProcess.address = LdrEntry.BaseDllName.Buffer;
							if(kull_m_memory_copy(&aBuffer, &aProcess, moduleName.MaximumLength))
							{
								continueCallback = callBack(&moduleInformation, pvArg);
							}
							API(KERNEL32, LocalFree)(moduleName.Buffer);
						}
					}
				}
				status = STATUS_SUCCESS;
			}
		}
#ifdef _M_X64
		if(continueCallback && NT_SUCCESS(status) && kull_m_process_peb(memory, (PPEB) &Peb32, TRUE))
		{
			status = STATUS_PARTIAL_COPY;
			aBuffer.address = &LdrData32; aProcess.address = (PVOID) Peb32.Ldr;
			if(kull_m_memory_copy(&aBuffer, &aProcess, sizeof(LdrData32)))
			{
				for(
					aLire  = (PBYTE) (LdrData32.InMemoryOrderModulevector.Flink) - FIELD_OFFSET(LDR_DATA_TABLE_ENTRY_F32, InMemoryOrderLinks),
					fin    = (PBYTE) (Peb32.Ldr) + FIELD_OFFSET(PEB_LDR_DATA_F32, InLoadOrderModulevector);
					(aLire != fin) && continueCallback;
					aLire  = (PBYTE) LdrEntry32.InMemoryOrderLinks.Flink - FIELD_OFFSET(LDR_DATA_TABLE_ENTRY_F32, InMemoryOrderLinks)
					)
				{
					aBuffer.address = &LdrEntry32; aProcess.address = aLire;
					if(kull_m_memory_copy(&aBuffer, &aProcess, sizeof(LdrEntry32)))
					{
						moduleInformation.DllBase.address = (PVOID) LdrEntry32.DllBase;
						moduleInformation.SizeOfImage = LdrEntry32.SizeOfImage;
						
						moduleName.Length = LdrEntry32.BaseDllName.Length;
						moduleName.MaximumLength = LdrEntry32.BaseDllName.MaximumLength;
						if(moduleName.Buffer = (PWSTR) API(KERNEL32, LocalAlloc)(LPTR, moduleName.MaximumLength))
						{
							aBuffer.address = moduleName.Buffer; aProcess.address = (PVOID) LdrEntry32.BaseDllName.Buffer;
							if(kull_m_memory_copy(&aBuffer, &aProcess, moduleName.MaximumLength))
								continueCallback = callBack(&moduleInformation, pvArg);
							API(KERNEL32, LocalFree)(moduleName.Buffer);
						}
					}
				}
				status = STATUS_SUCCESS;
			}
		}
#endif
		break;

	case KULL_M_MEMORY_TYPE_PROCESS_DMP:
		moduleInformation.NameDontUseOutsideCallback = &moduleName;
		if(pMinidumpModuleList = (PMINIDUMP_MODULE_LIST) kull_m_minidump_stream(memory->pHandleProcessDmp->hMinidump, ModuleListStream))
		{
			for(i = 0; (i < pMinidumpModuleList->NumberOfModules) && continueCallback; i++)
			{
				moduleInformation.DllBase.address = (PVOID) pMinidumpModuleList->Modules[i].BaseOfImage;
				moduleInformation.SizeOfImage = pMinidumpModuleList->Modules[i].SizeOfImage;
				if(pMinidumpString = (PMINIDUMP_STRING) kull_m_minidump_RVAtoPTR(memory->pHandleProcessDmp->hMinidump, pMinidumpModuleList->Modules[i].ModuleNameRva))
				{
					API(NTDLL, RtlInitUnicodeString)(&moduleName, wcsrchr(pMinidumpString->Buffer, L'\\') + 1);
					continueCallback = callBack(&moduleInformation, pvArg);
				}
			}
			status = STATUS_SUCCESS;
		}
		break;

	default:
		status = STATUS_NOT_IMPLEMENTED;
		break;
	}

	return status;
}

BOOL CALLBACK kull_m_process_callback_moduleForName(PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION pModuleInformation, PVOID pvArg)
{
	if(((PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION_FOR_NAME) pvArg)->isFound = API(NTDLL, RtlEqualUnicodeString)(pModuleInformation->NameDontUseOutsideCallback, ((PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION_FOR_NAME) pvArg)->name, TRUE))
		*((PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION_FOR_NAME) pvArg)->informations = *pModuleInformation;
	return !((PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION_FOR_NAME) pvArg)->isFound;
}

BOOL CALLBACK kull_m_process_callback_moduleFirst(PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION pModuleInformation, PVOID pvArg)
{
	*(PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION) pvArg = *pModuleInformation;
	return FALSE;
}

BOOL kull_m_process_getVeryBasicModuleInformationsForName(PKULL_M_MEMORY_HANDLE memory, PCWSTR name, PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION informations)
{
	BOOL status = FALSE;
	UNICODE_STRING uName;
	KULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION_FOR_NAME mySearch = {&uName, informations, FALSE};

	if(name)
	{
		API(NTDLL, RtlInitUnicodeString)(&uName, name);
		if(NT_SUCCESS(kull_m_process_getVeryBasicModuleInformations(memory, kull_m_process_callback_moduleForName, &mySearch)))
			status = mySearch.isFound;
	}
	else
		status = NT_SUCCESS(kull_m_process_getVeryBasicModuleInformations(memory, kull_m_process_callback_moduleFirst, informations));
	return status;
}

NTSTATUS kull_m_process_getMemoryInformations(PKULL_M_MEMORY_HANDLE memory, PKULL_M_MEMORY_RANGE_ENUM_CALLBACK callBack, PVOID pvArg)
{
	NTSTATUS status = STATUS_NOT_FOUND;
	MEMORY_BASIC_INFORMATION memoryInfos;
	PBYTE currentPage, maxPage;
	PMINIDUMP_MEMORY_INFO_LIST maListeInfo = NULL;
	PMINIDUMP_MEMORY_INFO mesInfos = NULL;
	ULONG i;
	BOOL continueCallback = TRUE;
	
	if(!NT_SUCCESS(API(NTDLL, NtQuerySystemInformation)(KIWI_SystemMmSystemRangeStart, &maxPage, sizeof(PBYTE), NULL)))
		maxPage = MmSystemRangeStart;

	switch(memory->type)
	{
	case KULL_M_MEMORY_TYPE_OWN:
		for(currentPage = 0; (currentPage < maxPage) && continueCallback; currentPage += memoryInfos.RegionSize)
			if(API(KERNEL32, VirtualQuery)(currentPage, &memoryInfos, sizeof(MEMORY_BASIC_INFORMATION)) == sizeof(MEMORY_BASIC_INFORMATION))
				continueCallback = callBack(&memoryInfos, pvArg);
			else break;
		status = STATUS_SUCCESS;
		break;
	case KULL_M_MEMORY_TYPE_PROCESS:
		for(currentPage = 0; (currentPage < maxPage) && continueCallback; currentPage += memoryInfos.RegionSize)
			if(API(KERNEL32, VirtualQueryEx)(memory->pHandleProcess->hProcess, currentPage, &memoryInfos, sizeof(MEMORY_BASIC_INFORMATION)) == sizeof(MEMORY_BASIC_INFORMATION))
				continueCallback = callBack(&memoryInfos, pvArg);
			else break;
		status = STATUS_SUCCESS;
		break;
	case KULL_M_MEMORY_TYPE_PROCESS_DMP:
		if(maListeInfo = (PMINIDUMP_MEMORY_INFO_LIST) kull_m_minidump_stream(memory->pHandleProcessDmp->hMinidump, MemoryInfoListStream))
		{
			for(i = 0; (i < maListeInfo->NumberOfEntries) && continueCallback; i++)
			{
				mesInfos = (PMINIDUMP_MEMORY_INFO) ((PBYTE) maListeInfo + maListeInfo->SizeOfHeader + (i * maListeInfo->SizeOfEntry));
				memoryInfos.AllocationBase = (PVOID) mesInfos->AllocationBase;
				memoryInfos.AllocationProtect = mesInfos->AllocationProtect;
				memoryInfos.BaseAddress = (PVOID) mesInfos->BaseAddress;
				memoryInfos.Protect = mesInfos->Protect;
				memoryInfos.RegionSize = (SIZE_T) mesInfos->RegionSize;
				memoryInfos.State = mesInfos->State;
				memoryInfos.Type = mesInfos->Type;
				continueCallback = callBack(&memoryInfos, pvArg);
			}
			status = STATUS_SUCCESS;
		}
		break;
	default:
		break;
	}

	return status;
}

BOOL kull_m_process_peb(PKULL_M_MEMORY_HANDLE memory, PPEB pPeb, BOOL isWOW)
{
	BOOL status = FALSE;
	PROCESS_BASIC_INFORMATION processInformations;
	HANDLE hProcess = (memory->type == KULL_M_MEMORY_TYPE_PROCESS) ? memory->pHandleProcess->hProcess : API(KERNEL32, GetCurrentProcess)();
	KULL_M_MEMORY_HANDLE  hBuffer = {KULL_M_MEMORY_TYPE_OWN, NULL};
	KULL_M_MEMORY_ADDRESS aBuffer = {pPeb, &hBuffer};
	KULL_M_MEMORY_ADDRESS aProcess= {NULL, memory};
	PROCESSINFOCLASS info;
	ULONG szPeb, szBuffer, szInfos;
	LPVOID buffer;

#ifdef _M_X64
	if(isWOW)
	{
		info = ProcessWow64Information;
		szBuffer = sizeof(processInformations.PebBaseAddress);
		buffer = &processInformations.PebBaseAddress;
		szPeb = sizeof(PEB_F32);
	}
	else
	{
#endif
		info = ProcessBasicInformation;
		szBuffer = sizeof(processInformations);
		buffer = &processInformations;
		szPeb = sizeof(PEB);
#ifdef _M_X64
	}
#endif

	switch(memory->type)
	{
	case KULL_M_MEMORY_TYPE_OWN:
		if(!isWOW)
		{
			*pPeb = *API(NTDLL, RtlGetCurrentPeb)();
			status = TRUE;
			break;
		}
	case KULL_M_MEMORY_TYPE_PROCESS:
		if(NT_SUCCESS(API(NTDLL, NtQueryInformationProcess)(hProcess, info, buffer, szBuffer, &szInfos)) && (szInfos == szBuffer) && processInformations.PebBaseAddress)
		{
			aProcess.address = processInformations.PebBaseAddress;
			status = kull_m_memory_copy(&aBuffer, &aProcess, szPeb);
		}
		break;
	}
	return status;
}

BOOL kull_m_process_ntheaders(PKULL_M_MEMORY_ADDRESS pBase, PIMAGE_NT_HEADERS * pHeaders)
{
	BOOL status = FALSE;
	IMAGE_DOS_HEADER headerImageDos;
	KULL_M_MEMORY_HANDLE  hBuffer = {KULL_M_MEMORY_TYPE_OWN, NULL};
	KULL_M_MEMORY_ADDRESS aBuffer = {&headerImageDos, &hBuffer}, aRealNtHeaders = {NULL, &hBuffer}, aProcess= {NULL, pBase->hMemory};
	DWORD size;

	if(kull_m_memory_copy(&aBuffer, pBase, sizeof(IMAGE_DOS_HEADER)) && headerImageDos.e_magic == IMAGE_DOS_SIGNATURE)
	{
		aProcess.address = (PBYTE) pBase->address + headerImageDos.e_lfanew;
		if(aBuffer.address = API(KERNEL32, LocalAlloc)(LPTR, sizeof(DWORD) + IMAGE_SIZEOF_FILE_HEADER))
		{
			if(kull_m_memory_copy(&aBuffer, &aProcess, sizeof(DWORD) + IMAGE_SIZEOF_FILE_HEADER) && ((PIMAGE_NT_HEADERS) aBuffer.address)->Signature == IMAGE_NT_SIGNATURE);
			{
				size = (((PIMAGE_NT_HEADERS) aBuffer.address)->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) ? sizeof(IMAGE_NT_HEADERS32) : sizeof(IMAGE_NT_HEADERS64);
				if(aRealNtHeaders.address = (PIMAGE_NT_HEADERS) API(KERNEL32, LocalAlloc)(LPTR, size))
				{
					status = kull_m_memory_copy(&aRealNtHeaders, &aProcess, size);

					if(status)
						*pHeaders = (PIMAGE_NT_HEADERS) aRealNtHeaders.address;
					else
						API(KERNEL32, LocalFree)(aRealNtHeaders.address);
				}
			}
			API(KERNEL32, LocalFree)(aBuffer.address);
		}
	}
	return status;
}

BOOL kull_m_process_datadirectory(PKULL_M_MEMORY_ADDRESS pBase, DWORD entry, PDWORD pRva, PDWORD pSize, PWORD pMachine, PVOID *pData)
{
	BOOL status = FALSE;
	KULL_M_MEMORY_HANDLE  hBuffer = {KULL_M_MEMORY_TYPE_OWN, NULL};
	KULL_M_MEMORY_ADDRESS aBuffer = {NULL, &hBuffer};
	KULL_M_MEMORY_ADDRESS aProcess= *pBase;
	
	DWORD rva, size;

	PIMAGE_NT_HEADERS pNtHeaders;
	if(kull_m_process_ntheaders(pBase, &pNtHeaders))
	{
		if(pMachine)
			*pMachine = pNtHeaders->FileHeader.Machine;
		
		if(pNtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
		{
			rva = ((PIMAGE_NT_HEADERS32) pNtHeaders)->OptionalHeader.DataDirectory[entry].VirtualAddress;
			size = ((PIMAGE_NT_HEADERS32) pNtHeaders)->OptionalHeader.DataDirectory[entry].Size;
		}
		else
		{
			rva = ((PIMAGE_NT_HEADERS64) pNtHeaders)->OptionalHeader.DataDirectory[entry].VirtualAddress;
			size = ((PIMAGE_NT_HEADERS64) pNtHeaders)->OptionalHeader.DataDirectory[entry].Size;
		}
		
		if(pRva)
			*pRva = rva;
		if(pSize)
			*pSize = size;

		if(rva && size && pData)
		{
			if(*pData = API(KERNEL32, LocalAlloc)(LPTR, size))
			{
				aProcess.address = (PBYTE) pBase->address + rva;
				aBuffer.address = *pData;
				status = kull_m_memory_copy(&aBuffer, &aProcess, size);
				
				if(!status)
					API(KERNEL32, LocalFree)(*pData);
			}
		}
		API(KERNEL32, LocalFree)(pNtHeaders);
	}
	return status;
}

BOOL kull_m_process_create(KULL_M_PROCESS_CREATE_TYPE type, PCWSTR commandLine, DWORD processFlags, HANDLE hUserToken, DWORD logonFlags, PCWSTR user, PCWSTR domain, PCWSTR password, PPROCESS_INFORMATION pProcessInfos, BOOL autoCloseHandle)
{
	BOOL status = FALSE;
	DWORD iProcessFlags = CREATE_NEW_CONSOLE | processFlags;
	DWORD iLogonFlags = 0 | logonFlags;
	PWSTR dupCommandLine;
	PPROCESS_INFORMATION ptrProcessInfos;
	STARTUPINFO startupInfo;
	RtlZeroMemory(&startupInfo, sizeof(STARTUPINFO));
	startupInfo.cb = sizeof(STARTUPINFO);

	ptrProcessInfos = pProcessInfos ? pProcessInfos : (PPROCESS_INFORMATION) API(KERNEL32, LocalAlloc)(LPTR, sizeof(PROCESS_INFORMATION));

	if(dupCommandLine = _wcsdup(commandLine))
	{
		switch(type)
		{
		case KULL_M_PROCESS_CREATE_NORMAL:
			status = CreateProcess(NULL, dupCommandLine, NULL, NULL, FALSE, iProcessFlags, NULL, NULL, &startupInfo, ptrProcessInfos);
			break;
		case KULL_M_PROCESS_CREATE_USER:
			status = CreateProcessAsUser(hUserToken, NULL, dupCommandLine, NULL, NULL, FALSE, iProcessFlags, NULL, NULL, &startupInfo, ptrProcessInfos);
			break;
		/*case KULL_M_PROCESS_CREATE_TOKEN:
			status = CreateProcessWithTokenW(hUserToken, iLogonFlags, NULL, dupCommandLine, iProcessFlags, NULL, NULL, &startupInfo, ptrProcessInfos);
			break;*/
		case KULL_M_PROCESS_CREATE_LOGON:
			status = CreateProcessWithLogonW(user, domain, password, iLogonFlags, NULL, dupCommandLine, iProcessFlags, NULL, NULL, &startupInfo, ptrProcessInfos);
			break;
		}

		if(autoCloseHandle || !pProcessInfos)
		{
			API(KERNEL32, CloseHandle)(ptrProcessInfos->hThread);
			API(KERNEL32, CloseHandle)(ptrProcessInfos->hProcess);
		}

		if(!pProcessInfos)
			API(KERNEL32, LocalFree)(ptrProcessInfos);
		free(dupCommandLine);
	}
	return status;
}

NTSTATUS kull_m_process_getExportedEntryInformations(PKULL_M_MEMORY_ADDRESS address, PKULL_M_EXPORTED_ENTRY_ENUM_CALLBACK callBack, PVOID pvArg)
{
	PIMAGE_EXPORT_DIRECTORY pExportDir;
	DWORD rva, size, i, j, rvaFunc;
	KULL_M_PROCESS_EXPORTED_ENTRY exportedEntry;
	BOOL continueCallback = TRUE;

	if(kull_m_process_datadirectory(address, IMAGE_DIRECTORY_ENTRY_EXPORT, &rva, &size, &exportedEntry.machine, (PVOID *) &pExportDir))
	{
		exportedEntry.function.hMemory = exportedEntry.pRva.hMemory = address->hMemory;
		for(i = 0; (i < pExportDir->NumberOfFunctions) && continueCallback ; i++)
		{
			if(rvaFunc = ((PDWORD) ((PBYTE) pExportDir + (pExportDir->AddressOfFunctions - rva)))[i])
			{
				exportedEntry.pRva.address = (PBYTE) address->address + pExportDir->AddressOfFunctions + (i * sizeof(DWORD));
				exportedEntry.ordinal = i + 1;
				for(j = 0, exportedEntry.hint = 0, exportedEntry.name = NULL; (j < pExportDir->NumberOfNames) && !exportedEntry.name && continueCallback; j++)
				{
					if(i == ((PWORD) ((PBYTE) pExportDir + (pExportDir->AddressOfNameOrdinals - rva)))[j])
					{
						exportedEntry.name = (PSTR) ((PBYTE) pExportDir + (((PDWORD) ((PBYTE) pExportDir + (pExportDir->AddressOfNames - rva)))[j] - rva));
						exportedEntry.hint = j;
					}
				}

				if((rvaFunc < rva) || (rvaFunc >= (rva + size)))
				{
					exportedEntry.function.address = (PBYTE) address->address + rvaFunc;
					exportedEntry.redirect = NULL;
				}
				else
				{
					exportedEntry.function.address = NULL;
					exportedEntry.redirect = (PSTR) ((PBYTE) pExportDir + (rvaFunc - rva));
				}

				continueCallback = callBack(&exportedEntry, pvArg);
			}
		}
		API(KERNEL32, LocalFree)(pExportDir);
	}
	return STATUS_SUCCESS;
}

PSTR kull_m_process_getImportNameWithoutEnd(PKULL_M_MEMORY_ADDRESS base)
{
	CHAR sEnd = '\0';
	SIZE_T size;
	KULL_M_MEMORY_HANDLE  hBuffer = {KULL_M_MEMORY_TYPE_OWN, NULL};
	KULL_M_MEMORY_ADDRESS aStringBuffer = {NULL, &hBuffer}, aNullBuffer = {&sEnd, &hBuffer};
	KULL_M_MEMORY_SEARCH sMemory = {{{base->address, base->hMemory}, MAX_PATH}, NULL};

	if(kull_m_memory_search(&aNullBuffer, sizeof(sEnd), &sMemory, FALSE))
	{
		size = (PBYTE) sMemory.result - (PBYTE) base->address + sizeof(char);
		if(aStringBuffer.address = API(KERNEL32, LocalAlloc)(LPTR, size))
			if(!kull_m_memory_copy(&aStringBuffer, base, size))
				aStringBuffer.address = API(KERNEL32, LocalFree)(aStringBuffer.address);
	}
	return (PSTR) aStringBuffer.address;
}

NTSTATUS kull_m_process_getImportedEntryInformations(PKULL_M_MEMORY_ADDRESS address, PKULL_M_IMPORTED_ENTRY_ENUM_CALLBACK callBack, PVOID pvArg)
{
	KULL_M_MEMORY_HANDLE  hBuffer = {KULL_M_MEMORY_TYPE_OWN, NULL};
	PVOID pLocalBuffer;
	PIMAGE_IMPORT_DESCRIPTOR pImportDir;
	ULONG sizeThunk;
	ULONGLONG OriginalFirstThunk, FirstThunk, ordinalPattern;
	KULL_M_MEMORY_ADDRESS aOriginalFirstThunk = {&OriginalFirstThunk, &hBuffer}, aFirstThunk = {&FirstThunk, &hBuffer};
	KULL_M_MEMORY_ADDRESS aProcOriginalFirstThunk = {NULL, address->hMemory}, aProcName = {NULL, address->hMemory};
	KULL_M_PROCESS_IMPORTED_ENTRY importedEntry;
	BOOL continueCallback = TRUE;

	importedEntry.pFunction.hMemory = address->hMemory;
	importedEntry.function.hMemory = address->hMemory;

	if(kull_m_process_datadirectory(address, IMAGE_DIRECTORY_ENTRY_IMPORT, NULL, NULL, &importedEntry.machine, &pLocalBuffer))
	{
		if(importedEntry.machine == IMAGE_FILE_MACHINE_I386)
		{
			sizeThunk = sizeof(IMAGE_THUNK_DATA32);
			ordinalPattern = IMAGE_ORDINAL_FLAG32;
		}
		else
		{
			sizeThunk = sizeof(IMAGE_THUNK_DATA64);
			ordinalPattern = IMAGE_ORDINAL_FLAG64;
		}
		
		for(pImportDir = (PIMAGE_IMPORT_DESCRIPTOR) pLocalBuffer ; pImportDir->Characteristics && continueCallback; pImportDir++)
		{
			aProcName.address = (PBYTE) address->address + pImportDir->Name;
			if(importedEntry.libname = kull_m_process_getImportNameWithoutEnd(&aProcName))
			{
				for(
					aProcOriginalFirstThunk.address = ((PBYTE) address->address + pImportDir->OriginalFirstThunk),
					importedEntry.pFunction.address = ((PBYTE) address->address + pImportDir->FirstThunk);

					(kull_m_memory_copy(&aOriginalFirstThunk, &aProcOriginalFirstThunk, sizeThunk) && kull_m_memory_copy(&aFirstThunk, &importedEntry.pFunction, sizeThunk)) && (OriginalFirstThunk && FirstThunk) ;

					aProcOriginalFirstThunk.address = ((PBYTE) aProcOriginalFirstThunk.address + sizeThunk), ((PDWORD) &OriginalFirstThunk)[1] = 0,
					importedEntry.pFunction.address = ((PBYTE) importedEntry.pFunction.address + sizeThunk), ((PDWORD) &FirstThunk)[1] = 0
					)
				{
					importedEntry.function.address = (PVOID) FirstThunk;
					if(OriginalFirstThunk & ordinalPattern)
					{
						importedEntry.name = NULL;
						importedEntry.ordinal = IMAGE_ORDINAL(OriginalFirstThunk);
					}
					else
					{
						aProcName.address = ((PIMAGE_IMPORT_BY_NAME) ((PBYTE) address->address + OriginalFirstThunk))->Name;
						importedEntry.name = kull_m_process_getImportNameWithoutEnd(&aProcName);
						importedEntry.ordinal = 0;
					}

					continueCallback = callBack(&importedEntry, pvArg);

					if(importedEntry.name)
						API(KERNEL32, LocalFree)(importedEntry.name);

				}
				API(KERNEL32, LocalFree)(importedEntry.libname);
			}
		}
		API(KERNEL32, LocalFree)(pLocalBuffer);
	}
	return TRUE;
}