# CiValidateFileObject: validation flag 0x00000020 Issue #3 is **documented; its TESTSIGNING-off runtime experiment is pending**. A related signer-selection experiment has now run successfully on Windows 11 22631 with TESTSIGNING enabled (below). That run does not close the requested 19041/19045 flag 0 versus 0x20 TESTSIGNING-off comparison. ## 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. ## Historical generation differences The same numeric flag does not imply the same internal root mask across Windows versions. Static analysis of the extracted x64 corpus adds these bounded facts: - On sampled `10.0.10240.16463` (1507), `10.0.10586.0` (1511), `10.0.14393.2189` (1607) and `10.0.15063.0` (1703), the recovered `CipGetAcceptedRootKeys` bodies have no direct `flag & 0x20` broadening branch. They still test larger masks containing that bit, so this is not evidence that the flag has no effect anywhere in those verifiers. - On sampled `10.0.16299.125` (1709) and `10.0.17134.1130` (1803), when the helper's main global mode is zero, `flag & 0x20` selects mask `0x224FF` (assignment), rather than the modern `mask |= 0xA4FF` operation. A signing-level predicate can select the same mask. Other global additions occur afterward. The other global mode returns `0x204FF` directly. The observed helper masks include branch-specific diagnostics/categories and must not be interpreted as an official stable root enum. The descriptive Domito constant names the intended broadening use without promising a single mask or identical behavior on every build. The ISO-derived **10.0.19041.1 x64** helper provides direct static evidence for the reported build family: in its main global mode it uses `mask |= 0x24FF` when flag `0x20` or the signing-level predicate applies; the alternate mode returns `0x24FF`. With requested level zero and optional global additions disabled, `0xAA` becomes `0x24FF` (added bits `0x2455`). This is an arithmetic witness, not a measurement of initialized OS policy. The exact CI SHA-256 is `2955710d34db26e352e62b20861ba338f0216cc36b378d128057be955e184282`. CodeIntegrity-RE retains matched-PDB pseudocode and assembly in `historical/ci-19041.1-contracts.json` and `historical/ci-19041.1-assembly.json`, with batch evidence ID `ev_13f89502777ee38ea98410f7a07f078bb934e78d792b662ab242f7b11d27e747`. Other 19041/19045 servicing revisions and the TESTSIGNING-off runtime workaround still require their own evidence. Historical records in CodeIntegrity-RE: `historical/ci-14393-contracts.json` `ev_e63a4c4b7613e231795051b784b9571605b9bf81258b5dcd04d32ddc681abb41`, `historical/ci-15063-contracts.json` `ev_338658fb82559fa6daa8ae5487b90fe84b8e408ea7578feed6d8dff5d590e114`, `historical/ci-16299-contracts.json` `ev_fe5731175f0726f0d46af2f78ca615d284f8598939296815e26b5589db3001cf`, and `historical/ci-17134-contracts.json` `ev_0c3e3359b8b7486318006afa6b95eb2257a18abeec9041a86a32db487bcbe996`. ## 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. ## Multiple signatures: a separate kernel experiment `0x20` is an accepted-root control, not a recovered "PE image" type flag. In the examined 22621.2792 and modern 26100 x64 bodies it leaves the preferred-signing-level search active. `PolicyInfo` can therefore describe a nested Microsoft signature even when the primary company signature also verifies. The first-success guard tests aggregate masks `0xE47FFF87` (22621.2792) and `0xE41FFF87` (modern 26100). `0x20` intersects neither; `0x80` intersects both. Compare flags `0x20` versus `0xA0`, requested signing level zero, on the same stable dual-signed file. Record the actual selected leaf/thumbprint, both policy statuses and signing time. Do not interpret status success alone as selection of the company certificate. First successful does not necessarily mean first listed: CI may reject the primary and continue. `0x80` has additional policy and fallback effects. It has not been published as a supported Domito constant or a production default. The companion research `SIGNATURE-SELECTION.md` provides the full static evidence and an alternative primary-signature path using `CiCheckSignedFile`. A user-run kernel probe on Windows 11 **10.0.22631 x64**, CI options **0x00080207** (TESTSIGNING enabled), confirms the selection difference for the exact dual-signed BthPS3 fixture: `0x20` returns Microsoft, while `0xA0` returns the Nefarius leaf `FF0CCF318FD8C14775023A0B52C28FFCCA59D7F7` and signing time **2026-10-02T18:32:01.000Z**. Direct primary SHA-256 `CiCheckSignedFile` returns the same Nefarius leaf, signer/timestamp policies and signing time. All three calls return STATUS_SUCCESS; all policy VerificationStatus fields also succeed. The file digest is identical across calls, before/after file hashes match, and TraceFmt reports 149 events with no losses, format errors or unknown formats. The companion `signature-selection/runtime-22631-bthps3.json` records exact trace hashes and the on-disk CI hash. This is one test-signing-enabled run; TESTSIGNING-off, other builds/policies and general signature enumeration remain unverified. `0xA0` does not mean "select any desired publisher". ## 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.