Document CI validation flag 0x20 and add kernel probe
This commit is contained in:
1 parent
195b9fccf3
commit
1026c48324
8 files changed
+381
-7
No files matched your search
@@ -71,6 +71,10 @@ Copy the provided `Domito.props` file into the directory of your `.vcxproj` file
|
||||
<!-- The rest of the project file -->
|
||||
```
|
||||
|
||||
## Code Integrity API notes
|
||||
|
||||
- [CiValidateFileObject flag 0x20: evidence, limitations, and kernel test procedure](docs/ci-validate-file-object.md)
|
||||
|
||||
## Sources & 3rd party credits
|
||||
|
||||
This library benefits from these awesome projects ❤ (appearance in no special order):
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
# CiValidateFileObject: validation flag 0x00000020
|
||||
|
||||
Issue #3 is **documented; kernel runtime verification is pending**. The probe
|
||||
builds and its plumbing is covered by host tests, but those tests do not execute
|
||||
Windows Code Integrity. Do not close the runtime-testing portion on that basis.
|
||||
|
||||
## Meaning supported by the evidence
|
||||
|
||||
`DOMITO_CI_VALIDATE_FLAG_BROADEN_ROOT_ACCEPTANCE` is Domito's descriptive name for
|
||||
`0x00000020` in the second argument, `ValidationFlags`. It is not a recovered
|
||||
Microsoft macro name. On the analyzed **ci.dll 10.0.26100.8328 x64**, the flag
|
||||
causes `CipGetAcceptedRootKeys` to OR `0xA4FF` into its accepted-root mask in the
|
||||
branch where the global at RVA `0x45A88` is zero. Other global state, flags, and
|
||||
the requested signing level also affect the result.
|
||||
|
||||
The assembly at preferred image base `0x180000000` establishes the operation:
|
||||
|
||||
```text
|
||||
0x180089e40: MOV EDX, 0xa4ff
|
||||
0x180089e4c: BT ECX, EDI ; ECX = 0x8c in this branch; EDI = signing level
|
||||
0x180089e4f: JC 0x180089e57
|
||||
0x180089e51: TEST SIL, 0x20 ; saved ValidationFlags
|
||||
0x180089e55: JZ 0x180089e59
|
||||
0x180089e57: OR EBX, EDX ; broaden accepted-root mask
|
||||
```
|
||||
|
||||
For requested level 0, flags 0, the branch above, and all other optional global
|
||||
additions disabled, the mask is `0x80AA`; setting only `0x20` makes it `0xA4FF`
|
||||
(additional bits `0x2455`). This is an arithmetic witness, **not a measurement of
|
||||
the running OS's initialized policy**. Levels 2, 3, and 7 already trigger the OR
|
||||
through `0x8C`. When the global at RVA `0x45A88` is nonzero, the helper returns
|
||||
`0xA4FF` directly, so the flag need not change its result.
|
||||
|
||||
Later processing can still remove bit `0x8` when the helper's third argument is
|
||||
nonzero and validation flag `0x00400000` is present. The surrounding
|
||||
`CipDisallowEmbeddedPicrootIfTrusted` path can add that latter flag itself.
|
||||
Thus `0xA4FF` is not a promise about every final policy result.
|
||||
|
||||
The widened mask includes categories associated with test roots and unresolved
|
||||
chains as well as other roots. It must not be named "trust any Authenticode
|
||||
signer", "require Microsoft", or "enable TESTSIGNING". It changes the acceptance
|
||||
policy for this operation; it does not change the system boot setting. Further
|
||||
signature, chain, signing-level, and policy checks still matter. A successful
|
||||
call alone does not establish equivalence with WinVerifyTrust or the caller's
|
||||
intended publisher trust policy.
|
||||
|
||||
`CipGetHashAlgorithmForLegacyScenario` does not test `0x20`: the flag is not a
|
||||
fixed SHA-1/SHA-256 selector. Also distinguish this **input flag** from the
|
||||
unrelated `MINCRYPT_POLICY_INFO.PolicyBits` output bit `0x20`, associated with
|
||||
the built-in DMD root category in this build.
|
||||
|
||||
## Evidence and compatibility limits
|
||||
|
||||
The [upstream report](https://github.com/Ido-Moshe-Github/CiDllDemo/issues/5#issuecomment-2725851295)
|
||||
describes success with flags `0x20`, signing level 0, and TESTSIGNING off on
|
||||
Windows builds 19041 and 19045. That is third-party runtime evidence, not a
|
||||
reproduction by this project. Its behavior is consistent with broader root
|
||||
acceptance; it does not establish identical internals across those builds.
|
||||
|
||||
Our static evidence uses REA 6.8.0 / Ghidra 12.1.3 and the binary SHA-256
|
||||
`c7d23814f3202c51fb23e3ca3d4328855787383cee5d14c4a986a13ec2aeda0b`.
|
||||
Public-symbol mapping uses ci.pdb GUID `1CAE574B-CF1F-3564-5F74-49567A128629`, age 1.
|
||||
The companion CodeIntegrity-RE repository retains:
|
||||
|
||||
- `ci-flag20-assembly-evidence.json`: assembly evidence
|
||||
`ev_ea763b9be7643a6241b0c805eae5f735a4b4a0a82df1383b1e68da4207f85fb4`.
|
||||
- `evidence/CipGetAcceptedRootKeys.c`: decompilation
|
||||
`ev_c905f03db9470c8b2d89a6295080143f25d5780ca0909de0ca554e40d078a3f3`.
|
||||
- `evidence/CipGetHashAlgorithmForLegacyScenario.c`:
|
||||
`ev_addf8f1bc18da58db80db54eeab0dd78ec5ab88b219ece3e082ebd9a8e50b257`.
|
||||
- `evidence/CipDisallowEmbeddedPicrootIfTrusted.c`:
|
||||
`ev_2475a5804fc87683a54b06acebd3785e5e8eb31f4f542f699fc2ff414c1ab7fe`.
|
||||
- `evidence/CipValidateFileObjectByFileOrPageHash.c`: capacity contract
|
||||
`ev_3897aa76c17cc64a30f60e72b994e695b88c8eda2ae6f246766bdc9314f06cca`.
|
||||
|
||||
No complete enumeration of downstream uses of the flag, historical Microsoft
|
||||
name, or cross-version/architecture guarantee is claimed. The public declaration
|
||||
keeps the existing parameter widths. Names now describe the known inputs; on
|
||||
26100 x64 the third argument is consumed as a byte. Narrowing the ABI everywhere
|
||||
requires separate evidence.
|
||||
|
||||
## Kernel experiment prepared for the VM
|
||||
|
||||
Add `tests/ci-file-object-probe.cpp` to an existing signed test driver and add
|
||||
`include/` to its include path. Link the existing CI imports (`ci.lib`). Include
|
||||
`ntifs.h`, `Domito.MinCrypt.h`, and `tests/ci-file-object-probe.h` at the call site.
|
||||
Invoke `DomitoProbeCiValidateFileObject` at PASSIVE_LEVEL with a referenced
|
||||
file object for the fixture and an array of `DOMITO_CI_PROBE_RESULT_COUNT` results.
|
||||
Keep the file bytes stable and hold the object reference for the entire probe.
|
||||
The surrounding driver owns opening/referencing/closing the file and exporting
|
||||
the result records. The probe has no DriverEntry or installation side effects.
|
||||
|
||||
It makes six calls, always at requested level 0:
|
||||
|
||||
1. Flags `0`, then `0x20`, then `0x20`, then `0`, with 64-byte capacity each time.
|
||||
2. Flags `0`, then `0x20`, with declared capacity 63 (64-byte backing storage).
|
||||
|
||||
Each call uses fresh policies, hash capacity, algorithm, and signing-time storage.
|
||||
Both policy objects are freed after capture, including on failure. The records
|
||||
capture status, both policy statuses/bits, chain presence, signing time, digest
|
||||
algorithm/length/bytes, and a trailing buffer guard. The digest is a **file hash**,
|
||||
not a certificate thumbprint. Failed-call outputs are raw diagnostics, not valid
|
||||
verification results. Chain internals are deliberately not dereferenced.
|
||||
|
||||
The repeated order helps reveal cache/order effects but does not eliminate them.
|
||||
Also run the reversed initial order in a fresh VM snapshot when comparing policy.
|
||||
For the analyzed 26100 helper, both 63-byte-capacity calls should return
|
||||
`STATUS_BUFFER_TOO_SMALL` and set length 64, regardless of the eventual algorithm.
|
||||
Treat older-build differences as observations to investigate, not silently as
|
||||
test success. Every guard must remain intact; successful hash sizes must be at
|
||||
most 64. Inspect non-success statuses rather than treating all failures as
|
||||
invalid signatures (I/O and resource errors are possible).
|
||||
|
||||
Run on Windows 10 19041 and 19045 and a current Windows 11 build. The decisive
|
||||
reproduction uses a driver loadable with TESTSIGNING **off**. Record that setting,
|
||||
Secure Boot, HVCI/WDAC policy, OS build, CI version/hash, driver identity, fixture
|
||||
SHA-256, and each complete result. Keep policy constant between paired calls.
|
||||
Use these fixtures:
|
||||
|
||||
- A known third-party RSA Authenticode executable (ideally the reported fixture).
|
||||
- A Microsoft-signed executable and a catalog-signed file as controls.
|
||||
- An unsigned executable and a copy of the signed file with a byte changed in a
|
||||
hashed section, leaving its certificate table unchanged.
|
||||
- A self-signed/test-root fixture to characterize the broader acceptance policy.
|
||||
- If available, timestamped, expired, revoked, and ECC variants, with expectations
|
||||
recorded in advance and revocation/configuration differences retained.
|
||||
|
||||
Compare with `signtool verify /pa /v` as a separate user-mode policy observation,
|
||||
not an equality oracle. The report is reproduced only when the same valid
|
||||
third-party fixture changes from rejection at 0 to success at `0x20`, under the
|
||||
reported environment, with usable outputs. Unchanged success can mean the
|
||||
existing policy already accepts it. Unchanged failure is a result to investigate.
|
||||
Unsigned and tampered fixtures must not become accepted as valid signatures.
|
||||
Self-signed acceptance must be reported explicitly, not confused with public-CA
|
||||
publisher trust. Do not use this flag as a new production default based only on
|
||||
one positive fixture.
|
||||
|
||||
## Checks available without the VM
|
||||
|
||||
From an x64 Visual Studio developer shell:
|
||||
|
||||
```powershell
|
||||
python tests\run-ci-probe-tests.py
|
||||
```
|
||||
|
||||
This compiles the actual probe body with mock CI calls and constants extracted
|
||||
from the public header. It verifies call order, input forwarding, fresh capacity
|
||||
and outputs, result capture, and freeing both policies after success and failure.
|
||||
It does **not** emulate the flag's trust semantics. Kernel compilation checks
|
||||
the real headers separately; neither compilation nor mock tests substitute for
|
||||
the VM run above.
|
||||
@@ -513,15 +513,24 @@ CiSetTrustedOriginClaimId(
|
||||
_In_ UINT32 ClaimId
|
||||
);
|
||||
|
||||
// Domito descriptive name, not an official Microsoft flag name.
|
||||
// Observed on CI 10.0.26100.8328 x64: broadens the accepted-root mask in
|
||||
// CipGetAcceptedRootKeys. This is not a WinVerifyTrust-equivalent trust policy.
|
||||
// See docs/ci-validate-file-object.md for evidence and version limitations.
|
||||
#define DOMITO_CI_VALIDATE_FLAG_BROADEN_ROOT_ACCEPTANCE 0x00000020UL
|
||||
|
||||
/**
|
||||
* Given a file object, verify the signature and provide information regarding
|
||||
* the certificates that was used for signing (the entire certificate chain)
|
||||
*
|
||||
* @param FileObject - FileObject of the PE in question
|
||||
*
|
||||
* @param Unkonwn1 - unknown, 0 is a valid value. (Unkonwn1 and Unkonwn2 together calculate the minimum support algorithm)
|
||||
* @param ValidationFlags - validation policy flags; 0 is a valid value.
|
||||
* See DOMITO_CI_VALIDATE_FLAG_BROADEN_ROOT_ACCEPTANCE and the accompanying documentation.
|
||||
*
|
||||
* @param Unkonwn2 - unknown, 0 is a valid value. (^ the words above refer to 'CipGetHashAlgorithmForLegacyScenario')
|
||||
* @param RequestedSigningLevel - requested signing level; 0 is used by the probe.
|
||||
* CI 26100 x64 consumes the low byte. UINT32 is retained for ABI compatibility
|
||||
* until other supported architectures/builds are verified; use values in 0..255.
|
||||
*
|
||||
* @param PolicyInfo[out] - PolicyInfo containing information about the signer certificate chain.
|
||||
*
|
||||
@@ -529,13 +538,19 @@ CiSetTrustedOriginClaimId(
|
||||
*
|
||||
* @param SigningTime[out] - when the file was signed (FILETIME format)
|
||||
*
|
||||
* @param Hash - buffer containing the digest
|
||||
* @param Hash[out] - file digest, not a certificate thumbprint. Supply at least
|
||||
* MINCRYPT_MAX_HASH_LENGTH bytes (required by the observed 26100 x64 helper).
|
||||
*
|
||||
* @param HashSize - size of the digest, e.g. 0x14(160bit) for SHA1, 0x20(256bit) for SHA256
|
||||
* @param HashSize[in,out] - buffer capacity on entry; actual digest size on success.
|
||||
* Initialize to sizeof(Hash) for every call, including retries.
|
||||
*
|
||||
* @param HashAlgId - digest algorithm identifier, e.g. CALG_SHA1(0x8004), CALG_SHA_256(0x800C)
|
||||
*
|
||||
* @return STATUS_SUCCESS if the file digest in the signature matches the given digest and the signer cetificate is verified.
|
||||
* Initialize both policy outputs to empty objects and free both with CiFreePolicyInfo
|
||||
* before reuse. Do not interpret failed-call outputs as a verification result.
|
||||
*
|
||||
* @return STATUS_SUCCESS when verification succeeds under the selected CI policy.
|
||||
* This does not by itself establish the application's signer trust policy.
|
||||
* Various error values otherwise, for example:
|
||||
* STATUS_INVALID_IMAGE_HASH - the digest does not match the digest in the signature
|
||||
* STATUS_IMAGE_CERT_REVOKED - the certificate used for signing the file is revoked
|
||||
@@ -547,8 +562,8 @@ NTSTATUS
|
||||
NTAPI
|
||||
CiValidateFileObject(
|
||||
_In_ FILE_OBJECT* FileObject,
|
||||
_In_opt_ UINT32 Unkonwn1,
|
||||
_In_opt_ UINT32 Unkonwn2,
|
||||
_In_opt_ UINT32 ValidationFlags,
|
||||
_In_opt_ UINT32 RequestedSigningLevel,
|
||||
_Out_ MINCRYPT_POLICY_INFO* PolicyInfo,
|
||||
_Out_ MINCRYPT_POLICY_INFO* TimeStampPolicyInfo,
|
||||
_Out_ LARGE_INTEGER* SigningTime,
|
||||
|
||||
@@ -21,3 +21,10 @@ The tests do not validate CI trust policy, asynchronous kernel I/O behavior, or
|
||||
general Authenticode hashing of unusual section layouts. The digest implementation
|
||||
still uses its existing linear byte-order convention and heuristic signature
|
||||
algorithm detection; replacing those requires a separate compatibility corpus.
|
||||
|
||||
## File-object validation flag probe
|
||||
|
||||
See [the flag documentation and VM procedure](../docs/ci-validate-file-object.md)
|
||||
for `CiValidateFileObject` flag `0x20`. Run `python tests\run-ci-probe-tests.py`
|
||||
from the same developer shell to test probe plumbing with mock CI. The kernel
|
||||
probe must still be run in a suitable VM to verify actual policy behavior.
|
||||
@@ -0,0 +1,96 @@
|
||||
// Host tests validate the probe plumbing, NOT CI trust behavior.
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
#include <ntstatus.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include "ci-probe-constants.inc"
|
||||
#define NT_SUCCESS(s) (((NTSTATUS)(s)) >= 0)
|
||||
#define DOMITO_CI_PROBE_HOST_TEST
|
||||
typedef void* PFILE_OBJECT;
|
||||
struct MINCRYPT_POLICY_INFO {
|
||||
ULONG Size, VerificationStatus, PolicyBits;
|
||||
void* ChainInfo;
|
||||
};
|
||||
static unsigned calls, frees, outstanding, checks;
|
||||
static bool failureMode;
|
||||
static int fileToken, chainToken;
|
||||
static void Check(bool value, const char* label)
|
||||
{
|
||||
++checks;
|
||||
if (!value) { std::fprintf(stderr, "FAIL: %s\n", label); std::exit(1); }
|
||||
}
|
||||
static NTSTATUS CiValidateFileObject(PFILE_OBJECT file, ULONG flags, ULONG level,
|
||||
MINCRYPT_POLICY_INFO* policy, MINCRYPT_POLICY_INFO* timestamp,
|
||||
LARGE_INTEGER* time, UCHAR* hash, UINT32* size, ALG_ID* algorithm)
|
||||
{
|
||||
const ULONG expectedFlags[] = {0, 0x20, 0x20, 0, 0, 0x20};
|
||||
Check(calls < 6 && flags == expectedFlags[calls], "paired flags and order");
|
||||
Check(file == &fileToken && level == 0, "same file and level zero");
|
||||
Check(outstanding == 0, "previous outputs freed before next call");
|
||||
Check(policy->Size == sizeof(*policy) && timestamp->Size == sizeof(*timestamp), "policy sizes");
|
||||
Check(!policy->ChainInfo && !timestamp->ChainInfo && !policy->PolicyBits &&
|
||||
!timestamp->PolicyBits, "fresh policy objects");
|
||||
Check(*size == (calls < 4 ? 64u : 63u), "capacity reset for every call");
|
||||
Check(*algorithm == 0 && time->QuadPart == 0, "fresh scalar outputs");
|
||||
for (unsigned i = 0; i < 64; ++i) Check(hash[i] == 0, "fresh hash buffer");
|
||||
++calls;
|
||||
if (*size < 64) { *size = 64; return STATUS_BUFFER_TOO_SMALL; }
|
||||
policy->ChainInfo = timestamp->ChainInfo = &chainToken;
|
||||
outstanding = 2;
|
||||
policy->PolicyBits = 0xA5;
|
||||
timestamp->PolicyBits = 0x5A;
|
||||
policy->VerificationStatus = failureMode ? STATUS_INVALID_IMAGE_HASH : STATUS_SUCCESS;
|
||||
timestamp->VerificationStatus = STATUS_SUCCESS;
|
||||
// Also allocate on simulated failure to exercise unconditional cleanup.
|
||||
if (failureMode) return STATUS_INVALID_IMAGE_HASH;
|
||||
std::memset(hash, 0xCC, 32);
|
||||
*size = 32;
|
||||
*algorithm = 0x800C;
|
||||
time->QuadPart = 123;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
static void CiFreePolicyInfo(MINCRYPT_POLICY_INFO* policy)
|
||||
{
|
||||
++frees;
|
||||
if (policy->ChainInfo) { Check(outstanding > 0, "owned chain"); --outstanding; }
|
||||
*policy = {};
|
||||
}
|
||||
#include "ci-file-object-probe.cpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
for (unsigned pass = 0; pass < 2; ++pass)
|
||||
{
|
||||
failureMode = pass != 0;
|
||||
calls = frees = outstanding = 0;
|
||||
DOMITO_CI_PROBE_RESULT results[DOMITO_CI_PROBE_RESULT_COUNT];
|
||||
std::memset(results, 0xFF, sizeof(results));
|
||||
DomitoProbeCiValidateFileObject(&fileToken, results);
|
||||
Check(calls == 6 && frees == 12 && outstanding == 0, "all calls and frees");
|
||||
for (unsigned i = 0; i < 6; ++i)
|
||||
{
|
||||
const auto& r = results[i];
|
||||
Check(r.HashGuardIntact, "buffer guard");
|
||||
const NTSTATUS expected = i >= 4 ? STATUS_BUFFER_TOO_SMALL :
|
||||
failureMode ? STATUS_INVALID_IMAGE_HASH : STATUS_SUCCESS;
|
||||
Check(r.Status == expected, "status preserved");
|
||||
if (i < 4) {
|
||||
Check(r.PolicyBits == 0xA5 && r.TimeStampPolicyBits == 0x5A, "policy capture");
|
||||
Check(r.HasChain && r.HasTimeStampChain, "chain presence captured before free");
|
||||
}
|
||||
if (NT_SUCCESS(r.Status)) {
|
||||
Check(r.HashSize == 32 && r.HashAlgId == 0x800C && r.SigningTime.QuadPart == 123,
|
||||
"digest metadata and signing time");
|
||||
for (unsigned j = 0; j < 64; ++j)
|
||||
Check(r.Hash[j] == (j < 32 ? 0xCC : 0), "digest copied and tail cleared");
|
||||
} else {
|
||||
for (auto byte : r.Hash) Check(byte == 0, "no digest from failed call");
|
||||
}
|
||||
if (i >= 4) Check(r.HashSize == 64, "required capacity captured");
|
||||
}
|
||||
}
|
||||
std::printf("PASS: %u probe checks (mock CI; no runtime trust validation)\n", checks);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Compile into an existing signed test driver; this file has no DriverEntry.
|
||||
#ifndef DOMITO_CI_PROBE_HOST_TEST
|
||||
#include <ntifs.h>
|
||||
#include "Domito.MinCrypt.h"
|
||||
#endif
|
||||
#include "ci-file-object-probe.h"
|
||||
|
||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||
void DomitoProbeCiValidateFileObject(
|
||||
PFILE_OBJECT FileObject, DOMITO_CI_PROBE_RESULT* Results)
|
||||
{
|
||||
const ULONG flags[DOMITO_CI_PROBE_RESULT_COUNT] = {
|
||||
0, DOMITO_CI_VALIDATE_FLAG_BROADEN_ROOT_ACCEPTANCE,
|
||||
DOMITO_CI_VALIDATE_FLAG_BROADEN_ROOT_ACCEPTANCE, 0,
|
||||
0, DOMITO_CI_VALIDATE_FLAG_BROADEN_ROOT_ACCEPTANCE
|
||||
};
|
||||
for (ULONG i = 0; i < DOMITO_CI_PROBE_RESULT_COUNT; ++i)
|
||||
{
|
||||
MINCRYPT_POLICY_INFO policy = {};
|
||||
MINCRYPT_POLICY_INFO timestamp = {};
|
||||
policy.Size = sizeof(policy);
|
||||
timestamp.Size = sizeof(timestamp);
|
||||
struct {
|
||||
UCHAR Bytes[MINCRYPT_MAX_HASH_LENGTH];
|
||||
ULONG Guard;
|
||||
} hash = {};
|
||||
hash.Guard = 0xD01170AB;
|
||||
DOMITO_CI_PROBE_RESULT& result = Results[i];
|
||||
result = {};
|
||||
result.Flags = flags[i];
|
||||
result.InputCapacity = i < 4 ? sizeof(hash.Bytes) : sizeof(hash.Bytes) - 1;
|
||||
result.HashSize = result.InputCapacity;
|
||||
result.Status = CiValidateFileObject(FileObject, result.Flags, 0,
|
||||
&policy, ×tamp, &result.SigningTime, hash.Bytes,
|
||||
&result.HashSize, &result.HashAlgId);
|
||||
result.HashGuardIntact = hash.Guard == 0xD01170AB;
|
||||
// Keep failure outputs as raw observations; only successful calls have
|
||||
// a usable digest. Do not dereference the incompletely mapped chains.
|
||||
result.PolicyBits = policy.PolicyBits;
|
||||
result.TimeStampPolicyBits = timestamp.PolicyBits;
|
||||
result.VerificationStatus = (NTSTATUS)policy.VerificationStatus;
|
||||
result.TimeStampVerificationStatus = (NTSTATUS)timestamp.VerificationStatus;
|
||||
result.HasChain = policy.ChainInfo != nullptr;
|
||||
result.HasTimeStampChain = timestamp.ChainInfo != nullptr;
|
||||
if (NT_SUCCESS(result.Status) && result.HashSize <= sizeof(result.Hash))
|
||||
{
|
||||
RtlCopyMemory(result.Hash, hash.Bytes, result.HashSize);
|
||||
}
|
||||
CiFreePolicyInfo(&policy);
|
||||
CiFreePolicyInfo(×tamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
// Include ntifs.h and Domito.MinCrypt.h first. Test support, not a library API.
|
||||
typedef struct _DOMITO_CI_PROBE_RESULT
|
||||
{
|
||||
ULONG Flags;
|
||||
ULONG InputCapacity;
|
||||
NTSTATUS Status;
|
||||
UINT32 HashSize;
|
||||
ALG_ID HashAlgId;
|
||||
UCHAR Hash[MINCRYPT_MAX_HASH_LENGTH];
|
||||
LARGE_INTEGER SigningTime;
|
||||
ULONG PolicyBits;
|
||||
ULONG TimeStampPolicyBits;
|
||||
NTSTATUS VerificationStatus;
|
||||
NTSTATUS TimeStampVerificationStatus;
|
||||
BOOLEAN HasChain;
|
||||
BOOLEAN HasTimeStampChain;
|
||||
BOOLEAN HashGuardIntact;
|
||||
} DOMITO_CI_PROBE_RESULT;
|
||||
|
||||
#define DOMITO_CI_PROBE_RESULT_COUNT 6
|
||||
|
||||
// PASSIVE_LEVEL; referenced file object; file bytes must remain stable.
|
||||
// Writes observations, not an overall trust verdict. Frees both policies per call.
|
||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||
void DomitoProbeCiValidateFileObject(
|
||||
_In_ PFILE_OBJECT FileObject,
|
||||
_Out_writes_(DOMITO_CI_PROBE_RESULT_COUNT) DOMITO_CI_PROBE_RESULT* Results);
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Test the actual kernel probe body with mock CI; run in an x64 VS dev shell."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
tests = Path(__file__).resolve().parent
|
||||
header = (tests.parent / 'include/Domito.MinCrypt.h').read_text(encoding='utf-8-sig')
|
||||
names = ['MINCRYPT_MAX_HASH_LENGTH', 'DOMITO_CI_VALIDATE_FLAG_BROADEN_ROOT_ACCEPTANCE']
|
||||
constants = '\n'.join(re.search(r'^#define\s+' + name + r'\s+[^\r\n]+', header, re.M)[0]
|
||||
for name in names)
|
||||
with tempfile.TemporaryDirectory(prefix='domito-ci-probe-') as temp:
|
||||
build = Path(temp)
|
||||
(build / 'ci-probe-constants.inc').write_text(constants + '\n', encoding='utf-8')
|
||||
exe = build / 'ci-probe-tests.exe'
|
||||
subprocess.run(['cl', '/nologo', '/EHsc', '/W4', '/WX', '/std:c++17',
|
||||
'/I' + str(build), str(tests / 'ci-file-object-probe-tests.cpp'),
|
||||
'/Fe' + str(exe), '/Fo' + str(build / 'ci-probe-tests.obj')],
|
||||
cwd=build, check=True)
|
||||
subprocess.run([str(exe)], cwd=build, check=True)
|
||||
Reference in new issue
Block a user