/* Benjamin DELPY `gentilkiwi` https://blog.gentilkiwi.com benjamin@gentilkiwi.com Licence : https://creativecommons.org/licenses/by/4.0/ */ #include "kull_m_dpapi.h" const GUID KULL_M_DPAPI_GUID_PROVIDER = CRYPTPROTECT_DEFAULT_PROVIDER; PKULL_M_DPAPI_BLOB kull_m_dpapi_blob_create(LPCVOID data/*, DWORD size*/) { PKULL_M_DPAPI_BLOB blob = NULL; if(data && (blob = (PKULL_M_DPAPI_BLOB) LocalAlloc(LPTR, sizeof(KULL_M_DPAPI_BLOB)))) { RtlCopyMemory(blob, data, FIELD_OFFSET(KULL_M_DPAPI_BLOB, szDescription)); blob->szDescription = (PWSTR) ((PBYTE) data + FIELD_OFFSET(KULL_M_DPAPI_BLOB, szDescription)); RtlCopyMemory(&blob->algCrypt, (PBYTE) blob->szDescription + blob->dwDescriptionLen, /*blob->dwDescriptionLen + */FIELD_OFFSET(KULL_M_DPAPI_BLOB, pbSalt) - FIELD_OFFSET(KULL_M_DPAPI_BLOB, algCrypt)); blob->pbSalt = (PBYTE) blob->szDescription + blob->dwDescriptionLen + FIELD_OFFSET(KULL_M_DPAPI_BLOB, pbSalt) - FIELD_OFFSET(KULL_M_DPAPI_BLOB, algCrypt); blob->dwHmacKeyLen = *(PDWORD) ((PBYTE) blob->pbSalt + blob->dwSaltLen); blob->pbHmackKey = (PBYTE) blob->pbSalt + blob->dwSaltLen + FIELD_OFFSET(KULL_M_DPAPI_BLOB, pbHmackKey) - FIELD_OFFSET(KULL_M_DPAPI_BLOB, dwHmacKeyLen); RtlCopyMemory(&blob->algHash, (PBYTE) blob->pbHmackKey + blob->dwHmacKeyLen, /*blob->dwHmacKeyLen + */FIELD_OFFSET(KULL_M_DPAPI_BLOB, pbHmack2Key) - FIELD_OFFSET(KULL_M_DPAPI_BLOB, algHash)); blob->pbHmack2Key = (PBYTE) blob->pbHmackKey + blob->dwHmacKeyLen + FIELD_OFFSET(KULL_M_DPAPI_BLOB, pbHmack2Key) - FIELD_OFFSET(KULL_M_DPAPI_BLOB, algHash); blob->dwDataLen = *(PDWORD) ((PBYTE) blob->pbHmack2Key + blob->dwHmac2KeyLen); blob->pbData = (PBYTE) blob->pbHmack2Key + blob->dwHmac2KeyLen + FIELD_OFFSET(KULL_M_DPAPI_BLOB, pbData) - FIELD_OFFSET(KULL_M_DPAPI_BLOB, dwDataLen); blob->dwSignLen = *(PDWORD) ((PBYTE) blob->pbData + blob->dwDataLen); blob->pbSign = (PBYTE) blob->pbData + blob->dwDataLen + FIELD_OFFSET(KULL_M_DPAPI_BLOB, pbSign) - FIELD_OFFSET(KULL_M_DPAPI_BLOB, dwSignLen); kull_m_string_ptr_replace(&blob->szDescription, blob->dwDescriptionLen); kull_m_string_ptr_replace(&blob->pbSalt, blob->dwSaltLen); kull_m_string_ptr_replace(&blob->pbHmackKey, blob->dwHmacKeyLen); kull_m_string_ptr_replace(&blob->pbHmack2Key, blob->dwHmac2KeyLen); kull_m_string_ptr_replace(&blob->pbData, blob->dwDataLen); kull_m_string_ptr_replace(&blob->pbSign, blob->dwSignLen); } return blob; } void kull_m_dpapi_blob_delete(PKULL_M_DPAPI_BLOB blob) { if(blob) { if(blob->szDescription) LocalFree(blob->szDescription); if(blob->pbSalt) LocalFree(blob->pbSalt); if(blob->pbHmackKey) LocalFree(blob->pbHmackKey); if(blob->pbHmack2Key) LocalFree(blob->pbHmack2Key); if(blob->pbData) LocalFree(blob->pbData); if(blob->pbSign) LocalFree(blob->pbSign); LocalFree(blob); } } void kull_m_dpapi_blob_descr(DWORD level, PKULL_M_DPAPI_BLOB blob) { kprintf(L"%*s" L"**BLOB**\n", level << 1, L""); if(blob) { kprintf(L"%*s" L" dwVersion : %08x - %u\n", level << 1, L"", blob->dwVersion, blob->dwVersion); kprintf(L"%*s" L" guidProvider : ", level << 1, L""); kull_m_string_displayGUID(&blob->guidProvider); kprintf(L"\n"); kprintf(L"%*s" L" dwMasterKeyVersion : %08x - %u\n", level << 1, L"", blob->dwMasterKeyVersion, blob->dwMasterKeyVersion); kprintf(L"%*s" L" guidMasterKey : ", level << 1, L""); kull_m_string_displayGUID(&blob->guidMasterKey); kprintf(L"\n"); kprintf(L"%*s" L" dwFlags : %08x - %u (", level << 1, L"", blob->dwFlags, blob->dwFlags); kull_m_dpapi_displayBlobFlags(blob->dwFlags); kprintf(L")\n"); kprintf(L"%*s" L" dwDescriptionLen : %08x - %u\n", level << 1, L"", blob->dwDescriptionLen, blob->dwDescriptionLen); kprintf(L"%*s" L" szDescription : %s\n", level << 1, L"", blob->szDescription); kprintf(L"%*s" L" algCrypt : %08x - %u (%s)\n", level << 1, L"", blob->algCrypt, blob->algCrypt, kull_m_crypto_algid_to_name(blob->algCrypt)); kprintf(L"%*s" L" dwAlgCryptLen : %08x - %u\n", level << 1, L"", blob->dwAlgCryptLen, blob->dwAlgCryptLen); kprintf(L"%*s" L" dwSaltLen : %08x - %u\n", level << 1, L"", blob->dwSaltLen, blob->dwSaltLen); kprintf(L"%*s" L" pbSalt : ", level << 1, L""); kull_m_string_wprintf_hex(blob->pbSalt, blob->dwSaltLen, 0); kprintf(L"\n"); kprintf(L"%*s" L" dwHmacKeyLen : %08x - %u\n", level << 1, L"", blob->dwHmacKeyLen, blob->dwHmacKeyLen); kprintf(L"%*s" L" pbHmackKey : ", level << 1, L""); kull_m_string_wprintf_hex(blob->pbHmackKey, blob->dwHmacKeyLen, 0); kprintf(L"\n"); kprintf(L"%*s" L" algHash : %08x - %u (%s)\n", level << 1, L"", blob->algHash, blob->algHash, kull_m_crypto_algid_to_name(blob->algHash)); kprintf(L"%*s" L" dwAlgHashLen : %08x - %u\n", level << 1, L"", blob->dwAlgHashLen, blob->dwAlgHashLen); kprintf(L"%*s" L" dwHmac2KeyLen : %08x - %u\n", level << 1, L"", blob->dwHmac2KeyLen, blob->dwHmac2KeyLen); kprintf(L"%*s" L" pbHmack2Key : ", level << 1, L""); kull_m_string_wprintf_hex(blob->pbHmack2Key, blob->dwHmac2KeyLen, 0); kprintf(L"\n"); kprintf(L"%*s" L" dwDataLen : %08x - %u\n", level << 1, L"", blob->dwDataLen, blob->dwDataLen); kprintf(L"%*s" L" pbData : ", level << 1, L""); kull_m_string_wprintf_hex(blob->pbData, blob->dwDataLen, 0); kprintf(L"\n"); kprintf(L"%*s" L" dwSignLen : %08x - %u\n", level << 1, L"", blob->dwSignLen, blob->dwSignLen); kprintf(L"%*s" L" pbSign : ", level << 1, L""); kull_m_string_wprintf_hex(blob->pbSign, blob->dwSignLen, 0); kprintf(L"\n\n"); } } void kull_m_dpapi_blob_quick_descr(DWORD level, LPCVOID data/*, DWORD size*/) { PKULL_M_DPAPI_BLOB blob; if(blob = kull_m_dpapi_blob_create(data)) { kull_m_dpapi_blob_descr(level, blob); kull_m_dpapi_blob_delete(blob); } } PKULL_M_DPAPI_MASTERKEY kull_m_dpapi_masterkey_create(LPCVOID data, DWORD64 size) { PKULL_M_DPAPI_MASTERKEY masterkey = NULL; if(data && (masterkey = (PKULL_M_DPAPI_MASTERKEY) LocalAlloc(LPTR, sizeof(KULL_M_DPAPI_MASTERKEY)))) { RtlCopyMemory(masterkey, data, FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY, pbKey)); masterkey->pbKey = (PBYTE) data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY, pbKey); masterkey->__dwKeyLen = (DWORD) size - FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY, pbKey); kull_m_string_ptr_replace(&masterkey->pbKey, masterkey->__dwKeyLen); } return masterkey; } void kull_m_dpapi_masterkey_delete(PKULL_M_DPAPI_MASTERKEY masterkey) { if(masterkey) { if(masterkey->pbKey) LocalFree(masterkey->pbKey); LocalFree(masterkey); } } void kull_m_dpapi_masterkey_descr(DWORD level, PKULL_M_DPAPI_MASTERKEY masterkey) { kprintf(L"%*s" L"**MASTERKEY**\n", level << 1, L""); if(masterkey) { kprintf(L"%*s" L" dwVersion : %08x - %u\n", level << 1, L"", masterkey->dwVersion, masterkey->dwVersion); kprintf(L"%*s" L" salt : ", level << 1, L""); kull_m_string_wprintf_hex(masterkey->salt, sizeof(masterkey->salt), 0); kprintf(L"\n"); kprintf(L"%*s" L" rounds : %08x - %u\n", level << 1, L"", masterkey->rounds, masterkey->rounds); kprintf(L"%*s" L" algHash : %08x - %u (%s)\n", level << 1, L"", masterkey->algHash, masterkey->algHash, kull_m_crypto_algid_to_name(masterkey->algHash)); kprintf(L"%*s" L" algCrypt : %08x - %u (%s)\n", level << 1, L"", masterkey->algCrypt, masterkey->algCrypt, kull_m_crypto_algid_to_name(masterkey->algCrypt)); kprintf(L"%*s" L" pbKey : ", level << 1, L""); kull_m_string_wprintf_hex(masterkey->pbKey, masterkey->__dwKeyLen, 0); kprintf(L"\n\n"); } } PBYTE kull_m_dpapi_masterkey_tobin(PKULL_M_DPAPI_MASTERKEY masterkey, OPTIONAL DWORD64 *size) { PBYTE data = NULL; if(data = (PBYTE) LocalAlloc(LPTR, FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY, pbKey) + masterkey->__dwKeyLen)) { RtlCopyMemory(data, masterkey, FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY, pbKey)); RtlCopyMemory(data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY, pbKey), masterkey->pbKey, masterkey->__dwKeyLen); if(size) *size = FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY, pbKey) + masterkey->__dwKeyLen; } return data; } PKULL_M_DPAPI_MASTERKEY_CREDHIST kull_m_dpapi_masterkeys_credhist_create(LPCVOID data, DWORD64 size) { PKULL_M_DPAPI_MASTERKEY_CREDHIST credhist = NULL; if(data && (credhist = (PKULL_M_DPAPI_MASTERKEY_CREDHIST) LocalAlloc(LPTR, sizeof(KULL_M_DPAPI_MASTERKEY_CREDHIST)))) RtlCopyMemory(credhist, data, sizeof(KULL_M_DPAPI_MASTERKEY_CREDHIST)); return credhist; } void kull_m_dpapi_masterkeys_credhist_delete(PKULL_M_DPAPI_MASTERKEY_CREDHIST credhist) { if(credhist) LocalFree(credhist); } void kull_m_dpapi_masterkeys_credhist_descr(DWORD level, PKULL_M_DPAPI_MASTERKEY_CREDHIST credhist) { kprintf(L"%*s" L"**CREDHIST INFO**\n", level << 1, L""); if(credhist) { kprintf(L"%*s" L" dwVersion : %08x - %u\n", level << 1, L"", credhist->dwVersion, credhist->dwVersion); kprintf(L"%*s" L" guid : ", level << 1, L""); kull_m_string_displayGUID(&credhist->guid); kprintf(L"\n\n"); } } PBYTE kull_m_dpapi_masterkeys_credhist_tobin(PKULL_M_DPAPI_MASTERKEY_CREDHIST credhist, OPTIONAL DWORD64 *size) { PBYTE data = NULL; if(data = (PBYTE) LocalAlloc(LPTR, sizeof(KULL_M_DPAPI_MASTERKEY_CREDHIST))) { RtlCopyMemory(data, credhist, sizeof(KULL_M_DPAPI_MASTERKEY_CREDHIST)); if(size) *size = sizeof(KULL_M_DPAPI_MASTERKEY_CREDHIST); } return data; } PKULL_M_DPAPI_MASTERKEY_DOMAINKEY kull_m_dpapi_masterkeys_domainkey_create(LPCVOID data, DWORD64 size) { PKULL_M_DPAPI_MASTERKEY_DOMAINKEY domainkey = NULL; if(data && (domainkey = (PKULL_M_DPAPI_MASTERKEY_DOMAINKEY) LocalAlloc(LPTR, sizeof(KULL_M_DPAPI_MASTERKEY_DOMAINKEY)))) { RtlCopyMemory(domainkey, data, FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY_DOMAINKEY, pbSecret)); domainkey->pbSecret = (PBYTE) data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY_DOMAINKEY, pbSecret); domainkey->pbAccesscheck = (PBYTE) data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY_DOMAINKEY, pbSecret) + domainkey->dwSecretLen; kull_m_string_ptr_replace(&domainkey->pbSecret, domainkey->dwSecretLen); kull_m_string_ptr_replace(&domainkey->pbAccesscheck, domainkey->dwAccesscheckLen); } return domainkey; } void kull_m_dpapi_masterkeys_domainkey_delete(PKULL_M_DPAPI_MASTERKEY_DOMAINKEY domainkey) { if(domainkey) { if(domainkey->pbSecret) LocalFree(domainkey->pbSecret); if(domainkey->pbAccesscheck) LocalFree(domainkey->pbAccesscheck); LocalFree(domainkey); } } void kull_m_dpapi_masterkeys_domainkey_descr(DWORD level, PKULL_M_DPAPI_MASTERKEY_DOMAINKEY domainkey) { kprintf(L"%*s" L"**DOMAINKEY**\n", level << 1, L""); if(domainkey) { kprintf(L"%*s" L" dwVersion : %08x - %u\n", level << 1, L"", domainkey->dwVersion, domainkey->dwVersion); kprintf(L"%*s" L" dwSecretLen : %08x - %u\n", level << 1, L"", domainkey->dwSecretLen, domainkey->dwSecretLen); kprintf(L"%*s" L" dwAccesscheckLen : %08x - %u\n", level << 1, L"", domainkey->dwAccesscheckLen, domainkey->dwAccesscheckLen); kprintf(L"%*s" L" guidMasterKey : ", level << 1, L""); kull_m_string_displayGUID(&domainkey->guidMasterKey); kprintf(L"\n"); kprintf(L"%*s" L" pbSecret : ", level << 1, L""); kull_m_string_wprintf_hex(domainkey->pbSecret, domainkey->dwSecretLen, 0); kprintf(L"\n"); kprintf(L"%*s" L" pbAccesscheck : ", level << 1, L""); kull_m_string_wprintf_hex(domainkey->pbAccesscheck, domainkey->dwAccesscheckLen, 0); kprintf(L"\n\n"); } } PBYTE kull_m_dpapi_masterkeys_domainkey_tobin(PKULL_M_DPAPI_MASTERKEY_DOMAINKEY domainkey, OPTIONAL DWORD64 *size) { PBYTE data = NULL; if(data = (PBYTE) LocalAlloc(LPTR, FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY_DOMAINKEY, pbSecret) + domainkey->dwSecretLen + domainkey->dwAccesscheckLen)) { RtlCopyMemory(data, domainkey, FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY_DOMAINKEY, pbSecret)); RtlCopyMemory(data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY_DOMAINKEY, pbSecret), domainkey->pbSecret, domainkey->dwSecretLen); RtlCopyMemory(data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY_DOMAINKEY, pbSecret) + domainkey->dwSecretLen, domainkey->pbAccesscheck, domainkey->dwAccesscheckLen); if(size) *size = FIELD_OFFSET(KULL_M_DPAPI_MASTERKEY_DOMAINKEY, pbSecret) + domainkey->dwSecretLen + domainkey->dwAccesscheckLen; } return data; } PKULL_M_DPAPI_MASTERKEYS kull_m_dpapi_masterkeys_create(LPCVOID data/*, DWORD size*/) { PKULL_M_DPAPI_MASTERKEYS masterkeys = NULL; if(data && (masterkeys = (PKULL_M_DPAPI_MASTERKEYS) LocalAlloc(LPTR, sizeof(KULL_M_DPAPI_MASTERKEYS)))) { RtlCopyMemory(masterkeys, data, FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey)); if(masterkeys->dwMasterKeyLen) masterkeys->MasterKey = kull_m_dpapi_masterkey_create((PBYTE) data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + 0, masterkeys->dwMasterKeyLen); if(masterkeys->dwBackupKeyLen) masterkeys->BackupKey = kull_m_dpapi_masterkey_create((PBYTE) data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + masterkeys->dwMasterKeyLen, masterkeys->dwBackupKeyLen); if(masterkeys->dwCredHistLen) masterkeys->CredHist = kull_m_dpapi_masterkeys_credhist_create((PBYTE) data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + masterkeys->dwMasterKeyLen + masterkeys->dwBackupKeyLen, masterkeys->dwCredHistLen); if(masterkeys->dwDomainKeyLen) masterkeys->DomainKey = kull_m_dpapi_masterkeys_domainkey_create((PBYTE) data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + masterkeys->dwMasterKeyLen + masterkeys->dwBackupKeyLen + masterkeys->dwCredHistLen, masterkeys->dwDomainKeyLen); } return masterkeys; } void kull_m_dpapi_masterkeys_delete(PKULL_M_DPAPI_MASTERKEYS masterkeys) { if(masterkeys) { if(masterkeys->MasterKey) kull_m_dpapi_masterkey_delete(masterkeys->MasterKey); if(masterkeys->BackupKey) kull_m_dpapi_masterkey_delete(masterkeys->BackupKey); if(masterkeys->CredHist) kull_m_dpapi_masterkeys_credhist_delete(masterkeys->CredHist); if(masterkeys->DomainKey) kull_m_dpapi_masterkeys_domainkey_delete(masterkeys->DomainKey); LocalFree(masterkeys); } } void kull_m_dpapi_masterkeys_descr(DWORD level, PKULL_M_DPAPI_MASTERKEYS masterkeys) { kprintf(L"%*s" L"**MASTERKEYS**\n", level << 1, L""); if(masterkeys) { kprintf(L"%*s" L" dwVersion : %08x - %u\n", level << 1, L"", masterkeys->dwVersion, masterkeys->dwVersion); kprintf(L"%*s" L" szGuid : {%.36s}\n", level << 1, L"", masterkeys->szGuid); kprintf(L"%*s" L" dwFlags : %08x - %u\n", level << 1, L"", masterkeys->dwFlags, masterkeys->dwFlags); kprintf(L"%*s" L" dwMasterKeyLen : %08x - %u\n", level << 1, L"", (DWORD) masterkeys->dwMasterKeyLen, (DWORD) masterkeys->dwMasterKeyLen); kprintf(L"%*s" L" dwBackupKeyLen : %08x - %u\n", level << 1, L"", (DWORD) masterkeys->dwBackupKeyLen, (DWORD) masterkeys->dwBackupKeyLen); kprintf(L"%*s" L" dwCredHistLen : %08x - %u\n", level << 1, L"", (DWORD) masterkeys->dwCredHistLen, (DWORD) masterkeys->dwCredHistLen); kprintf(L"%*s" L" dwDomainKeyLen : %08x - %u\n", level << 1, L"", (DWORD) masterkeys->dwDomainKeyLen, (DWORD) masterkeys->dwDomainKeyLen); if(masterkeys->MasterKey) { kprintf(L"%*s" L"[masterkey]\n", level << 1, L""); kull_m_dpapi_masterkey_descr(level + 1, masterkeys->MasterKey); } if(masterkeys->BackupKey) { kprintf(L"%*s" L"[backupkey]\n", level << 1, L""); kull_m_dpapi_masterkey_descr(level + 1, masterkeys->BackupKey); } if(masterkeys->CredHist) { kprintf(L"%*s" L"[credhist]\n", level << 1, L""); kull_m_dpapi_masterkeys_credhist_descr(level + 1, masterkeys->CredHist); } if(masterkeys->DomainKey) { kprintf(L"%*s" L"[domainkey]\n", level << 1, L""); kull_m_dpapi_masterkeys_domainkey_descr(level + 1, masterkeys->DomainKey); } kprintf(L"\n"); } } PBYTE kull_m_dpapi_masterkeys_tobin(PKULL_M_DPAPI_MASTERKEYS masterkeys, OPTIONAL DWORD64 *size) { PBYTE data = NULL; PBYTE MasterKey = NULL, BackupKey = NULL, CredHist = NULL, DomainKey = NULL; if(masterkeys->MasterKey) MasterKey = kull_m_dpapi_masterkey_tobin(masterkeys->MasterKey, &masterkeys->dwMasterKeyLen); if(masterkeys->BackupKey) BackupKey = kull_m_dpapi_masterkey_tobin(masterkeys->BackupKey, &masterkeys->dwBackupKeyLen); if(masterkeys->CredHist) CredHist = kull_m_dpapi_masterkeys_credhist_tobin(masterkeys->CredHist, &masterkeys->dwCredHistLen); if(masterkeys->DomainKey) DomainKey = kull_m_dpapi_masterkeys_domainkey_tobin(masterkeys->DomainKey, &masterkeys->dwDomainKeyLen); if(data = (PBYTE) LocalAlloc(LPTR, FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + (SIZE_T) (masterkeys->dwMasterKeyLen + masterkeys->dwBackupKeyLen + masterkeys->dwCredHistLen + masterkeys->dwDomainKeyLen))) { RtlCopyMemory(data, masterkeys, FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey)); if(MasterKey) RtlCopyMemory(data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey), MasterKey, (size_t) masterkeys->dwMasterKeyLen); if(BackupKey) RtlCopyMemory(data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + masterkeys->dwMasterKeyLen, BackupKey, (size_t) masterkeys->dwBackupKeyLen); if(CredHist) RtlCopyMemory(data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + masterkeys->dwMasterKeyLen + masterkeys->dwBackupKeyLen, CredHist, (size_t) masterkeys->dwCredHistLen); if(DomainKey) RtlCopyMemory(data + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + masterkeys->dwMasterKeyLen + masterkeys->dwBackupKeyLen + masterkeys->dwCredHistLen, DomainKey, (size_t) masterkeys->dwDomainKeyLen); if(size) *size = FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + masterkeys->dwMasterKeyLen + masterkeys->dwBackupKeyLen + masterkeys->dwCredHistLen + masterkeys->dwDomainKeyLen; } return data; } PKULL_M_DPAPI_CREDHIST kull_m_dpapi_credhist_create(LPCVOID data, DWORD size) { PKULL_M_DPAPI_CREDHIST credhist = NULL; DWORD currSize, sumSize, i; if(data && (credhist = (PKULL_M_DPAPI_CREDHIST) LocalAlloc(LPTR, sizeof(KULL_M_DPAPI_CREDHIST)))) { RtlCopyMemory(credhist, (PBYTE) data + size - sizeof(KULL_M_DPAPI_CREDHIST_HEADER), sizeof(KULL_M_DPAPI_CREDHIST_HEADER)); for( sumSize = sizeof(KULL_M_DPAPI_CREDHIST_HEADER), currSize = credhist->current.dwNextLen; (sumSize < size) && currSize; currSize = ((PKULL_M_DPAPI_CREDHIST_ENTRY) ((PBYTE) data + size - (sumSize + currSize)))->header.dwNextLen, sumSize += currSize, credhist->__dwCount++ ); if(credhist->entries = (PKULL_M_DPAPI_CREDHIST_ENTRY *) LocalAlloc(LPTR, credhist->__dwCount * sizeof(PKULL_M_DPAPI_CREDHIST_ENTRY))) for( i = 0, sumSize = sizeof(KULL_M_DPAPI_CREDHIST_HEADER), currSize = credhist->current.dwNextLen; (sumSize < size) && currSize; currSize = ((PKULL_M_DPAPI_CREDHIST_ENTRY) ((PBYTE) data + size - (sumSize + currSize)))->header.dwNextLen, sumSize += currSize, i++ ) credhist->entries[i] = kull_m_dpapi_credhist_entry_create(((PBYTE) data + size - (sumSize + currSize)), currSize); } return credhist; } void kull_m_dpapi_credhist_delete(PKULL_M_DPAPI_CREDHIST credhist) { DWORD i; if(credhist) { for(i = 0; i < credhist->__dwCount; i++) kull_m_dpapi_credhist_entry_delete(credhist->entries[i]); LocalFree(credhist); } } void kull_m_dpapi_credhist_descr(DWORD level, PKULL_M_DPAPI_CREDHIST credhist) { DWORD i; kprintf(L"%*s" L"**CREDHIST**\n", level << 1, L""); if(credhist) { kprintf(L"%*s" L" dwVersion : %08x - %u\n", level << 1, L"", credhist->current.dwVersion, credhist->current.dwVersion); kprintf(L"%*s" L" guid : ", level << 1, L""); kull_m_string_displayGUID(&credhist->current.guid); kprintf(L"\n"); kprintf(L"%*s" L" dwNextLen : %08x - %u\n", level << 1, L"", credhist->current.dwNextLen, credhist->current.dwNextLen); for(i = 0; i < credhist->__dwCount; i++) kull_m_dpapi_credhist_entry_descr(level + 1, credhist->entries[i]); kprintf(L"\n"); } } PKULL_M_DPAPI_CREDHIST_ENTRY kull_m_dpapi_credhist_entry_create(LPCVOID data, DWORD size) { PKULL_M_DPAPI_CREDHIST_ENTRY entry = NULL; if(data && (entry = (PKULL_M_DPAPI_CREDHIST_ENTRY) LocalAlloc(LPTR, sizeof(KULL_M_DPAPI_CREDHIST_ENTRY)))) { RtlCopyMemory(entry, data, FIELD_OFFSET(KULL_M_DPAPI_CREDHIST_ENTRY, pSid)); entry->pSid = (PBYTE) data + FIELD_OFFSET(KULL_M_DPAPI_CREDHIST_ENTRY, pSid); entry->pSecret = (PBYTE) entry->pSid + entry->sidLen; entry->__dwSecretLen = size - (FIELD_OFFSET(KULL_M_DPAPI_CREDHIST_ENTRY, pSid) + entry->sidLen); kull_m_string_ptr_replace(&entry->pSid, entry->sidLen); kull_m_string_ptr_replace(&entry->pSecret, entry->__dwSecretLen); } return entry; } void kull_m_dpapi_credhist_entry_delete(PKULL_M_DPAPI_CREDHIST_ENTRY entry) { if(entry) { if(entry->pSid) LocalFree(entry->pSid); if(entry->pSecret) LocalFree(entry->pSecret); LocalFree(entry); } } void kull_m_dpapi_credhist_entry_descr(DWORD level, PKULL_M_DPAPI_CREDHIST_ENTRY entry) { kprintf(L"%*s" L"**CREDHIST ENTRY**\n", level << 1, L""); if(entry) { kprintf(L"%*s" L" dwVersion : %08x - %u\n", level << 1, L"", entry->header.dwVersion, entry->header.dwVersion); kprintf(L"%*s" L" guid : ", level << 1, L""); kull_m_string_displayGUID(&entry->header.guid); kprintf(L"\n"); kprintf(L"%*s" L" dwNextLen : %08x - %u\n", level << 1, L"", entry->header.dwNextLen, entry->header.dwNextLen); kprintf(L"%*s" L" dwType : %08x - %u\n", level << 1, L"", entry->dwType, entry->dwType); kprintf(L"%*s" L" algHash : %08x - %u (%s)\n", level << 1, L"", entry->algHash, entry->algHash, kull_m_crypto_algid_to_name(entry->algHash)); kprintf(L"%*s" L" rounds : %08x - %u\n", level << 1, L"", entry->rounds, entry->rounds); kprintf(L"%*s" L" sidLen : %08x - %u\n", level << 1, L"", entry->sidLen, entry->sidLen); kprintf(L"%*s" L" algCrypt : %08x - %u (%s)\n", level << 1, L"", entry->algCrypt, entry->algCrypt, kull_m_crypto_algid_to_name(entry->algCrypt)); kprintf(L"%*s" L" sha1Len : %08x - %u\n", level << 1, L"", entry->sha1Len, entry->sha1Len); kprintf(L"%*s" L" md4Len : %08x - %u\n", level << 1, L"", entry->md4Len, entry->md4Len); kprintf(L"%*s" L" Salt : ", level << 1, L""); kull_m_string_wprintf_hex(entry->salt, sizeof(entry->salt), 0); kprintf(L"\n"); kprintf(L"%*s" L" Sid : ", level << 1, L""); kull_m_string_displaySID(entry->pSid); kprintf(L"\n"); kprintf(L"%*s" L" pSecret : ", level << 1, L""); kull_m_string_wprintf_hex(entry->pSecret, entry->__dwSecretLen, 0); kprintf(L"\n\n"); } } BOOL kull_m_dpapi_hmac_sha1_incorrect(LPCVOID key, DWORD keyLen, LPCVOID salt, DWORD saltLen, LPCVOID entropy, DWORD entropyLen, LPCVOID data, DWORD dataLen, LPVOID outKey) { BOOL status = FALSE; BYTE ipad[64], opad[64], hash[SHA_DIGEST_LENGTH], *bufferI, *bufferO; DWORD i; RtlFillMemory(ipad, sizeof(ipad), '6'); RtlFillMemory(opad, sizeof(opad), '\\'); for(i = 0; i < keyLen; i++) { ipad[i] ^= ((PBYTE) key)[i]; opad[i] ^= ((PBYTE) key)[i]; } if(bufferI = (PBYTE) LocalAlloc(LPTR, sizeof(ipad) + saltLen)) { RtlCopyMemory(bufferI, ipad, sizeof(ipad)); RtlCopyMemory(bufferI + sizeof(ipad), salt, saltLen); if(kull_m_crypto_hash(CALG_SHA1, bufferI, sizeof(ipad) + saltLen, hash, SHA_DIGEST_LENGTH)) { if(bufferO = (PBYTE) LocalAlloc(LPTR, sizeof(opad) + SHA_DIGEST_LENGTH + entropyLen + dataLen)) { RtlCopyMemory(bufferO, opad, sizeof(opad)); RtlCopyMemory(bufferO + sizeof(opad), hash, SHA_DIGEST_LENGTH); if(entropy && entropyLen) RtlCopyMemory(bufferO + sizeof(opad) + SHA_DIGEST_LENGTH, entropy, entropyLen); if(data && dataLen) RtlCopyMemory(bufferO + sizeof(opad) + SHA_DIGEST_LENGTH + entropyLen, data, dataLen); status = kull_m_crypto_hash(CALG_SHA1, bufferO, sizeof(opad) + SHA_DIGEST_LENGTH + entropyLen + dataLen, outKey, SHA_DIGEST_LENGTH); LocalFree(bufferO); } } LocalFree(bufferI); } return status; } BOOL kull_m_dpapi_sessionkey(LPCVOID masterkey, DWORD masterkeyLen, LPCVOID salt, DWORD saltLen, LPCVOID entropy, DWORD entropyLen, LPCVOID data, DWORD dataLen, ALG_ID hashAlg, LPVOID outKey, DWORD outKeyLen) { BOOL status = FALSE; LPCVOID pKey = NULL; BYTE dgstMasterKey[SHA_DIGEST_LENGTH]; PBYTE tmp; if(masterkeyLen == SHA_DIGEST_LENGTH) pKey = masterkey; else if(kull_m_crypto_hash(CALG_SHA1, masterkey, masterkeyLen, dgstMasterKey, SHA_DIGEST_LENGTH)) pKey = dgstMasterKey; if(pKey) { if((hashAlg == CALG_SHA1) && (entropy || data)) status = kull_m_dpapi_hmac_sha1_incorrect(masterkey, masterkeyLen, salt, saltLen, entropy, entropyLen, data, dataLen, outKey); else if(tmp = (PBYTE) LocalAlloc(LPTR, saltLen + entropyLen + dataLen)) { RtlCopyMemory(tmp, salt, saltLen); if(entropy && entropyLen) RtlCopyMemory(tmp + saltLen, entropy, entropyLen); if(data && dataLen) RtlCopyMemory(tmp + saltLen + entropyLen, data, dataLen); status = kull_m_crypto_hmac(hashAlg, pKey, SHA_DIGEST_LENGTH, tmp, saltLen + entropyLen + dataLen, outKey, outKeyLen); LocalFree(tmp); } } return status; } BOOL kull_m_dpapi_unprotect_blob(PKULL_M_DPAPI_BLOB blob, LPCVOID masterkey, DWORD masterkeyLen, LPCVOID entropy, DWORD entropyLen, LPCWSTR password, LPVOID *dataOut, DWORD *dataOutLen) { BOOL status = FALSE, iStatus = !password; PVOID hmac, key, hashPassword = NULL; HCRYPTPROV hSessionProv; HCRYPTKEY hSessionKey; DWORD hashLen = blob->dwAlgHashLen / 8, cryptLen = blob->dwAlgCryptLen / 8, hashPasswordLen; ALG_ID passwordHash; if((blob->algCrypt == CALG_3DES) && (cryptLen < (192 / 8))) cryptLen = 192 / 8; if(!iStatus) { if(blob->algHash == CALG_SHA_512) { passwordHash = CALG_SHA_512; hashPasswordLen = hashLen; } else { passwordHash = CALG_SHA1; hashPasswordLen = SHA_DIGEST_LENGTH; } if(hashPassword = LocalAlloc(LPTR, hashPasswordLen)) iStatus = kull_m_crypto_hash(passwordHash, password, (DWORD) (wcslen(password) * sizeof(wchar_t)), hashPassword, hashPasswordLen); } if(iStatus) { if(hmac = LocalAlloc(LPTR, hashLen)) { if(kull_m_dpapi_sessionkey(masterkey, masterkeyLen, blob->pbSalt, blob->dwSaltLen, entropy, entropyLen, hashPassword, hashPassword ? hashPasswordLen : 0, blob->algHash, hmac, hashLen)) { if(key = LocalAlloc(LPTR, cryptLen)) { if(kull_m_crypto_DeriveKeyRaw(blob->algHash, hmac, hashLen, key, cryptLen)) { if(kull_m_crypto_hkey_session(blob->algCrypt, key, cryptLen, 0, &hSessionKey, &hSessionProv)) { if(*dataOut = LocalAlloc(LPTR, blob->dwDataLen)) { RtlCopyMemory(*dataOut, blob->pbData, blob->dwDataLen); *dataOutLen = blob->dwDataLen; status = CryptDecrypt(hSessionKey, 0, TRUE, 0, (LPBYTE) *dataOut, dataOutLen); if(!status) { *dataOut = LocalFree(*dataOut); PRINT_ERROR_AUTO(L"CryptDecrypt"); } } CryptDestroyKey(hSessionKey); if(!kull_m_crypto_close_hprov_delete_container(hSessionProv)) PRINT_ERROR_AUTO(L"kull_m_crypto_close_hprov_delete_container"); } else PRINT_ERROR_AUTO(L"kull_m_crypto_hkey_session"); } LocalFree(key); } } LocalFree(hmac); } } if(hashPassword) LocalFree(hashPassword); return status; } BOOL kull_m_dpapi_unprotect_raw_or_blob(LPCVOID pDataIn, DWORD dwDataInLen, LPWSTR *ppszDataDescr, LPCVOID pOptionalEntropy, DWORD dwOptionalEntropyLen, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, LPVOID *pDataOut, DWORD *dwDataOutLen, LPCVOID pMasterKey, DWORD dwMasterKeyLen, LPCWSTR pPassword) { BOOL status = FALSE; DATA_BLOB dataIn = {dwDataInLen, (PBYTE) pDataIn}, dataEntropy = {dwOptionalEntropyLen, (PBYTE) pOptionalEntropy}, dataOut; PKULL_M_DPAPI_BLOB blob; if(pMasterKey && dwMasterKeyLen) { if(blob = kull_m_dpapi_blob_create(pDataIn)) { if(status = kull_m_dpapi_unprotect_blob(blob, pMasterKey, dwMasterKeyLen, pOptionalEntropy, dwOptionalEntropyLen, pPassword, pDataOut, dwDataOutLen)) if(ppszDataDescr && blob->szDescription && blob->dwDescriptionLen) if(*ppszDataDescr = (LPWSTR) LocalAlloc(LPTR, blob->dwDescriptionLen)) RtlCopyMemory(*ppszDataDescr, blob->szDescription, blob->dwDescriptionLen); kull_m_dpapi_blob_delete(blob); } } else { if(status = CryptUnprotectData(&dataIn, ppszDataDescr, &dataEntropy, NULL, pPromptStruct, dwFlags, &dataOut)) { *dwDataOutLen = dataOut.cbData; if(*pDataOut = LocalAlloc(LPTR, *dwDataOutLen)) RtlCopyMemory(*pDataOut, dataOut.pbData, *dwDataOutLen); LocalFree(dataOut.pbData); } } return status; } BOOL kull_m_dpapi_getProtected(PVOID PassHash, DWORD PassLen, PCWSTR sid) { BOOL status = FALSE; DWORD SidLen = lstrlen(sid) * sizeof(wchar_t); BYTE sha2[32]; if(kull_m_crypto_pkcs5_pbkdf2_hmac(CALG_SHA_256, PassHash, PassLen, sid, SidLen, 10000, sha2, sizeof(sha2), FALSE)) status = kull_m_crypto_pkcs5_pbkdf2_hmac(CALG_SHA_256, sha2, sizeof(sha2), sid, SidLen, 1, (PBYTE) PassHash, PassLen, FALSE); return status; } BOOL kull_m_dpapi_unprotect_masterkey_with_password(DWORD flags, PKULL_M_DPAPI_MASTERKEY masterkey, PCWSTR password, PCWSTR sid, BOOL isKeyOfProtectedUser, PVOID *output, DWORD *outputLen) { BOOL status = FALSE; ALG_ID PassAlg; DWORD PassLen, SidLen = (DWORD) wcslen(sid) * sizeof(wchar_t); PVOID PassHash; PassAlg = (flags & 4) ? CALG_SHA1 : CALG_MD4; PassLen = kull_m_crypto_hash_len(PassAlg); if(PassHash = LocalAlloc(LPTR, PassLen)) { if(kull_m_crypto_hash(PassAlg, password, (DWORD) wcslen(password) * sizeof(wchar_t), PassHash, PassLen)) status = kull_m_dpapi_unprotect_masterkey_with_userHash(masterkey, PassHash, PassLen, sid, isKeyOfProtectedUser, output, outputLen); LocalFree(PassHash); } return status; } BOOL kull_m_dpapi_unprotect_masterkey_with_userHash(PKULL_M_DPAPI_MASTERKEY masterkey, LPCVOID userHash, DWORD userHashLen, PCWSTR sid, BOOL isKeyOfProtectedUser, PVOID *output, DWORD *outputLen) { BYTE sha1DerivedKey[SHA_DIGEST_LENGTH], ntlmofprotected[LM_NTLM_HASH_LENGTH]; BOOL status = FALSE, isNTLMofProtected = isKeyOfProtectedUser && sid && (userHashLen == sizeof(ntlmofprotected)); LPCVOID hash; DWORD hashLen; if(!isNTLMofProtected) { hash = userHash; hashLen = userHashLen; } else { RtlCopyMemory(ntlmofprotected, userHash, sizeof(ntlmofprotected)); kull_m_dpapi_getProtected(ntlmofprotected, sizeof(ntlmofprotected), sid); hash = ntlmofprotected; hashLen = sizeof(ntlmofprotected); } if(sid) status = kull_m_crypto_hmac(CALG_SHA1, hash, hashLen, sid, (lstrlen(sid) + 1) * sizeof(wchar_t), sha1DerivedKey, SHA_DIGEST_LENGTH); else RtlCopyMemory(sha1DerivedKey, hash, min(sizeof(sha1DerivedKey), hashLen)); if(!sid || status) status = kull_m_dpapi_unprotect_masterkey_with_shaDerivedkey(masterkey, sha1DerivedKey, SHA_DIGEST_LENGTH, output, outputLen); return status; } BOOL kull_m_dpapi_unprotect_masterkey_with_shaDerivedkey(PKULL_M_DPAPI_MASTERKEY masterkey, LPCVOID shaDerivedkey, DWORD shaDerivedkeyLen, PVOID *output, DWORD *outputLen) { BOOL status = FALSE; HCRYPTPROV hSessionProv; HCRYPTKEY hSessionKey; ALG_ID HMACAlg; DWORD HMACLen, BlockLen, KeyLen, OutLen; PVOID HMACHash, CryptBuffer, hmac1, hmac2; HMACAlg = (masterkey->algHash == CALG_HMAC) ? CALG_SHA1 : masterkey->algHash; HMACLen = kull_m_crypto_hash_len(HMACAlg); KeyLen = kull_m_crypto_cipher_keylen(masterkey->algCrypt); BlockLen = kull_m_crypto_cipher_blocklen(masterkey->algCrypt); if(HMACHash = LocalAlloc(LPTR, KeyLen + BlockLen)) { if(kull_m_crypto_pkcs5_pbkdf2_hmac(HMACAlg, shaDerivedkey, shaDerivedkeyLen, masterkey->salt, sizeof(masterkey->salt), masterkey->rounds, (PBYTE) HMACHash, KeyLen + BlockLen, TRUE)) { if(kull_m_crypto_hkey_session(masterkey->algCrypt, HMACHash, KeyLen, 0, &hSessionKey, &hSessionProv)) { if(CryptSetKeyParam(hSessionKey, KP_IV, (PBYTE) HMACHash + KeyLen, 0)) { OutLen = masterkey->__dwKeyLen; if(CryptBuffer = LocalAlloc(LPTR, OutLen)) { RtlCopyMemory(CryptBuffer, masterkey->pbKey, OutLen); if(CryptDecrypt(hSessionKey, 0, FALSE, 0, (PBYTE) CryptBuffer, &OutLen)) { *outputLen = OutLen - 16 - HMACLen - ((masterkey->algCrypt == CALG_3DES) ? 4 : 0); // reversed -- see with blocklen like in protect if(hmac1 = LocalAlloc(LPTR, HMACLen)) { if(kull_m_crypto_hmac(HMACAlg, shaDerivedkey, shaDerivedkeyLen, CryptBuffer, 16, hmac1, HMACLen)) { if(hmac2 = LocalAlloc(LPTR, HMACLen)) { if(kull_m_crypto_hmac(HMACAlg, hmac1, HMACLen, (PBYTE) CryptBuffer + OutLen - *outputLen, *outputLen, hmac2, HMACLen)) { if(status = RtlEqualMemory(hmac2, (PBYTE) CryptBuffer + 16, HMACLen)) { if(*output = LocalAlloc(LPTR, *outputLen)) RtlCopyMemory(*output, (PBYTE) CryptBuffer + OutLen - *outputLen, *outputLen); } } LocalFree(hmac2); } } LocalFree(hmac1); } } LocalFree(CryptBuffer); } } CryptDestroyKey(hSessionKey); if(!kull_m_crypto_close_hprov_delete_container(hSessionProv)) PRINT_ERROR_AUTO(L"kull_m_crypto_close_hprov_delete_container"); } else PRINT_ERROR_AUTO(L"kull_m_crypto_hkey_session"); } LocalFree(HMACHash); } return status; } BOOL kull_m_dpapi_protect_masterkey_with_password(DWORD flags, PKULL_M_DPAPI_MASTERKEY masterkey, PCWSTR password, PCWSTR sid, BOOL isKeyOfProtectedUser, LPCVOID pbKey, DWORD dwKey, OPTIONAL LPCVOID pbInternalSalt) { BOOL status = FALSE; ALG_ID PassAlg; DWORD PassLen, SidLen = (DWORD) wcslen(sid) * sizeof(wchar_t); PVOID PassHash; PassAlg = (flags & 4) ? CALG_SHA1 : CALG_MD4; PassLen = kull_m_crypto_hash_len(PassAlg); if(PassHash = LocalAlloc(LPTR, PassLen)) { if(kull_m_crypto_hash(PassAlg, password, (DWORD) wcslen(password) * sizeof(wchar_t), PassHash, PassLen)) status = kull_m_dpapi_protect_masterkey_with_userHash(masterkey, PassHash, PassLen, sid, isKeyOfProtectedUser, pbKey, dwKey, pbInternalSalt); LocalFree(PassHash); } return status; } BOOL kull_m_dpapi_protect_masterkey_with_userHash(PKULL_M_DPAPI_MASTERKEY masterkey, LPCVOID userHash, DWORD userHashLen, PCWSTR sid, BOOL isKeyOfProtectedUser, LPCVOID pbKey, DWORD dwKey, OPTIONAL LPCVOID pbInternalSalt) { BYTE sha1DerivedKey[SHA_DIGEST_LENGTH], ntlmofprotected[LM_NTLM_HASH_LENGTH]; BOOL status = FALSE, isNTLMofProtected = isKeyOfProtectedUser && sid && (userHashLen == sizeof(ntlmofprotected)); LPCVOID hash; DWORD hashLen; if(!isNTLMofProtected) { hash = userHash; hashLen = userHashLen; } else { RtlCopyMemory(ntlmofprotected, userHash, sizeof(ntlmofprotected)); kull_m_dpapi_getProtected(ntlmofprotected, sizeof(ntlmofprotected), sid); hash = ntlmofprotected; hashLen = sizeof(ntlmofprotected); } if(sid) status = kull_m_crypto_hmac(CALG_SHA1, hash, hashLen, sid, (lstrlen(sid) + 1) * sizeof(wchar_t), sha1DerivedKey, SHA_DIGEST_LENGTH); else RtlCopyMemory(sha1DerivedKey, hash, min(sizeof(sha1DerivedKey), hashLen)); if(!sid || status) status = kull_m_dpapi_protect_masterkey_with_shaDerivedkey(masterkey, sha1DerivedKey, SHA_DIGEST_LENGTH, pbKey, dwKey, pbInternalSalt); return status; } BOOL kull_m_dpapi_protect_masterkey_with_shaDerivedkey(PKULL_M_DPAPI_MASTERKEY masterkey, LPCVOID shaDerivedkey, DWORD shaDerivedkeyLen, LPCVOID pbKey, DWORD dwKey, OPTIONAL LPCVOID pbInternalSalt) { BOOL status = FALSE; HCRYPTPROV hSessionProv; HCRYPTKEY hSessionKey; ALG_ID HMACAlg; DWORD HMACLen, BlockLen, KeyLen; PVOID HMACHash; HMACAlg = (masterkey->algHash == CALG_HMAC) ? CALG_SHA1 : masterkey->algHash; HMACLen = kull_m_crypto_hash_len(HMACAlg); KeyLen = kull_m_crypto_cipher_keylen(masterkey->algCrypt); BlockLen = kull_m_crypto_cipher_blocklen(masterkey->algCrypt); HMACLen += HMACLen % BlockLen; // will be truncated in kull_m_crypto_hmac masterkey->__dwKeyLen = 16 + HMACLen + dwKey; if(masterkey->pbKey = (PBYTE) LocalAlloc(LPTR, masterkey->__dwKeyLen + BlockLen)) { if(pbInternalSalt) RtlCopyMemory(masterkey->pbKey, pbInternalSalt, 16); else CDGenerateRandomBits(masterkey->pbKey, 16); if(kull_m_crypto_hmac(HMACAlg, shaDerivedkey, shaDerivedkeyLen, masterkey->pbKey, 16, masterkey->pbKey + 16, HMACLen)) // tmp usage of hmac2 for hmac1 { if(kull_m_crypto_hmac(HMACAlg, masterkey->pbKey + 16, HMACLen, pbKey, dwKey, masterkey->pbKey + 16, HMACLen)) { RtlCopyMemory(masterkey->pbKey + 16 + HMACLen, pbKey, dwKey); if(HMACHash = LocalAlloc(LPTR, KeyLen + BlockLen)) { if(kull_m_crypto_pkcs5_pbkdf2_hmac(HMACAlg, shaDerivedkey, shaDerivedkeyLen, masterkey->salt, sizeof(masterkey->salt), masterkey->rounds, (PBYTE) HMACHash, KeyLen + BlockLen, TRUE)) { if(kull_m_crypto_hkey_session(masterkey->algCrypt, HMACHash, KeyLen, 0, &hSessionKey, &hSessionProv)) { if(CryptSetKeyParam(hSessionKey, KP_IV, (PBYTE) HMACHash + KeyLen, 0)) { if(status = CryptEncrypt(hSessionKey, 0, TRUE, 0, masterkey->pbKey, &masterkey->__dwKeyLen, masterkey->__dwKeyLen + BlockLen)) masterkey->__dwKeyLen -= BlockLen; else PRINT_ERROR_AUTO(L"CryptEncrypt"); } CryptDestroyKey(hSessionKey); if(!kull_m_crypto_close_hprov_delete_container(hSessionProv)) PRINT_ERROR_AUTO(L"kull_m_crypto_close_hprov_delete_container"); } else PRINT_ERROR_AUTO(L"kull_m_crypto_hkey_session"); } LocalFree(HMACHash); } } } if(!status) { masterkey->pbKey = (PBYTE) LocalFree(masterkey->pbKey); masterkey->__dwKeyLen = 0; } } return status; } BOOL kull_m_dpapi_unprotect_backupkey_with_secret(DWORD flags, PKULL_M_DPAPI_MASTERKEY masterkey, PCWSTR sid, LPCVOID secret, DWORD secretLen, PVOID *output, DWORD *outputLen) { BOOL status = FALSE, isDPAPISecret = flags & 1; LPCBYTE ptrSecret = (LPCBYTE) secret; PVOID data, hash; ALG_ID algID = (masterkey->algHash == CALG_SHA_512) ? CALG_SHA_512 : CALG_SHA1; DWORD sidLen = (DWORD) (wcslen(sid) + 1) * sizeof(wchar_t), hashSize = kull_m_crypto_hash_len(algID), dataSize = sidLen; if(!isDPAPISecret || (isDPAPISecret && ptrSecret && secretLen)) { if(secretLen == 2 * SHA_DIGEST_LENGTH + sizeof(DWORD)) { ptrSecret += sizeof(DWORD); secretLen -= sizeof(DWORD); } if(isDPAPISecret) dataSize += secretLen; if(data = (PBYTE) LocalAlloc(LPTR, dataSize)) { RtlCopyMemory(data, sid, sidLen); if(isDPAPISecret) RtlCopyMemory((PBYTE) data + sidLen, ptrSecret, secretLen); if(hash = LocalAlloc(LPTR, hashSize)) { if(kull_m_crypto_hash(algID, data, dataSize, hash, hashSize)) status = kull_m_dpapi_unprotect_masterkey_with_shaDerivedkey(masterkey, hash, hashSize, output, outputLen); LocalFree(hash); } LocalFree(data); } } else PRINT_ERROR(L"This backup key need DPAPI_SYSTEM secret\n"); return status; } BOOL kull_m_dpapi_unprotect_domainkey_with_key(PKULL_M_DPAPI_MASTERKEY_DOMAINKEY domainkey, LPCVOID key, DWORD keyLen, PVOID *output, DWORD *outputLen, PSID *sid) { BOOL status = FALSE; HCRYPTPROV hProv, hSessionProv; HCRYPTKEY hKey, hSessionKey; PKULL_M_DPAPI_DOMAIN_RSA_MASTER_KEY rsa_buffer; PKULL_M_DPAPI_DOMAIN_ACCESS_CHECK des_buffer; BYTE digest[SHA_DIGEST_LENGTH]; DWORD cbOutput; PSID pSid; if(CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { if(CryptImportKey(hProv, (PBYTE) key, keyLen, 0, 0, &hKey)) { cbOutput = domainkey->dwSecretLen; if(rsa_buffer = (PKULL_M_DPAPI_DOMAIN_RSA_MASTER_KEY) LocalAlloc(LPTR, cbOutput)) { RtlCopyMemory(rsa_buffer, domainkey->pbSecret, cbOutput); if(CryptDecrypt(hKey, 0, TRUE, 0, (PBYTE) rsa_buffer, &cbOutput)) { kprintf(L"\nRSA decrypt is a success\n"); kprintf(L" * MasterKey len: %u\n", rsa_buffer->cbMasterKey); kull_m_string_wprintf_hex(rsa_buffer->buffer, rsa_buffer->cbMasterKey, 1 | (16 << 16)); kprintf(L" * SuppKey len: %u\n", rsa_buffer->cbSuppKey); kull_m_string_wprintf_hex(rsa_buffer->buffer + rsa_buffer->cbMasterKey, rsa_buffer->cbSuppKey, 1 | (16 << 16)); if(kull_m_crypto_hkey(hProv, CALG_3DES, rsa_buffer->buffer + rsa_buffer->cbMasterKey, 192 / 8, 0, &hSessionKey, &hSessionProv)) { if(CryptSetKeyParam(hSessionKey, KP_IV, rsa_buffer->buffer + rsa_buffer->cbMasterKey + 192 / 8, 0)) { cbOutput = domainkey->dwAccesscheckLen; if(des_buffer = (PKULL_M_DPAPI_DOMAIN_ACCESS_CHECK) LocalAlloc(LPTR, cbOutput)) { RtlCopyMemory(des_buffer, domainkey->pbAccesscheck, cbOutput); if(CryptDecrypt(hSessionKey, 0, FALSE, 0, (PBYTE) des_buffer, &cbOutput)) { pSid = (PSID) (des_buffer->data + des_buffer->dataLen); kprintf(L"\n3DES decrypt is a success too\n"); kull_m_string_wprintf_hex(des_buffer, cbOutput, 1 | (16 << 16)); kprintf(L"\n"); kprintf(L" * nonce : "); kull_m_string_wprintf_hex(des_buffer->data, des_buffer->dataLen, 0); kprintf(L"\n"); // try to leave it as is =) kprintf(L" * SID : "); kull_m_string_displaySID(pSid); kprintf(L"\n"); kprintf(L" * SHA1 : "); kull_m_string_wprintf_hex((PBYTE) des_buffer + cbOutput - SHA_DIGEST_LENGTH, SHA_DIGEST_LENGTH, 0); kprintf(L"\n"); if(kull_m_crypto_hash(CALG_SHA1, des_buffer, cbOutput - SHA_DIGEST_LENGTH, digest, SHA_DIGEST_LENGTH)) { kprintf(L" > Calc SHA1: "); kull_m_string_wprintf_hex(digest, SHA_DIGEST_LENGTH, 0); kprintf(L"\n"); if(RtlEqualMemory((PBYTE) des_buffer + cbOutput - SHA_DIGEST_LENGTH, digest, SHA_DIGEST_LENGTH)) { *outputLen = rsa_buffer->cbMasterKey; if(*output = LocalAlloc(LPTR, *outputLen)) { RtlCopyMemory(*output, rsa_buffer->buffer, *outputLen); status = TRUE; *sid = NULL; if(sid) { status = FALSE; cbOutput = GetLengthSid(pSid); if(*sid = (PSID) LocalAlloc(LPTR, cbOutput)) status = CopySid(cbOutput, *sid, pSid); } if(!status) { if(*output) *output = LocalFree(*output); if(*sid) *sid = LocalFree(*sid); *outputLen = 0; } } } } } else PRINT_ERROR_AUTO(L"CryptDecrypt"); LocalFree(des_buffer); } } else PRINT_ERROR_AUTO(L"CryptSetKeyParam"); CryptDestroyKey(hSessionKey); if(!kull_m_crypto_close_hprov_delete_container(hSessionProv)) PRINT_ERROR_AUTO(L"kull_m_crypto_close_hprov_delete_container"); } } else PRINT_ERROR_AUTO(L"CryptDecrypt"); LocalFree(rsa_buffer); } CryptDestroyKey(hKey); } CryptReleaseContext(hProv, 0); } return status; } BOOL kull_m_dpapi_unprotect_domainkey_with_rpc(PKULL_M_DPAPI_MASTERKEYS masterkeys, PVOID rawMasterkeys, LPCWSTR server, PVOID *output, DWORD *outputLen) { BOOL status = FALSE; PBYTE out; DWORD dwOut; *output = NULL; *outputLen = 0; if(status = kull_m_rpc_bkrp_Restore(server, (PBYTE) rawMasterkeys + FIELD_OFFSET(KULL_M_DPAPI_MASTERKEYS, MasterKey) + masterkeys->dwMasterKeyLen + masterkeys->dwBackupKeyLen + masterkeys->dwCredHistLen, (DWORD) masterkeys->dwDomainKeyLen, (PVOID *) &out, &dwOut)) { *outputLen = dwOut - sizeof(DWORD); if(*output = LocalAlloc(LPTR, *outputLen)) RtlCopyMemory(*output, out + sizeof(DWORD), dwOut - sizeof(DWORD)); LocalFree(out); } return status; } BOOL kull_m_dpapi_unprotect_credhist_entry_with_shaDerivedkey(PKULL_M_DPAPI_CREDHIST_ENTRY entry, LPCVOID shaDerivedkey, DWORD shaDerivedkeyLen, PVOID md4hash, PVOID sha1hash) { BOOL status = FALSE; HCRYPTPROV hSessionProv; HCRYPTKEY hSessionKey; ALG_ID HMACAlg; DWORD HMACLen, BlockLen, KeyLen, OutLen; PVOID HMACHash, CryptBuffer; DWORD i; HMACAlg = (entry->algHash == CALG_HMAC) ? CALG_SHA1 : entry->algHash; HMACLen = kull_m_crypto_hash_len(HMACAlg); KeyLen = kull_m_crypto_cipher_keylen(entry->algCrypt); BlockLen = kull_m_crypto_cipher_blocklen(entry->algCrypt); if(HMACHash = LocalAlloc(LPTR, KeyLen + BlockLen)) { if(kull_m_crypto_pkcs5_pbkdf2_hmac(HMACAlg, shaDerivedkey, shaDerivedkeyLen, entry->salt, sizeof(entry->salt), entry->rounds, (PBYTE) HMACHash, KeyLen + BlockLen, TRUE)) { if(kull_m_crypto_hkey_session(entry->algCrypt, HMACHash, KeyLen, 0, &hSessionKey, &hSessionProv)) { if(CryptSetKeyParam(hSessionKey, KP_IV, (PBYTE) HMACHash + KeyLen, 0)) { OutLen = entry->__dwSecretLen; if(CryptBuffer = LocalAlloc(LPTR, OutLen)) { RtlCopyMemory(CryptBuffer, entry->pSecret, OutLen); if(CryptDecrypt(hSessionKey, 0, FALSE, 0, (PBYTE) CryptBuffer, &OutLen)) { RtlCopyMemory(sha1hash, CryptBuffer, min(entry->sha1Len, SHA_DIGEST_LENGTH)); RtlCopyMemory(md4hash, (PBYTE) CryptBuffer + entry->sha1Len, min(entry->md4Len, LM_NTLM_HASH_LENGTH)); status = TRUE; if(entry->md4Len - LM_NTLM_HASH_LENGTH) for(i = 0; (i < (entry->md4Len - LM_NTLM_HASH_LENGTH) && status); i++) status &= ! *((PBYTE) CryptBuffer + entry->sha1Len + LM_NTLM_HASH_LENGTH + i); } LocalFree(CryptBuffer); } } CryptDestroyKey(hSessionKey); if(!kull_m_crypto_close_hprov_delete_container(hSessionProv)) PRINT_ERROR_AUTO(L"kull_m_crypto_close_hprov_delete_container"); } else PRINT_ERROR_AUTO(L"kull_m_crypto_hkey_session"); } LocalFree(HMACHash); } return status; } const PCWCHAR DPAPIPromptFlagsToStrings[] = { L"on_unprotect", L"on_protect", L"reserved", L"strong", L"require_strong", }; void kull_m_dpapi_displayPromptFlags(DWORD flags) { DWORD i; for(i = 0; i < ARRAYSIZE(DPAPIPromptFlagsToStrings); i++) if((flags >> i) & 1) kprintf(L"%s ; ", DPAPIPromptFlagsToStrings[i]); } const PCWCHAR DPAPIProtectFlagsToStrings[] = { L"ui_forbidden", L"unknown", L"local_machine", L"cred_sync", L"audit", L"no_recovery", L"verify_protection", L"cred_regenerate", }; void kull_m_dpapi_displayProtectionFlags(DWORD flags) { DWORD i; for(i = 0; i < ARRAYSIZE(DPAPIProtectFlagsToStrings); i++) if((flags >> i) & 1) kprintf(L"%s ; ", DPAPIProtectFlagsToStrings[i]); if(flags & CRYPTPROTECT_SYSTEM) kprintf(L"%s ; ", L"system"); } const PCWCHAR DPAPIBlobFlagsToStrings[] = { L"prompt_on_unprotect", L"prompt_on_protect", L"local_machine", L"prompt_strong", L"audit", }; void kull_m_dpapi_displayBlobFlags(DWORD flags) { DWORD i; for(i = 0; i < ARRAYSIZE(DPAPIBlobFlagsToStrings); i++) if((flags >> i) & 1) kprintf(L"%s ; ", DPAPIBlobFlagsToStrings[i]); if(flags & CRYPTPROTECT_SYSTEM) kprintf(L"%s ; ", L"system"); }