Add reusable certificate and file validation helpers

Fix process-name ownership and module lookup outputs, add explicit SPKI hashing and bounded chain access, and improve WDM integration. Deprecate superseded entry points with migration guidance and align branch code and tests with the library style.

Add CI validation helpers with explicit signature selection and owned results

Document runtime-confirmed CI signer selection and crypto reuse

Expand CI compatibility matrix to twenty verified samples

Extend CI export matrix with Windows 11 x64 and ARM64 samples

Document CI API availability and historical mincrypt layouts

Add explicit Authenticode workflows and correct CI helper contracts

Document CI validation flag 0x20 and add kernel probe

Harden CI digest parsing and failure cleanup
This commit is contained in:
nefarius committed 2026-10-27 00:17:25 +01:00
1 parent 7985b23be5
commit 3fbfe31d15
38 files changed
+9201 -751

No files matched your search

+396
View File
@@ -0,0 +1,396 @@
#include "Domito.Internal.h"
typedef struct _DOMITO_CERTIFICATE_DER_VIEW
{
const UCHAR* Data;
ULONG Size;
} DOMITO_CERTIFICATE_DER_VIEW;
static BOOLEAN
DomitoCertificateDerTake(
DOMITO_CERTIFICATE_DER_VIEW* Input,
UCHAR Tag,
DOMITO_CERTIFICATE_DER_VIEW* Content,
DOMITO_CERTIFICATE_DER_VIEW* Encoded = nullptr
)
{
if (Input->Size < 2 || Input->Data[0] != Tag)
{
return FALSE;
}
ULONG length = Input->Data[1];
ULONG header = 2;
if (length & 0x80)
{
const ULONG count = length & 0x7f;
if (!count || count > 4 || count > Input->Size - 2 || !Input->Data[2])
{
return FALSE;
}
length = 0;
for (ULONG i = 0; i < count; ++i)
{
length = (length << 8) | Input->Data[header++];
}
if (length < 128)
{
return FALSE;
}
}
if (length > Input->Size - header)
{
return FALSE;
}
*Content = {Input->Data + header, length};
if (Encoded)
{
*Encoded = {Input->Data, header + length};
}
Input->Data += header + length;
Input->Size -= header + length;
return TRUE;
}
_Use_decl_annotations_
NTSTATUS
DomitoGetCertificateSpki(
const UCHAR* CertData,
ULONG CertSize,
DOMITO_CERTIFICATE_SPKI* Spki
)
{
PAGED_CODE();
if (!Spki)
{
return STATUS_INVALID_PARAMETER;
}
*Spki = {};
if (!CertData || !CertSize)
{
return STATUS_INVALID_PARAMETER;
}
DOMITO_CERTIFICATE_DER_VIEW certificateInput = {CertData, CertSize};
DOMITO_CERTIFICATE_DER_VIEW certificate;
DOMITO_CERTIFICATE_DER_VIEW tbsCertificate;
DOMITO_CERTIFICATE_DER_VIEW fieldContent;
DOMITO_CERTIFICATE_DER_VIEW encodedSpki;
DOMITO_CERTIFICATE_DER_VIEW spkiContent;
// Validate complete certificate framing, without interpreting trust/algorithms.
if (!DomitoCertificateDerTake(&certificateInput, 0x30, &certificate) || certificateInput.Size ||
!DomitoCertificateDerTake(&certificate, 0x30, &tbsCertificate) ||
!DomitoCertificateDerTake(&certificate, 0x30, &fieldContent) ||
!DomitoCertificateDerTake(&certificate, 0x03, &fieldContent) || !fieldContent.Size ||
fieldContent.Data[0] > 7 || certificate.Size)
{
return STATUS_INVALID_IMAGE_FORMAT;
}
if (tbsCertificate.Size && tbsCertificate.Data[0] == 0xa0)
{
DOMITO_CERTIFICATE_DER_VIEW certificateVersion;
if (!DomitoCertificateDerTake(&tbsCertificate, 0xa0, &fieldContent) ||
!DomitoCertificateDerTake(&fieldContent, 0x02, &certificateVersion) || fieldContent.Size ||
certificateVersion.Size != 1 || certificateVersion.Data[0] > 2)
{
return STATUS_INVALID_IMAGE_FORMAT;
}
}
const UCHAR tbsFieldTags[] = {0x02, 0x30, 0x30, 0x30, 0x30};
for (ULONG i = 0; i < RTL_NUMBER_OF(tbsFieldTags); ++i)
{
if (!DomitoCertificateDerTake(&tbsCertificate, tbsFieldTags[i], &fieldContent) ||
(i == 0 && !fieldContent.Size))
{
return STATUS_INVALID_IMAGE_FORMAT;
}
}
if (!DomitoCertificateDerTake(&tbsCertificate, 0x30, &spkiContent, &encodedSpki))
{
return STATUS_INVALID_IMAGE_FORMAT;
}
DOMITO_CERTIFICATE_DER_VIEW spkiFields = spkiContent;
DOMITO_CERTIFICATE_DER_VIEW keyAlgorithm;
DOMITO_CERTIFICATE_DER_VIEW algorithmOid;
DOMITO_CERTIFICATE_DER_VIEW publicKeyBits;
if (!DomitoCertificateDerTake(&spkiFields, 0x30, &keyAlgorithm) ||
!DomitoCertificateDerTake(&keyAlgorithm, 0x06, &algorithmOid) || !algorithmOid.Size ||
!DomitoCertificateDerTake(&spkiFields, 0x03, &publicKeyBits) || publicKeyBits.Size < 2 ||
publicKeyBits.Data[0] > 7 || spkiFields.Size)
{
return STATUS_INVALID_IMAGE_FORMAT;
}
Spki->Encoded = {encodedSpki.Data, encodedSpki.Size};
Spki->Content = {spkiContent.Data, spkiContent.Size};
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS
DomitoHashBuffer(
const UCHAR* Bytes,
ULONG Size,
ALG_ID Algorithm,
UCHAR* Hash,
ULONG* HashSize
)
{
PAGED_CODE();
if (!HashSize)
{
return STATUS_INVALID_PARAMETER;
}
const ULONG capacity = *HashSize;
ULONG required;
switch (Algorithm)
{
case CALG_SHA1:
required = 20;
break;
case CALG_SHA_256:
required = 32;
break;
case CALG_SHA_384:
required = 48;
break;
case CALG_SHA_512:
required = 64;
break;
default:
*HashSize = 0;
return STATUS_NOT_SUPPORTED;
}
if (!Bytes && Size)
{
*HashSize = 0;
return STATUS_INVALID_PARAMETER;
}
if (!Hash || capacity < required)
{
*HashSize = required;
return STATUS_BUFFER_TOO_SMALL;
}
RtlZeroMemory(Hash, required);
BCRYPT_ALG_HANDLE algorithm = nullptr;
BCRYPT_HASH_HANDLE hash = nullptr;
NTSTATUS status =
BCryptOpenAlgorithmProvider(&algorithm, DOMITO_CALG_TO_BCRYPT_ALGORITHM(Algorithm), MS_PRIMITIVE_PROVIDER, 0);
if (NT_SUCCESS(status))
{
status = BCryptCreateHash(algorithm, &hash, nullptr, 0, nullptr, 0, 0);
}
if (NT_SUCCESS(status) && Size)
{
status = BCryptHashData(hash, const_cast<PUCHAR>(Bytes), Size, 0);
}
if (NT_SUCCESS(status))
{
status = BCryptFinishHash(hash, Hash, required, 0);
}
if (hash)
{
BCryptDestroyHash(hash);
}
if (algorithm)
{
BCryptCloseAlgorithmProvider(algorithm, 0);
}
if (NT_SUCCESS(status))
{
*HashSize = required;
}
else
{
RtlZeroMemory(Hash, required);
*HashSize = 0;
}
return status;
}
_Use_decl_annotations_
NTSTATUS
DomitoHashCertificate(
const UCHAR* CertData,
ULONG CertSize,
DOMITO_CERTIFICATE_HASH_KIND Kind,
ALG_ID Algorithm,
UCHAR* Hash,
ULONG* HashSize
)
{
PAGED_CODE();
if (!HashSize)
{
return STATUS_INVALID_PARAMETER;
}
if (!CertData || !CertSize)
{
*HashSize = 0;
return STATUS_INVALID_PARAMETER;
}
DOMITO_CERTIFICATE_SPKI spki = {};
if (Kind == DomitoCertificateHashSpkiDer || Kind == DomitoCertificateHashSpkiContentLegacy)
{
NTSTATUS status = DomitoGetCertificateSpki(CertData, CertSize, &spki);
if (!NT_SUCCESS(status))
{
*HashSize = 0;
return status;
}
const DOMITO_BYTE_VIEW selected = Kind == DomitoCertificateHashSpkiDer ? spki.Encoded : spki.Content;
return DomitoHashBuffer(selected.Data, selected.Size, Algorithm, Hash, HashSize);
}
if (Kind != DomitoCertificateHashDer)
{
*HashSize = 0;
return STATUS_INVALID_PARAMETER;
}
// Whole-certificate fingerprint: hashes exact input bytes, no trust claim.
return DomitoHashBuffer(CertData, CertSize, Algorithm, Hash, HashSize);
}
static int
DomitoHexDigit(
WCHAR Value
)
{
if (Value >= L'0' && Value <= L'9')
{
return Value - L'0';
}
if (Value >= L'A' && Value <= L'F')
{
return Value - L'A' + 10;
}
if (Value >= L'a' && Value <= L'f')
{
return Value - L'a' + 10;
}
return -1;
}
_Use_decl_annotations_
NTSTATUS
DomitoDecodeHex(
const UNICODE_STRING* Hex,
UCHAR* Bytes,
ULONG ByteCount
)
{
PAGED_CODE();
if (!Bytes && ByteCount)
{
return STATUS_INVALID_PARAMETER;
}
if (Bytes && ByteCount)
{
RtlZeroMemory(Bytes, ByteCount);
}
if (!Hex || Hex->Length > Hex->MaximumLength || (Hex->Length && !Hex->Buffer) ||
ByteCount > MAXUSHORT / (2 * sizeof(WCHAR)) || Hex->Length != ByteCount * 2 * sizeof(WCHAR))
{
return STATUS_INVALID_PARAMETER;
}
for (ULONG i = 0; i < ByteCount * 2; ++i)
{
if (DomitoHexDigit(Hex->Buffer[i]) < 0)
{
return STATUS_INVALID_PARAMETER;
}
}
for (ULONG i = 0; i < ByteCount; ++i)
{
Bytes[i] =
static_cast<UCHAR>((DomitoHexDigit(Hex->Buffer[2 * i]) << 4) | DomitoHexDigit(Hex->Buffer[2 * i + 1]));
}
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS
DomitoEncodeHex(
const UCHAR* Bytes,
ULONG ByteCount,
WCHAR* Hex,
ULONG HexChars
)
{
PAGED_CODE();
if (Hex && HexChars)
{
Hex[0] = 0;
}
if ((!Bytes && ByteCount) || !Hex)
{
return STATUS_INVALID_PARAMETER;
}
if (!HexChars || ByteCount > (HexChars - 1) / 2)
{
return STATUS_BUFFER_TOO_SMALL;
}
static const WCHAR digits[] = L"0123456789ABCDEF";
WCHAR* destination = Hex;
for (ULONG i = 0; i < ByteCount; ++i)
{
*destination++ = digits[Bytes[i] >> 4];
*destination++ = digits[Bytes[i] & 15];
}
*destination = 0;
return STATUS_SUCCESS;
}
+216
View File
@@ -0,0 +1,216 @@
#include "Domito.Internal.h"
#if (NTDDI_VERSION >= NTDDI_WIN10)
static UINT32
DomitoCiDigestSize(
ALG_ID Algorithm
)
{
switch (Algorithm)
{
case CALG_SHA1:
return MINCRYPT_SHA1_LENGTH;
case CALG_SHA_256:
return MINCRYPT_SHA256_LENGTH;
case CALG_SHA_384:
return MINCRYPT_SHA384_LENGTH;
case CALG_SHA_512:
return MINCRYPT_SHA512_LENGTH;
default:
return 0;
}
}
static void
DomitoClearCiDigest(
DOMITO_CI_VALIDATION_RESULT* Result
)
{
RtlZeroMemory(Result->Hash, sizeof(Result->Hash));
Result->HashSize = 0;
Result->HashAlgId = 0;
Result->SigningTime.QuadPart = 0;
}
_Use_decl_annotations_
void
DomitoFreeCiValidationResult(
DOMITO_CI_VALIDATION_RESULT* Result
)
{
PAGED_CODE();
if (!Result)
{
return;
}
CiFreePolicyInfo(&Result->PolicyInfo);
CiFreePolicyInfo(&Result->TimeStampPolicyInfo);
RtlZeroMemory(Result, sizeof(*Result));
}
_Use_decl_annotations_
NTSTATUS
DomitoValidateFileObjectEx(
PFILE_OBJECT FileObject,
UINT32 ValidationFlags,
UINT8 RequestedSigningLevel,
DOMITO_CI_SIGNATURE_SELECTION Selection,
DOMITO_CI_VALIDATION_RESULT* Result
)
{
PAGED_CODE();
if (!Result)
{
return STATUS_INVALID_PARAMETER;
}
DomitoFreeCiValidationResult(Result);
if (!FileObject)
{
return STATUS_INVALID_PARAMETER;
}
if (Selection != DomitoCiSignatureSelectionNative &&
Selection != DomitoCiSignatureSelectionFirstSuccessfulExperimental)
{
return STATUS_INVALID_PARAMETER;
}
if (Selection == DomitoCiSignatureSelectionFirstSuccessfulExperimental)
{
#if defined(_M_AMD64) || defined(_AMD64_)
ValidationFlags |= DOMITO_CI_VALIDATE_FLAG_FIRST_SUCCESS_EXPERIMENTAL;
#else
return STATUS_NOT_SUPPORTED;
#endif
}
Result->FileObjectValidationFlags = ValidationFlags;
Result->RequestedSigningLevel = RequestedSigningLevel;
Result->HashSize = sizeof(Result->Hash);
NTSTATUS status = CiValidateFileObject(
FileObject,
ValidationFlags,
RequestedSigningLevel,
&Result->PolicyInfo,
&Result->TimeStampPolicyInfo,
&Result->SigningTime,
Result->Hash,
&Result->HashSize,
&Result->HashAlgId
);
if (NT_SUCCESS(status))
{
const UINT32 expected = DomitoCiDigestSize(Result->HashAlgId);
if (!expected)
{
status = STATUS_NOT_SUPPORTED;
}
else if (Result->HashSize != expected || Result->HashSize > sizeof(Result->Hash))
{
status = STATUS_INVALID_BUFFER_SIZE;
}
}
if (!NT_SUCCESS(status))
{
DomitoClearCiDigest(Result);
}
else
{
RtlZeroMemory(Result->Hash + Result->HashSize, sizeof(Result->Hash) - Result->HashSize);
}
return status;
}
_Use_decl_annotations_
NTSTATUS
DomitoValidatePortableExecutableEmbeddedSignature(
const UCHAR* PeBytes,
ULONG PeSize,
ALG_ID HashAlgId,
DOMITO_CI_VALIDATION_RESULT* Result
)
{
PAGED_CODE();
if (!Result)
{
return STATUS_INVALID_PARAMETER;
}
DomitoFreeCiValidationResult(Result);
if (!PeBytes)
{
return STATUS_INVALID_PARAMETER;
}
const UINT32 expected = DomitoCiDigestSize(HashAlgId);
if (!expected)
{
return STATUS_NOT_SUPPORTED;
}
PVOID digest = nullptr;
ULONG digestSize = 0;
ULONG securitySize = 0;
LPWIN_CERTIFICATE certificate = nullptr;
NTSTATUS status = DomitoCalculatePortableExecutableDigestEx(
PeBytes, PeSize, HashAlgId, &digestSize, &digest, &certificate, &securitySize
);
if (NT_SUCCESS(status))
{
if (!certificate || !securitySize)
{
status = STATUS_INVALID_IMAGE_HASH;
}
else if (!digest || digestSize != expected || digestSize > sizeof(Result->Hash))
{
status = STATUS_INVALID_BUFFER_SIZE;
}
else
{
status = CiCheckSignedFile(
digest,
digestSize,
HashAlgId,
certificate,
securitySize,
&Result->PolicyInfo,
&Result->SigningTime,
&Result->TimeStampPolicyInfo
);
if (NT_SUCCESS(status))
{
RtlCopyMemory(Result->Hash, digest, digestSize);
Result->HashSize = digestSize;
Result->HashAlgId = HashAlgId;
}
}
}
if (digest)
{
DomitoFreePortableExecutableDigest(digest);
}
if (!NT_SUCCESS(status))
{
DomitoClearCiDigest(Result);
}
return status;
}
#endif // NTDDI_VERSION >= NTDDI_WIN10
File diff suppressed because it is too large. Load diff
+395
View File
@@ -0,0 +1,395 @@
#include "Domito.Internal.h"
_Use_decl_annotations_
NTSTATUS
DomitoFindModuleExportedFunctionAddress(
PANSI_STRING ModuleName,
PANSI_STRING FunctionName,
PVOID* FunctionAddress
)
{
PAGED_CODE();
if (!FunctionAddress)
{
return STATUS_INVALID_PARAMETER;
}
*FunctionAddress = nullptr;
if (!ModuleName || !ModuleName->Buffer || !ModuleName->Length || ModuleName->Length > ModuleName->MaximumLength ||
!FunctionName || !FunctionName->Buffer || !FunctionName->Length ||
FunctionName->Length > FunctionName->MaximumLength)
{
return STATUS_INVALID_PARAMETER;
}
PVOID moduleBase = nullptr;
NTSTATUS status = DomitoFindModuleBaseAddress(ModuleName, &moduleBase);
if (!NT_SUCCESS(status))
{
return status;
}
return DomitoFindExportedFunctionAddress(moduleBase, FunctionName, FunctionAddress);
}
_Use_decl_annotations_
void
DomitoFreeProcessImageName(
PUNICODE_STRING ImageName
)
{
PAGED_CODE();
if (ImageName)
{
G_Memory.Free(ImageName);
}
}
_Use_decl_annotations_
NTSTATUS
DomitoGetProcessImageNameEx(
PEPROCESS Process,
PUNICODE_STRING* ImageName
)
{
PAGED_CODE();
if (!ImageName)
{
return STATUS_INVALID_PARAMETER;
}
*ImageName = nullptr;
if (!Process)
{
return STATUS_INVALID_PARAMETER;
}
PUNICODE_STRING processImageName = nullptr;
NTSTATUS status = SeLocateProcessImageName(Process, &processImageName);
if (NT_SUCCESS(status))
{
if (!processImageName || !processImageName->Buffer || !processImageName->Length ||
(processImageName->Length & 1) || processImageName->Length > processImageName->MaximumLength ||
processImageName->Length > MAXUSHORT - sizeof(WCHAR))
{
status = STATUS_INVALID_BUFFER_SIZE;
}
else
{
const SIZE_T size = sizeof(UNICODE_STRING) + processImageName->Length + sizeof(WCHAR);
PUNICODE_STRING imageName = static_cast<PUNICODE_STRING>(G_Memory.Allocate(size));
if (!imageName)
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
imageName->Buffer = reinterpret_cast<PWCH>(imageName + 1);
imageName->Length = processImageName->Length;
imageName->MaximumLength = processImageName->Length + sizeof(WCHAR);
RtlCopyMemory(imageName->Buffer, processImageName->Buffer, processImageName->Length);
imageName->Buffer[imageName->Length / sizeof(WCHAR)] = 0;
*ImageName = imageName;
}
}
}
if (processImageName)
{
ExFreePool(processImageName);
}
return status;
}
#if (NTDDI_VERSION >= NTDDI_WIN10)
_Use_decl_annotations_
NTSTATUS
DomitoValidateFileHandleEx(
HANDLE FileHandle,
UINT32 ValidationFlags,
UINT8 RequestedSigningLevel,
DOMITO_CI_SIGNATURE_SELECTION Selection,
DOMITO_CI_VALIDATION_RESULT* Result
)
{
PAGED_CODE();
if (!Result)
{
return STATUS_INVALID_PARAMETER;
}
DomitoFreeCiValidationResult(Result);
if (!FileHandle)
{
return STATUS_INVALID_PARAMETER;
}
PFILE_OBJECT fileObject = nullptr;
NTSTATUS status = ObReferenceObjectByHandle(
FileHandle, FILE_READ_DATA, *IoFileObjectType, KernelMode, reinterpret_cast<PVOID*>(&fileObject), nullptr
);
if (!NT_SUCCESS(status))
{
return status;
}
status = DomitoValidateFileObjectEx(fileObject, ValidationFlags, RequestedSigningLevel, Selection, Result);
ObDereferenceObject(fileObject);
return status;
}
_Use_decl_annotations_
NTSTATUS
DomitoValidateFilePathEx(
const UNICODE_STRING* Path,
UINT32 ValidationFlags,
UINT8 RequestedSigningLevel,
DOMITO_CI_SIGNATURE_SELECTION Selection,
DOMITO_CI_VALIDATION_RESULT* Result
)
{
PAGED_CODE();
if (!Result)
{
return STATUS_INVALID_PARAMETER;
}
DomitoFreeCiValidationResult(Result);
if (!Path || !Path->Buffer || Path->Length < sizeof(WCHAR) || (Path->Length & 1) ||
Path->Length > Path->MaximumLength || Path->Buffer[0] != L'\\')
{
return STATUS_INVALID_PARAMETER;
}
for (USHORT i = 0; i < Path->Length / sizeof(WCHAR); ++i)
{
if (!Path->Buffer[i])
{
return STATUS_INVALID_PARAMETER;
}
}
OBJECT_ATTRIBUTES attributes;
InitializeObjectAttributes(
&attributes, const_cast<PUNICODE_STRING>(Path), OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, nullptr, nullptr
);
HANDLE fileHandle = nullptr;
IO_STATUS_BLOCK iosb = {};
NTSTATUS status = ZwOpenFile(
&fileHandle,
FILE_READ_DATA | SYNCHRONIZE,
&attributes,
&iosb,
FILE_SHARE_READ,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
);
if (!NT_SUCCESS(status))
{
return status;
}
status = DomitoValidateFileHandleEx(fileHandle, ValidationFlags, RequestedSigningLevel, Selection, Result);
ZwClose(fileHandle);
return status;
}
#if defined(_M_AMD64) || defined(_AMD64_)
static BOOLEAN
DomitoCiOwnedSpan(
const MINCRYPT_CHAIN_INFO* Chain,
const void* Data,
SIZE_T Size
)
{
const ULONG_PTR chainBaseAddress = reinterpret_cast<ULONG_PTR>(Chain);
const ULONG_PTR blobAddress = reinterpret_cast<ULONG_PTR>(Data);
// Empty blobs may be NULL; they are never dereferenced by this accessor.
if (!Size)
{
return TRUE;
}
return blobAddress >= chainBaseAddress && blobAddress - chainBaseAddress >= 56 &&
blobAddress - chainBaseAddress <= Chain->Size && Size <= Chain->Size - (blobAddress - chainBaseAddress);
}
#endif
_Use_decl_annotations_
NTSTATUS
DomitoGetCiChainView(
const MINCRYPT_POLICY_INFO* Policy,
DOMITO_CI_CHAIN_LAYOUT Layout,
DOMITO_CI_CHAIN_VIEW* View
)
{
PAGED_CODE();
if (!View)
{
return STATUS_INVALID_PARAMETER;
}
*View = {};
if (!Policy || Layout != DomitoCiChainLayoutObservedX64)
{
return STATUS_INVALID_PARAMETER;
}
#if defined(_M_AMD64) || defined(_AMD64_)
C_ASSERT(sizeof(MINCRYPT_CHAIN_ELEMENT) == 120);
C_ASSERT(FIELD_OFFSET(MINCRYPT_CHAIN_INFO, ChainElements) == 40);
C_ASSERT(FIELD_OFFSET(MINCRYPT_CHAIN_INFO, NumberOfChainElement) == 48);
if (Policy->Size != sizeof(*Policy))
{
return STATUS_NOT_SUPPORTED;
}
const MINCRYPT_CHAIN_INFO* chain = Policy->ChainInfo;
if (!chain)
{
return STATUS_NOT_FOUND;
}
if (chain->Size < 56)
{
return STATUS_INVALID_BUFFER_SIZE;
}
const ULONG count = chain->NumberOfChainElement;
if (!count)
{
return STATUS_NOT_FOUND;
}
if (count > (chain->Size - 56) / sizeof(MINCRYPT_CHAIN_ELEMENT) ||
!DomitoCiOwnedSpan(chain, chain->ChainElements, static_cast<SIZE_T>(count) * sizeof(MINCRYPT_CHAIN_ELEMENT)))
{
return STATUS_INVALID_BUFFER_SIZE;
}
for (ULONG i = 0; i < count; ++i)
{
const MINCRYPT_CHAIN_ELEMENT& element = chain->ChainElements[i];
if (element.HashSize > sizeof(element.Hash) || !element.Certificate.Size ||
!DomitoCiOwnedSpan(chain, element.Certificate.Data, element.Certificate.Size) ||
!DomitoCiOwnedSpan(chain, element.Subject.Data, element.Subject.Size) ||
!DomitoCiOwnedSpan(chain, element.Issuer.Data, element.Issuer.Size))
{
return STATUS_INVALID_BUFFER_SIZE;
}
}
ULONG rootId;
RtlCopyMemory(&rootId, reinterpret_cast<const UCHAR*>(chain) + 52, sizeof(rootId));
*View = {chain->ChainElements, count, rootId};
return STATUS_SUCCESS;
#else
return STATUS_NOT_SUPPORTED;
#endif
}
static MINCRYPT_ALLOCATE_ROUTINE DomitoAllocatePublisher;
static PVOID NTAPI
DomitoAllocatePublisher(
SIZE_T Size
)
{
#pragma warning(suppress : 4996)
return ExAllocatePoolWithTag(PagedPool, Size, DOMITO_POOL_TAG);
}
_Use_decl_annotations_
void
DomitoFreeCertificatePublisherName(
PUNICODE_STRING PublisherName
)
{
PAGED_CODE();
if (!PublisherName)
{
return;
}
if (PublisherName->Buffer)
{
ExFreePoolWithTag(PublisherName->Buffer, DOMITO_POOL_TAG);
}
*PublisherName = {};
}
_Use_decl_annotations_
NTSTATUS
DomitoGetCertificatePublisherName(
const MINCERT_BLOB* Certificate,
PUNICODE_STRING PublisherName
)
{
PAGED_CODE();
if (!PublisherName)
{
return STATUS_INVALID_PARAMETER;
}
*PublisherName = {};
if (!Certificate || !Certificate->Data || !Certificate->Size)
{
return STATUS_INVALID_PARAMETER;
}
ANSI_STRING moduleName = RTL_CONSTANT_STRING("\\SystemRoot\\system32\\CI.dll");
ANSI_STRING exportName = RTL_CONSTANT_STRING("CiGetCertPublisherName");
PVOID publisherRoutine = nullptr;
NTSTATUS status = DomitoFindModuleExportedFunctionAddress(&moduleName, &exportName, &publisherRoutine);
if (!NT_SUCCESS(status))
{
return status;
}
if (!publisherRoutine)
{
return STATUS_PROCEDURE_NOT_FOUND;
}
typedef NTSTATUS(NTAPI * GET_PUBLISHER)(MINCERT_BLOB*, PMINCRYPT_ALLOCATE_ROUTINE, PUNICODE_STRING);
const GET_PUBLISHER getPublisher = reinterpret_cast<GET_PUBLISHER>(publisherRoutine);
status = getPublisher(const_cast<MINCERT_BLOB*>(Certificate), DomitoAllocatePublisher, PublisherName);
if (NT_SUCCESS(status) && (PublisherName->Length > PublisherName->MaximumLength || (PublisherName->Length & 1) ||
!PublisherName->Length || !PublisherName->Buffer))
{
status = STATUS_INVALID_BUFFER_SIZE;
}
if (!NT_SUCCESS(status))
{
DomitoFreeCertificatePublisherName(PublisherName);
}
return status;
}
#endif
+305 -157
View File
@@ -7,18 +7,26 @@
#include "Domito.Internal.h"
DOMITO_COMMON G_Common = {};
DECLARE_GLOBAL_CONST_UNICODE_STRING(G_QipRoutineName, L"ZwQueryInformationProcess");
DECLARE_GLOBAL_CONST_UNICODE_STRING(G_IdetdRoutineName, L"RtlImageDirectoryEntryToData");
DECLARE_GLOBAL_CONST_UNICODE_STRING(G_QsiRoutineName, L"ZwQuerySystemInformation");
DECLARE_GLOBAL_CONST_UNICODE_STRING(
G_QipRoutineName,
L"ZwQueryInformationProcess"
);
DECLARE_GLOBAL_CONST_UNICODE_STRING(
G_IdetdRoutineName,
L"RtlImageDirectoryEntryToData"
);
DECLARE_GLOBAL_CONST_UNICODE_STRING(
G_QsiRoutineName,
L"ZwQuerySystemInformation"
);
#ifndef LOG
#define LOG(Format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Domito][" __FUNCTION__ "] " Format " \n", __VA_ARGS__)
#define LOG(Format, ...) \
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[Domito][" __FUNCTION__ "] " Format " \n", __VA_ARGS__)
#endif
_Success_(return == STATUS_SUCCESS)
_Must_inspect_result_
_IRQL_requires_max_(PASSIVE_LEVEL)
@@ -27,7 +35,7 @@ DomitoInit()
{
//
// Do those first since the follow-up code depends on them
//
//
G_Common.ZwQueryInformationProcess =
(t_ZwQueryInformationProcess)MmGetSystemRoutineAddress((PUNICODE_STRING)&G_QipRoutineName);
@@ -43,19 +51,27 @@ _IRQL_requires_max_(PASSIVE_LEVEL)
void
DomitoShutdown()
{
}
_Success_(return == STATUS_SUCCESS)
_Must_inspect_result_
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
DomitoFindModuleBaseAddress(
_In_ PANSI_STRING ModuleName,
_Inout_opt_ PVOID * ModuleBase
_Inout_opt_ PVOID* ModuleBase
)
{
if (ModuleBase)
{
*ModuleBase = nullptr;
}
if (!ModuleName || !ModuleName->Buffer || !ModuleName->Length || ModuleName->Length > ModuleName->MaximumLength)
{
return STATUS_INVALID_PARAMETER;
}
ULONG bufferSize = 0;
PSYSTEM_MODULE_INFORMATION moduleInfo = NULL;
@@ -67,12 +83,7 @@ DomitoFindModuleBaseAddress(
const ULONG SystemModuleInformation = 11;
// Query the required buffer size for module information
NTSTATUS status = G_Common.ZwQuerySystemInformation(
SystemModuleInformation,
&bufferSize,
0,
&bufferSize
);
NTSTATUS status = G_Common.ZwQuerySystemInformation(SystemModuleInformation, &bufferSize, 0, &bufferSize);
if (status != STATUS_INFO_LENGTH_MISMATCH)
{
@@ -80,9 +91,7 @@ DomitoFindModuleBaseAddress(
}
// Allocate memory for the module information
moduleInfo = (PSYSTEM_MODULE_INFORMATION)G_Memory.Allocate(
bufferSize
);
moduleInfo = (PSYSTEM_MODULE_INFORMATION)G_Memory.Allocate(bufferSize);
if (moduleInfo == NULL)
{
@@ -90,12 +99,7 @@ DomitoFindModuleBaseAddress(
}
// Retrieve the module information
status = G_Common.ZwQuerySystemInformation(
SystemModuleInformation,
moduleInfo,
bufferSize,
NULL
);
status = G_Common.ZwQuerySystemInformation(SystemModuleInformation, moduleInfo, bufferSize, NULL);
if (!NT_SUCCESS(status))
{
@@ -120,6 +124,7 @@ DomitoFindModuleBaseAddress(
{
*ModuleBase = moduleInfo->Module[i].Base;
}
break;
}
}
@@ -129,6 +134,19 @@ DomitoFindModuleBaseAddress(
return status;
}
// This helper handles directory RVAs after the loader-mapped header establishes
// SizeOfImage. ModuleBase is not an arbitrary file buffer (there is no size input).
static BOOLEAN
DomitoImageRvaSpan(
ULONG ImageSize,
ULONG Rva,
ULONG Count,
ULONG ElementSize
)
{
return Rva <= ImageSize && ElementSize != 0 && Count <= (ImageSize - Rva) / ElementSize;
}
_Success_(return == STATUS_SUCCESS)
_Must_inspect_result_
_IRQL_requires_max_(PASSIVE_LEVEL)
@@ -136,59 +154,146 @@ NTSTATUS
DomitoFindExportedFunctionAddress(
_In_ PVOID ModuleBase,
_In_ PANSI_STRING FunctionName,
_Inout_opt_ PVOID * FunctionAddress
_Inout_opt_ PVOID* FunctionAddress
)
{
NTSTATUS status = STATUS_NOT_FOUND;
ULONG exportSize;
if (FunctionAddress)
{
*FunctionAddress = nullptr;
}
if (G_Common.RtlImageDirectoryEntryToData == NULL)
if (!ModuleBase || !FunctionName || !FunctionName->Buffer || !FunctionName->Length)
{
return STATUS_INVALID_PARAMETER;
}
if (!G_Common.RtlImageDirectoryEntryToData)
{
return STATUS_NOT_IMPLEMENTED;
}
// Retrieve the export directory information
const PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)G_Common.RtlImageDirectoryEntryToData(
ModuleBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&exportSize
);
// Initial headers belong to a valid loaded module, as required by this API.
const PUCHAR base = (PUCHAR)ModuleBase;
IMAGE_DOS_HEADER dos;
memcpy(&dos, base, sizeof(dos));
if (exportDirectory == NULL)
if (dos.e_magic != IMAGE_DOS_SIGNATURE || dos.e_lfanew < (LONG)sizeof(dos))
{
return STATUS_INVALID_IMAGE_FORMAT;
}
STRING currentFunctionName;
const PUCHAR nt = base + dos.e_lfanew;
ULONG signature;
memcpy(&signature, nt, sizeof(signature));
USHORT magic;
memcpy(&magic, nt + sizeof(ULONG) + sizeof(IMAGE_FILE_HEADER), sizeof(magic));
const PULONG functionAddresses = (PULONG)((ULONG_PTR)ModuleBase + exportDirectory->AddressOfFunctions);
const PULONG functionNames = (PULONG)((ULONG_PTR)ModuleBase + exportDirectory->AddressOfNames);
const PUSHORT functionOrdinals = (PUSHORT)((ULONG_PTR)ModuleBase + exportDirectory->AddressOfNameOrdinals);
for (ULONG i = 0; i < exportDirectory->NumberOfNames; i++)
if (signature != IMAGE_NT_SIGNATURE ||
(magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC))
{
const char* functionName = (const char*)((ULONG_PTR)ModuleBase + functionNames[i]);
const USHORT functionOrdinal = functionOrdinals[i];
UNREFERENCED_PARAMETER(functionOrdinal);
const ULONG functionRva = functionAddresses[i];
const PVOID functionAddress = (PVOID)((ULONG_PTR)ModuleBase + functionRva);
RtlInitAnsiString(&currentFunctionName, functionName);
if (0 == RtlCompareString(FunctionName, &currentFunctionName, TRUE))
{
if (FunctionAddress)
{
status = STATUS_SUCCESS;
*FunctionAddress = functionAddress;
}
break;
}
return STATUS_INVALID_IMAGE_FORMAT;
}
return status;
// SizeOfImage is at the same offset in PE32 and PE32+ optional headers.
ULONG imageSize;
memcpy(
&imageSize,
nt + sizeof(ULONG) + sizeof(IMAGE_FILE_HEADER) + FIELD_OFFSET(IMAGE_OPTIONAL_HEADER32, SizeOfImage),
sizeof(imageSize)
);
if (!DomitoImageRvaSpan(
imageSize,
(ULONG)dos.e_lfanew,
sizeof(ULONG) + sizeof(IMAGE_FILE_HEADER) + FIELD_OFFSET(IMAGE_OPTIONAL_HEADER32, SizeOfImage) +
sizeof(imageSize),
1
))
{
return STATUS_INVALID_IMAGE_FORMAT;
}
ULONG exportSize = 0;
const PVOID directory =
G_Common.RtlImageDirectoryEntryToData(ModuleBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &exportSize);
const ULONG_PTR directoryOffset = (ULONG_PTR)directory - (ULONG_PTR)ModuleBase;
if (!directory || directoryOffset > imageSize || exportSize < sizeof(IMAGE_EXPORT_DIRECTORY) ||
exportSize > imageSize - directoryOffset)
{
return STATUS_INVALID_IMAGE_FORMAT;
}
IMAGE_EXPORT_DIRECTORY exports;
memcpy(&exports, directory, sizeof(exports));
if (!DomitoImageRvaSpan(imageSize, exports.AddressOfFunctions, exports.NumberOfFunctions, sizeof(ULONG)) ||
!DomitoImageRvaSpan(imageSize, exports.AddressOfNames, exports.NumberOfNames, sizeof(ULONG)) ||
!DomitoImageRvaSpan(imageSize, exports.AddressOfNameOrdinals, exports.NumberOfNames, sizeof(USHORT)))
{
return STATUS_INVALID_IMAGE_FORMAT;
}
for (ULONG i = 0; i < exports.NumberOfNames; ++i)
{
ULONG nameRva;
USHORT ordinal;
memcpy(&nameRva, base + exports.AddressOfNames + i * sizeof(ULONG), sizeof(nameRva));
memcpy(&ordinal, base + exports.AddressOfNameOrdinals + i * sizeof(USHORT), sizeof(ordinal));
if (nameRva >= imageSize || ordinal >= exports.NumberOfFunctions)
{
return STATUS_INVALID_IMAGE_FORMAT;
}
ULONG length = 0;
while (length < imageSize - nameRva && base[nameRva + length] != 0)
{
++length;
}
if (length == imageSize - nameRva || length > MAXUSHORT)
{
return STATUS_INVALID_IMAGE_FORMAT;
}
ANSI_STRING current;
current.Buffer = (PCHAR)(base + nameRva);
current.Length = (USHORT)length;
current.MaximumLength = (USHORT)length;
if (RtlCompareString(FunctionName, &current, TRUE) != 0)
{
continue;
}
ULONG functionRva;
memcpy(&functionRva, base + exports.AddressOfFunctions + ordinal * sizeof(ULONG), sizeof(functionRva));
if (!functionRva)
{
return STATUS_NOT_FOUND; // Empty EAT slot.
}
if (functionRva >= imageSize)
{
return STATUS_INVALID_IMAGE_FORMAT;
}
if (functionRva >= directoryOffset && functionRva - directoryOffset < exportSize)
{
return STATUS_NOT_SUPPORTED; // Forwarder string, not a callable address.
}
if (FunctionAddress)
{
*FunctionAddress = base + functionRva;
}
return STATUS_SUCCESS;
}
return STATUS_NOT_FOUND;
}
_Success_(return == STATUS_SUCCESS)
@@ -201,7 +306,7 @@ DomitoMemorySearchPattern(
_In_ SIZE_T puLen,
_In_ PVOID pcBase,
_In_ SIZE_T puSize,
_Outptr_result_maybenull_ PVOID * ppMatch
_Outptr_result_maybenull_ PVOID* ppMatch
)
{
ASSERT(ppMatch != NULL && pcPattern != NULL && pcBase != NULL);
@@ -221,6 +326,7 @@ DomitoMemorySearchPattern(
for (SIZE_T i = 0; i + puLen <= puSize; i++)
{
BOOLEAN found = TRUE;
for (SIZE_T j = 0; j < puLen; j++)
{
if (pcPattern[j] != uWildcard && pcPattern[j] != ((PCUCHAR)pcBase)[i + j])
@@ -240,6 +346,74 @@ DomitoMemorySearchPattern(
return STATUS_NOT_FOUND;
}
_Success_(return == STATUS_SUCCESS)
_Must_inspect_result_
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
DomitoReadFileAt(
_In_ HANDLE FileHandle,
_Out_writes_bytes_(BufferSize) PVOID Buffer,
_In_ ULONG BufferSize,
_In_ const LARGE_INTEGER* ByteOffset
)
{
if (!FileHandle || !ByteOffset || ByteOffset->QuadPart < 0 || (!Buffer && BufferSize))
{
return STATUS_INVALID_PARAMETER;
}
if (!BufferSize)
{
return STATUS_SUCCESS;
}
HANDLE eventHandle = nullptr;
OBJECT_ATTRIBUTES attributes;
InitializeObjectAttributes(&attributes, nullptr, OBJ_KERNEL_HANDLE, nullptr, nullptr);
NTSTATUS status = ZwCreateEvent(&eventHandle, EVENT_ALL_ACCESS, &attributes, NotificationEvent, FALSE);
if (!NT_SUCCESS(status))
{
return status;
}
PVOID eventObject = nullptr;
status = ObReferenceObjectByHandle(eventHandle, SYNCHRONIZE, *ExEventObjectType, KernelMode, &eventObject, nullptr);
if (!NT_SUCCESS(status))
{
ZwClose(eventHandle);
return status;
}
IO_STATUS_BLOCK ioStatusBlock = {};
LARGE_INTEGER offset = *ByteOffset;
status =
ZwReadFile(FileHandle, eventHandle, nullptr, nullptr, &ioStatusBlock, Buffer, BufferSize, &offset, nullptr);
if (status == STATUS_PENDING)
{
// A private event identifies this request even if the file has other I/O.
// KernelMode + nonalertable + no timeout cannot return before completion.
KeWaitForSingleObject(eventObject, Executive, KernelMode, FALSE, nullptr);
status = ioStatusBlock.Status;
}
if (NT_SUCCESS(status))
{
status = ioStatusBlock.Status;
if (NT_SUCCESS(status) && ioStatusBlock.Information != BufferSize)
{
status = STATUS_END_OF_FILE;
}
}
ObDereferenceObject(eventObject);
ZwClose(eventHandle);
return status;
}
_Success_(return == STATUS_SUCCESS)
_Must_inspect_result_
_IRQL_requires_max_(PASSIVE_LEVEL)
@@ -250,33 +424,8 @@ DomitoReadFile(
_In_ ULONG BufferSize
)
{
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK ioStatusBlock;
// Read the file into memory using ZwReadFile
if (!NT_SUCCESS(status = ZwReadFile(
FileHandle,
NULL,
NULL,
NULL,
&ioStatusBlock,
Buffer,
BufferSize,
NULL,
NULL
)))
{
return status;
}
// Check if the file was read successfully
if (!NT_SUCCESS(ioStatusBlock.Status))
{
return ioStatusBlock.Status;
}
// File read successfully
return status;
const LARGE_INTEGER offset = {};
return DomitoReadFileAt(FileHandle, Buffer, BufferSize, &offset);
}
_Success_(return == STATUS_SUCCESS)
@@ -286,95 +435,94 @@ _IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
DomitoGetProcessImageName(
_In_ ULONG ProcessId,
_Inout_ PUNICODE_STRING * ProcessImageName
_Outptr_result_maybenull_ PUNICODE_STRING* ProcessImageName
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
ULONG returnedLength;
HANDLE hProcess = NULL;
PAGED_CODE();
PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process
if (ProcessId == 0 || ProcessId == 4)
{
return STATUS_INVALID_PARAMETER_1;
}
if (ProcessImageName == NULL)
if (!ProcessImageName)
{
return STATUS_INVALID_PARAMETER_2;
}
CLIENT_ID cid;
cid.UniqueProcess = (HANDLE)ProcessId;
cid.UniqueThread = NULL;
*ProcessImageName = nullptr;
OBJECT_ATTRIBUTES objAttr;
InitializeObjectAttributes(
&objAttr,
NULL,
OBJ_KERNEL_HANDLE,
NULL,
NULL
);
if (!ProcessId || ProcessId == 4)
{
return STATUS_INVALID_PARAMETER_1;
}
if (!NT_SUCCESS(status = ZwOpenProcess(
&hProcess,
PROCESS_ALL_ACCESS,
&objAttr,
&cid
)))
if (!G_Common.ZwQueryInformationProcess)
{
return STATUS_NOT_IMPLEMENTED;
}
CLIENT_ID cid = {};
cid.UniqueProcess = ULongToHandle(ProcessId);
OBJECT_ATTRIBUTES attributes;
InitializeObjectAttributes(&attributes, nullptr, OBJ_KERNEL_HANDLE, nullptr, nullptr);
HANDLE process = nullptr;
// PROCESS_QUERY_LIMITED_INFORMATION (WinNT.h); not named by the WDK headers.
const ACCESS_MASK queryLimitedInformation = 0x1000;
NTSTATUS status = ZwOpenProcess(&process, queryLimitedInformation, &attributes, &cid);
if (!NT_SUCCESS(status))
{
return status;
}
if (G_Common.ZwQueryInformationProcess == NULL)
ULONG size = 0;
status = G_Common.ZwQueryInformationProcess(process, ProcessImageFileName, nullptr, 0, &size);
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
status = STATUS_NOT_IMPLEMENTED;
goto cleanUp;
}
if (size < sizeof(UNICODE_STRING))
{
status = STATUS_INVALID_BUFFER_SIZE;
}
else
{
auto image = static_cast<PUNICODE_STRING>(G_Memory.Allocate(size));
/* Query the actual size of the process path */
status = G_Common.ZwQueryInformationProcess(
hProcess,
ProcessImageFileName,
NULL, // buffer
0, // buffer size
&returnedLength
);
if (!image)
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
ULONG returned = 0;
status = G_Common.ZwQueryInformationProcess(process, ProcessImageFileName, image, size, &returned);
if (STATUS_INFO_LENGTH_MISMATCH != status)
{
goto cleanUp;
}
if (NT_SUCCESS(status))
{
const ULONG_PTR base = reinterpret_cast<ULONG_PTR>(image);
const ULONG_PTR buffer = reinterpret_cast<ULONG_PTR>(image->Buffer);
*ProcessImageName = (PUNICODE_STRING)G_Memory.Allocate(returnedLength);
if (!image->Length || (image->Length & 1) || image->Length > image->MaximumLength ||
buffer < base || buffer - base < sizeof(UNICODE_STRING) || buffer - base > size ||
image->MaximumLength > size - (buffer - base))
{
status = STATUS_INVALID_BUFFER_SIZE;
}
}
if (*ProcessImageName == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanUp;
if (NT_SUCCESS(status))
{
*ProcessImageName = image;
}
else
{
G_Memory.Free(image);
}
}
}
}
/* Retrieve the process path from the handle to the process */
if (!NT_SUCCESS(status = G_Common.ZwQueryInformationProcess(
hProcess,
ProcessImageFileName,
*ProcessImageName,
returnedLength,
&returnedLength
)))
{
G_Memory.Free(*ProcessImageName);
}
cleanUp:
if (hProcess)
else if (NT_SUCCESS(status))
{
ZwClose(hProcess);
status = STATUS_INVALID_BUFFER_SIZE;
}
ZwClose(process);
return status;
}
#pragma code_seg()
+6
View File
@@ -121,12 +121,18 @@
<FilesToPackage Include="$(TargetPath)" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Domito.Certificate.cpp" />
<ClCompile Include="Domito.Consumer.cpp" />
<ClCompile Include="Domito.CodeIntegrity.cpp" />
<ClCompile Include="Domito.CodeIntegrity.Validation.cpp" />
<ClCompile Include="Domito.cpp" />
<ClCompile Include="Domito.Memory.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\Domito.MinCrypt.h" />
<ClInclude Include="..\include\Domito.MinCrypt.26100.h" />
<ClInclude Include="..\include\Domito.MinCrypt.Historical.h" />
<ClInclude Include="..\include\Domito.Consumer.h" />
<ClInclude Include="..\include\Domito.h" />
<ClInclude Include="Domito.Internal.h" />
</ItemGroup>
+18
View File
@@ -28,6 +28,15 @@
<ClCompile Include="Domito.Memory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Domito.CodeIntegrity.Validation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Domito.Certificate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Domito.Consumer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Domito.CodeIntegrity.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -36,9 +45,18 @@
<ClInclude Include="Domito.Internal.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\Domito.Consumer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\Domito.h">
<Filter>Header Files\Public</Filter>
</ClInclude>
<ClInclude Include="..\include\Domito.MinCrypt.26100.h">
<Filter>Header Files\Public</Filter>
</ClInclude>
<ClInclude Include="..\include\Domito.MinCrypt.Historical.h">
<Filter>Header Files\Public</Filter>
</ClInclude>
<ClInclude Include="..\include\Domito.MinCrypt.h">
<Filter>Header Files\Public</Filter>
</ClInclude>