mirror of
https://github.com/donnaskiez/ac.git
synced 2024-11-21 22:24:08 +01:00
655 lines
No EOL
16 KiB
C
655 lines
No EOL
16 KiB
C
#include "crypt.h"
|
|
|
|
#include "driver.h"
|
|
#include "imports.h"
|
|
#include "lib/stdlib.h"
|
|
#include "session.h"
|
|
#include "types/tpm20.h"
|
|
#include "types/tpmptp.h"
|
|
#include "util.h"
|
|
|
|
#include <bcrypt.h>
|
|
#include <immintrin.h>
|
|
|
|
FORCEINLINE
|
|
STATIC
|
|
UINT64
|
|
CryptGenerateRandomKey64(_In_ PUINT32 Seed)
|
|
{
|
|
return ((UINT64)RtlRandomEx(Seed) << 32 | RtlRandomEx(Seed));
|
|
}
|
|
|
|
STATIC
|
|
__m256i
|
|
CryptXorKeyGenerate_m256i()
|
|
{
|
|
UINT32 seed = (UINT32)__rdtsc();
|
|
UINT64 key_1 = CryptGenerateRandomKey64(&seed);
|
|
UINT64 key_2 = CryptGenerateRandomKey64(&seed);
|
|
UINT64 key_3 = CryptGenerateRandomKey64(&seed);
|
|
UINT64 key_4 = CryptGenerateRandomKey64(&seed);
|
|
|
|
return _mm256_set_epi64x(key_1, key_2, key_3, key_4);
|
|
}
|
|
|
|
UINT64
|
|
CryptXorKeyGenerate_uint64()
|
|
{
|
|
UINT32 seed = (UINT32)__rdtsc();
|
|
return CryptGenerateRandomKey64(&seed);
|
|
}
|
|
|
|
VOID
|
|
CryptEncryptImportsArray(_In_ PUINT64 Array, _In_ UINT32 Entries)
|
|
{
|
|
__m256i* imports_key = GetDriverImportsKey();
|
|
UINT32 block_size = sizeof(__m256i) / sizeof(UINT64);
|
|
UINT32 block_count = Entries / block_size;
|
|
|
|
*imports_key = CryptXorKeyGenerate_m256i();
|
|
|
|
/*
|
|
* Here we break down the import array into blocks of 32 bytes. Each
|
|
* block is loaded into an SSE register, xored with the key, and then
|
|
* copied back into the array.
|
|
*/
|
|
for (UINT32 block_index = 0; block_index < block_count; block_index++) {
|
|
__m256i current_block = {0};
|
|
__m256i load_block = {0};
|
|
__m256i xored_block = {0};
|
|
|
|
IntCopyMemory(
|
|
¤t_block,
|
|
&Array[block_index * block_size],
|
|
sizeof(__m256i));
|
|
|
|
load_block = _mm256_loadu_si256(¤t_block);
|
|
xored_block = _mm256_xor_si256(load_block, *imports_key);
|
|
|
|
IntCopyMemory(
|
|
&Array[block_index * block_size],
|
|
&xored_block,
|
|
sizeof(__m256i));
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
INLINE
|
|
__m256i
|
|
CryptDecryptImportBlock(_In_ PUINT64 Array, _In_ UINT32 BlockIndex)
|
|
{
|
|
__m256i load_block = {0};
|
|
__m256i* imports_key = GetDriverImportsKey();
|
|
UINT32 block_size = sizeof(__m256i) / sizeof(UINT64);
|
|
|
|
IntCopyMemory(
|
|
&load_block,
|
|
&Array[BlockIndex * block_size],
|
|
sizeof(__m256i));
|
|
|
|
return _mm256_xor_si256(load_block, *imports_key);
|
|
}
|
|
|
|
FORCEINLINE
|
|
INLINE
|
|
VOID
|
|
CryptFindContainingBlockForArrayIndex(
|
|
_In_ UINT32 EntryIndex,
|
|
_In_ UINT32 BlockSize,
|
|
_Out_ PUINT32 ContainingBlockIndex,
|
|
_Out_ PUINT32 BlockSubIndex)
|
|
{
|
|
UINT32 containing_block = EntryIndex;
|
|
UINT32 block_index = 0;
|
|
|
|
if (EntryIndex < BlockSize) {
|
|
*ContainingBlockIndex = 0;
|
|
*BlockSubIndex = EntryIndex;
|
|
return;
|
|
}
|
|
|
|
if (EntryIndex == BlockSize) {
|
|
*ContainingBlockIndex = 1;
|
|
*BlockSubIndex = 0;
|
|
return;
|
|
}
|
|
|
|
while (containing_block % BlockSize != 0) {
|
|
containing_block--;
|
|
block_index++;
|
|
}
|
|
|
|
*ContainingBlockIndex = containing_block / BlockSize;
|
|
*BlockSubIndex = block_index;
|
|
}
|
|
|
|
UINT64
|
|
CryptDecryptImportsArrayEntry(
|
|
_In_ PUINT64 Array, _In_ UINT32 Entries, _In_ UINT32 EntryIndex)
|
|
{
|
|
__m256i original_block = {0};
|
|
__m128i original_half = {0};
|
|
UINT32 block_size = sizeof(__m256i) / sizeof(UINT64);
|
|
UINT32 containing_block_index = 0;
|
|
UINT32 block_sub_index = 0;
|
|
UINT64 pointer = 0;
|
|
|
|
CryptFindContainingBlockForArrayIndex(
|
|
EntryIndex,
|
|
block_size,
|
|
&containing_block_index,
|
|
&block_sub_index);
|
|
|
|
original_block = CryptDecryptImportBlock(Array, containing_block_index);
|
|
|
|
if (block_sub_index < 2) {
|
|
original_half = _mm256_extracti128_si256(original_block, 0);
|
|
|
|
if (block_sub_index < 1)
|
|
pointer = _mm_extract_epi64(original_half, 0);
|
|
else
|
|
pointer = _mm_extract_epi64(original_half, 1);
|
|
}
|
|
else {
|
|
original_half = _mm256_extracti128_si256(original_block, 1);
|
|
|
|
if (block_sub_index == 2)
|
|
pointer = _mm_extract_epi64(original_half, 0);
|
|
else
|
|
pointer = _mm_extract_epi64(original_half, 1);
|
|
}
|
|
|
|
return pointer;
|
|
}
|
|
|
|
STATIC
|
|
PBCRYPT_KEY_DATA_BLOB_HEADER
|
|
CryptBuildBlobForKeyImport(_In_ PACTIVE_SESSION Session)
|
|
{
|
|
PBCRYPT_KEY_DATA_BLOB_HEADER blob = ExAllocatePool2(
|
|
POOL_FLAG_NON_PAGED,
|
|
sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + AES_256_KEY_SIZE,
|
|
POOL_TAG_CRYPT);
|
|
|
|
if (!blob)
|
|
return NULL;
|
|
|
|
blob->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
|
|
blob->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
|
|
blob->cbKeyData = AES_256_KEY_SIZE;
|
|
|
|
IntCopyMemory(
|
|
(UINT64)blob + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER),
|
|
Session->aes_key,
|
|
AES_256_KEY_SIZE);
|
|
|
|
return blob;
|
|
}
|
|
|
|
#define AES_256_BLOCK_SIZE 16
|
|
|
|
UINT32
|
|
CryptRequestRequiredBufferLength(_In_ UINT32 BufferLength)
|
|
{
|
|
// status = BCryptEncrypt(session->key_handle,
|
|
// lol,
|
|
// BufferLength,
|
|
// NULL,
|
|
// session->iv,
|
|
// sizeof(session->iv),
|
|
// NULL,
|
|
// 0,
|
|
// RequiredLength,
|
|
// 0);
|
|
|
|
// if (!NT_SUCCESS(status))
|
|
// DEBUG_ERROR("CryptRequestRequiredBufferLength -> BCryptEncrypt: %x",
|
|
// status);
|
|
|
|
return (BufferLength + AES_256_BLOCK_SIZE - 1) / AES_256_BLOCK_SIZE *
|
|
AES_256_BLOCK_SIZE;
|
|
}
|
|
|
|
/* Encrypts in place! */
|
|
NTSTATUS
|
|
CryptEncryptBuffer(_In_ PVOID Buffer, _In_ UINT32 BufferLength)
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
UINT32 data_copied = 0;
|
|
PACTIVE_SESSION session = GetActiveSession();
|
|
UCHAR local_iv[sizeof(session->iv)] = {0};
|
|
UINT64 buffer = (UINT64)Buffer;
|
|
UINT32 length = BufferLength;
|
|
|
|
/* The IV is consumed during every encrypt / decrypt procedure, so to ensure
|
|
* we have access to the iv we need to create a local copy.*/
|
|
IntCopyMemory(local_iv, session->iv, sizeof(session->iv));
|
|
|
|
/* We arent encrypting the first 16 bytes */
|
|
buffer = buffer + AES_256_BLOCK_SIZE;
|
|
length = length - AES_256_BLOCK_SIZE;
|
|
|
|
status = BCryptEncrypt(
|
|
session->key_handle,
|
|
buffer,
|
|
length,
|
|
NULL,
|
|
local_iv,
|
|
sizeof(local_iv),
|
|
buffer,
|
|
length,
|
|
&data_copied,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
DEBUG_ERROR("CryptEncryptBuffer -> BCryptEncrypt: %x", status);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Lock is held */
|
|
VOID
|
|
CryptCloseSessionCryptObjects()
|
|
{
|
|
PACTIVE_SESSION session = GetActiveSession();
|
|
|
|
if (session->key_handle) {
|
|
BCryptDestroyKey(session->key_handle);
|
|
session->key_handle = NULL;
|
|
}
|
|
|
|
if (session->key_object) {
|
|
ExFreePoolWithTag(session->key_object, POOL_TAG_CRYPT);
|
|
session->key_object = NULL;
|
|
}
|
|
|
|
session->key_object_length = 0;
|
|
}
|
|
|
|
NTSTATUS
|
|
CryptInitialiseSessionCryptObjects()
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
UINT32 data_copied = 0;
|
|
PACTIVE_SESSION session = GetActiveSession();
|
|
PBCRYPT_KEY_DATA_BLOB_HEADER blob = NULL;
|
|
BCRYPT_ALG_HANDLE* handle = GetCryptHandle_AES();
|
|
|
|
blob = CryptBuildBlobForKeyImport(session);
|
|
|
|
if (!blob)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
status = BCryptGetProperty(
|
|
*handle,
|
|
BCRYPT_OBJECT_LENGTH,
|
|
&session->key_object_length,
|
|
sizeof(UINT32),
|
|
&data_copied,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("BCryptGetProperty: %x", status);
|
|
goto end;
|
|
}
|
|
|
|
session->key_object = ExAllocatePool2(
|
|
POOL_FLAG_NON_PAGED,
|
|
session->key_object_length,
|
|
POOL_TAG_CRYPT);
|
|
|
|
if (!session->key_object) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto end;
|
|
}
|
|
|
|
DEBUG_INFO(
|
|
"key object: %llx, key_object_length: %lx",
|
|
session->key_object,
|
|
session->key_object_length);
|
|
|
|
status = BCryptImportKey(
|
|
*handle,
|
|
NULL,
|
|
BCRYPT_KEY_DATA_BLOB,
|
|
&session->key_handle,
|
|
session->key_object,
|
|
session->key_object_length,
|
|
blob,
|
|
sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + AES_256_KEY_SIZE,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("BCryptImportKey: %x", status);
|
|
ExFreePoolWithTag(session->key_object, POOL_TAG_CRYPT);
|
|
goto end;
|
|
}
|
|
|
|
end:
|
|
if (blob)
|
|
ExFreePoolWithTag(blob, POOL_TAG_CRYPT);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CryptInitialiseProvider()
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
BCRYPT_ALG_HANDLE* handle = GetCryptHandle_AES();
|
|
|
|
status = BCryptOpenAlgorithmProvider(
|
|
handle,
|
|
BCRYPT_AES_ALGORITHM,
|
|
NULL,
|
|
BCRYPT_PROV_DISPATCH);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
DEBUG_ERROR("BCryptOpenAlgorithmProvider: %x", status);
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
CryptCloseProvider()
|
|
{
|
|
BCRYPT_ALG_HANDLE* handle = GetCryptHandle_AES();
|
|
BCryptCloseAlgorithmProvider(*handle, 0);
|
|
}
|
|
|
|
/*
|
|
* Basic TPM EK Extraction implementation. Various sources were used alongside
|
|
* the various TPM specification manuals.
|
|
*
|
|
* https://github.com/tianocore/edk2
|
|
* https://github.com/microsoft/ms-tpm-20-ref
|
|
* https://github.com/SyncUD/tpm-mmio
|
|
*/
|
|
|
|
#define TPM20_INTEL_BASE_PHYSICAL 0xfed40000
|
|
#define TPM20_OBJECT_HANDLE_EK 0x81010001
|
|
#define TPM20_PTP_NO_VALID_CHIP 0xFF
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
TpmIsPlatformSupported()
|
|
{
|
|
PSYSTEM_INFORMATION system = GetDriverConfigSystemInformation();
|
|
|
|
if (system->processor == AuthenticAmd) {
|
|
DEBUG_ERROR(
|
|
"TpmPlatformSuport unavailable on process type: AuthenticAmd");
|
|
return FALSE;
|
|
}
|
|
|
|
if (system->processor == GenuineIntel)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC
|
|
NTSTATUS
|
|
TpmCheckPtpRegisterPresence(_In_ PVOID Register, _Out_ PUINT32 Result)
|
|
{
|
|
UINT8 value = 0;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
*Result = FALSE;
|
|
|
|
status = MapAndReadPhysical(Register, sizeof(value), &value, sizeof(value));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("MapAndReadPhysical: %x", status);
|
|
return status;
|
|
}
|
|
|
|
if (value != TPM20_PTP_NO_VALID_CHIP)
|
|
*Result = TRUE;
|
|
|
|
return status;
|
|
}
|
|
|
|
FORCEINLINE
|
|
STATIC
|
|
TPM2_PTP_INTERFACE_TYPE
|
|
TpmExtractInterfaceTypeFromCapabilityAndId(
|
|
_In_ PTP_CRB_INTERFACE_IDENTIFIER* Identifier,
|
|
_In_ PTP_FIFO_INTERFACE_CAPABILITY* Capability)
|
|
{
|
|
if ((Identifier->Bits.InterfaceType ==
|
|
PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_CRB) &&
|
|
(Identifier->Bits.InterfaceVersion ==
|
|
PTP_INTERFACE_IDENTIFIER_INTERFACE_VERSION_CRB) &&
|
|
(Identifier->Bits.CapCRB != 0)) {
|
|
return Tpm2PtpInterfaceCrb;
|
|
}
|
|
|
|
if ((Identifier->Bits.InterfaceType ==
|
|
PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_FIFO) &&
|
|
(Identifier->Bits.InterfaceVersion ==
|
|
PTP_INTERFACE_IDENTIFIER_INTERFACE_VERSION_FIFO) &&
|
|
(Identifier->Bits.CapFIFO != 0) &&
|
|
(Capability->Bits.InterfaceVersion ==
|
|
INTERFACE_CAPABILITY_INTERFACE_VERSION_PTP)) {
|
|
return Tpm2PtpInterfaceFifo;
|
|
}
|
|
|
|
if (Identifier->Bits.InterfaceType ==
|
|
PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_TIS) {
|
|
return Tpm2PtpInterfaceTis;
|
|
}
|
|
|
|
return Tpm2PtpInterfaceMax;
|
|
}
|
|
|
|
/*
|
|
* Assumes the presence of the register has already been confirmed via
|
|
* TpmCheckPtpRegisterPresence.
|
|
*/
|
|
STATIC
|
|
NTSTATUS
|
|
TpmGetPtpInterfaceType(
|
|
_In_ PVOID Register, _Out_ TPM2_PTP_INTERFACE_TYPE* InterfaceType)
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PTP_CRB_INTERFACE_IDENTIFIER identifier = {0};
|
|
PTP_FIFO_INTERFACE_CAPABILITY capability = {0};
|
|
|
|
*InterfaceType = 0;
|
|
|
|
status = MapAndReadPhysical(
|
|
(UINT64)(&((PTP_CRB_REGISTERS*)Register)->InterfaceId),
|
|
sizeof(PTP_CRB_INTERFACE_IDENTIFIER),
|
|
&identifier,
|
|
sizeof(PTP_CRB_INTERFACE_IDENTIFIER));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("MapAndReadPhysical: %x", status);
|
|
return status;
|
|
}
|
|
|
|
status = MapAndReadPhysical(
|
|
(UINT64) & ((PTP_FIFO_REGISTERS*)Register)->InterfaceCapability,
|
|
sizeof(PTP_FIFO_INTERFACE_CAPABILITY),
|
|
&capability,
|
|
sizeof(PTP_FIFO_INTERFACE_CAPABILITY));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("MapAndReadPhysical: %x", status);
|
|
return status;
|
|
}
|
|
|
|
*InterfaceType =
|
|
TpmExtractInterfaceTypeFromCapabilityAndId(&identifier, &capability);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
TpmExtractEndorsementKey()
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
BOOLEAN presence = FALSE;
|
|
TPM2_PTP_INTERFACE_TYPE type = {0};
|
|
|
|
if (!TpmIsPlatformSupported())
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
status = TpmCheckPtpRegisterPresence(TPM20_INTEL_BASE_PHYSICAL, &presence);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("TpmCheckPtpRegisterPresence: %x", status);
|
|
return status;
|
|
}
|
|
|
|
if (!presence) {
|
|
DEBUG_INFO("TPM2.0 PTP Presence not detected.");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
status = TpmGetPtpInterfaceType(TPM20_INTEL_BASE_PHYSICAL, &type);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("TpmGetPtpInterfaceType: %x", status);
|
|
return status;
|
|
}
|
|
|
|
DEBUG_INFO("TPM2.0 PTP Interface Type: %x", (UINT32)type);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CryptHashBuffer_sha256(
|
|
_In_ PVOID Buffer,
|
|
_In_ ULONG BufferSize,
|
|
_Out_ PVOID* HashResult,
|
|
_Out_ PULONG HashResultSize)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
BCRYPT_ALG_HANDLE* algo_handle = GetCryptHandle_Sha256();
|
|
BCRYPT_HASH_HANDLE hash_handle = NULL;
|
|
ULONG bytes_copied = 0;
|
|
ULONG resulting_hash_size = 0;
|
|
ULONG hash_object_size = 0;
|
|
PCHAR hash_object = NULL;
|
|
PCHAR resulting_hash = NULL;
|
|
|
|
*HashResult = NULL;
|
|
*HashResultSize = 0;
|
|
|
|
/*
|
|
* Request the size of the hash object buffer, this is different then
|
|
* the buffer that will store the resulting hash, instead this will be
|
|
* used to store the hash object used to create the hash.
|
|
*/
|
|
status = BCryptGetProperty(
|
|
*algo_handle,
|
|
BCRYPT_OBJECT_LENGTH,
|
|
(PCHAR)&hash_object_size,
|
|
sizeof(ULONG),
|
|
&bytes_copied,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("BCryptGetProperty failed with status %x", status);
|
|
goto end;
|
|
}
|
|
|
|
hash_object = ImpExAllocatePool2(
|
|
POOL_FLAG_NON_PAGED,
|
|
hash_object_size,
|
|
POOL_TAG_INTEGRITY);
|
|
|
|
if (!hash_object) {
|
|
status = STATUS_MEMORY_NOT_ALLOCATED;
|
|
goto end;
|
|
}
|
|
|
|
/*
|
|
* This call gets the size of the resulting hash, which we will use to
|
|
* allocate the resulting hash buffer.
|
|
*/
|
|
status = BCryptGetProperty(
|
|
*algo_handle,
|
|
BCRYPT_HASH_LENGTH,
|
|
(PCHAR)&resulting_hash_size,
|
|
sizeof(ULONG),
|
|
&bytes_copied,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("BCryptGetProperty failed with status %x", status);
|
|
goto end;
|
|
}
|
|
|
|
resulting_hash = ImpExAllocatePool2(
|
|
POOL_FLAG_NON_PAGED,
|
|
resulting_hash_size,
|
|
POOL_TAG_INTEGRITY);
|
|
|
|
if (!resulting_hash) {
|
|
status = STATUS_MEMORY_NOT_ALLOCATED;
|
|
goto end;
|
|
}
|
|
|
|
/*
|
|
* Here we create our hash object and store it in the hash_object
|
|
* buffer.
|
|
*/
|
|
status = BCryptCreateHash(
|
|
*algo_handle,
|
|
&hash_handle,
|
|
hash_object,
|
|
hash_object_size,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("BCryptCreateHash failed with status %x", status);
|
|
goto end;
|
|
}
|
|
|
|
/*
|
|
* This function hashes the buffer, but does NOT store it in our
|
|
* resulting buffer yet, we need to call BCryptFinishHash to retrieve
|
|
* the final hash.
|
|
*/
|
|
status = BCryptHashData(hash_handle, Buffer, BufferSize, NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("BCryptHashData failed with status %x", status);
|
|
goto end;
|
|
}
|
|
|
|
/*
|
|
* As said in the previous comment, this is where we retrieve the final
|
|
* hash and store it in our output buffer.
|
|
*/
|
|
status = BCryptFinishHash(
|
|
hash_handle,
|
|
resulting_hash,
|
|
resulting_hash_size,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DEBUG_ERROR("BCryptFinishHash failed with status %x", status);
|
|
goto end;
|
|
}
|
|
|
|
*HashResult = resulting_hash;
|
|
*HashResultSize = resulting_hash_size;
|
|
|
|
end:
|
|
|
|
if (hash_handle)
|
|
BCryptDestroyHash(hash_handle);
|
|
|
|
if (hash_object)
|
|
ImpExFreePoolWithTag(hash_object, POOL_TAG_INTEGRITY);
|
|
|
|
return status;
|
|
} |