-
-
Notifications
You must be signed in to change notification settings - Fork 232
System Integrity Policy Transformations | XML to CIP and Back
This document provides an exhaustive exploration of the processes involved in transforming a System Integrity Policy (SiPolicy, aka App Control Policy, aka WDAC policy) from an XML representation into a binary format with a .cip
extension, and subsequently reversing that transformation to reconstruct the original XML from the binary data. The XML format serves as a human-readable structure for defining policies that dictate system security and integrity within the Windows operation system. In contrast, the binary format is a compact, machine-readable rendition optimized for enforcement by the operating system kernel. Understanding these conversions is paramount for ensuring that policies are accurately interpreted, applied, and maintained across different states of representation.
The transformations involve intricate manipulations at the byte level, precise structuring of data, and conditional logic to accommodate various policy components. This guide delves into each step of these processes, elucidating the rationale behind design choices, the versioning mechanism that governs which blocks are present, and the mechanisms that preserve data integrity throughout the conversions.
Tip
Everything explained in this document is implemented in the AppControl Manager.

-
XML → Object Model
Deserialize the policy XML into a strongly typed
SiPolicy
object graph that is language agnostic. -
Object Model → Binary
Write a phased header (with a 32-bit version identifier followed by GUIDs, flags, counts, and a body-offset placeholder) and then a body stream containing policy data in sequential, versioned blocks.
-
Binary → XML
Read the 32-bit version identifier to determine which body blocks are present, parse header metadata, then sequentially parse each versioned block to reconstruct the
SiPolicy
object, and finally serialize that back to XML.

- Open the XML file via a
FileStream
. - Serialize it to map XML elements into the
SiPolicy
object hierarchy. - Optional or missing XML elements are preserved as
null
or default values, enabling conditional encoding of versioned blocks downstream.
The .cip
binary is composed of two concatenated parts:
-
Header Stream A fixed-width preamble containing:
- A 32-bit Version Identifier (indicates the highest versioned block written).
- Two 16-byte GUIDs: PolicyTypeID and PlatformID.
- 32-bit option flags.
- 32-bit counts for EKUs, FileRules, Signers, SigningScenarios.
- 64-bit policy version.
- 32-bit body-offset placeholder.
-
Body Stream A variable-length, in-memory buffer that serializes policy data in discrete, versioned blocks. Upon body completion, its length and offset are back-patched into the header, and the two streams are merged.
Offset | Size | Field |
---|---|---|
0x00 | 4 | Version identifier (uint32 ) → denotes highest V-block |
0x04 | 16 | PolicyTypeID GUID (from BasePolicyID ) |
0x14 | 16 | PlatformID GUID (zeroed if unspecified) |
0x24 | 4 | Option flags (uint32 ) |
0x28 | 4 | Count of EKUs (uint32 ) |
0x2C | 4 | Count of FileRules |
0x30 | 4 | Count of Signers |
0x34 | 4 | Count of SigningScenarios |
0x38 | 8 | Policy version (two uint32 from VersionEx ) |
0x40 | 4 | Body-offset placeholder (uint32 ) |
… | … | (continues into Body Stream) |
The Version Identifier serves to signal exactly which subsequent versioned blocks (V3, V4, …, V8) are encoded in the body. This is a brilliant implementation done by Microsoft which enables backward compatibility: older policies omit higher-numbered blocks, preventing attempts to read beyond EOF.
- 32-bit unsigned length prefixes for strings and byte arrays.
- UTF-16LE encoding for all text.
- 4-byte alignment: pad with zero bytes so each data element boundary is aligned.
- Zero terminators (
uint32 = 0
) after optional strings.
-
EKU Section
- For each EKU, write its DER bytes via
WritePaddedCountedBytes
. - ID in XML is generated as
ID_EKU_<MD5("EKU:" + Base64(value))>
.
- For each EKU, write its DER bytes via
-
FileRules Section
- Sorted by rule type and key properties.
- For each rule:
-
uint32 Type
(0 = Deny, 1 = Allow, 2 = FileAttrib, 3 = FileRule). - Optional
FileName
string. -
UInt64 MinimumFileVersion
(twouint32
s). - Hash byte array.
-
- Later V-blocks append metadata (max version, AppIDs, internal names, etc.).
-
Signers Section
- Certificate root indicator + data.
- EKU index references.
- Issuer, Publisher, OEM ID strings.
- FileAttribRef index array.
- Later V3 block adds
SignTimeAfter
.
-
SigningScenarios Section
- For each scenario:
-
uint32 Value
, inherited-scenarios index array, minimum-hash-algorithm, allowed/denied/test signer groups.
-
- For each scenario:
-
HVCI Options
- Single
uint32
bitmask.
- Single
-
Secure Settings
- Sorted by provider, key, value-name.
- Each record: provider, key, value-name strings; type tag; typed value.
-
Versioned Extension Blocks
-
V3 (tag=3): MaximumFileVersion + AppIDs for each FileRule;
SignTimeAfter
per Signer. - V4 (tag=4): FileRule metadata (InternalName, FileDescription, ProductName).
- V5 (tag=5): PackageFamilyName + PackageVersion.
- V6 (tag=6): Final PolicyID & BasePolicyID GUIDs and SupplementalPolicySigners.
- V7 (tag=7): FilePath for each FileRule.
- V8 (tag=8): AppSettings region (AppRoot + individual AppSetting entries).
-
End Tag =
version + 1
marks termination.
-
V3 (tag=3): MaximumFileVersion + AppIDs for each FileRule;
- Compute
bodyLength
and overwrite the header’s size placeholder. - Back-patch the header’s body-offset with the start position of the body.
- Concatenate header and body into the final
.cip
file.
Elements not serialized into the CIP binary:
- Generated IDs (SignerID, FileRuleID) are only created during parsing.
-
FriendlyName
attributes. -
Signer.Name
(human-friendly certificate name).

The reverse process reconstructs the XML from the binary format, requiring conditional parsing based on the version identifier, and mapping byte-level data back into the SiPolicy
object model.
- Read raw bytes from
.cip
. - If detected as PKCS#7 SignedData, decode and extract
ContentInfo.Content
; otherwise use raw bytes directly.
- Read the 32-bit Version Identifier.
- Read PolicyTypeID and PlatformID GUIDs.
- Read option flags and element counts (EKU, FileRule, Signer, Scenario).
- Read policy version (two
uint32
→UInt64
→ dotted string). - Read body-offset, seek there, and read the 32-bit body length.
Parse only blocks N
where N <= version
:
-
EKUs:
ReadCountedAlignedBytes
→ reconstructEKU.Value
→ generate IDs. - FileRules: read type, name, minimum version, hash → instantiate rule objects.
- Signers: reconstruct certificate root, indices, strings, FileAttribRefs.
-
V3 (if
version >= 3
): read max versions, AppIDs for FileRules; readSignTimeAfter
per Signer. - V4: internal names, descriptions, product names for FileRules.
- V5: package info for FileRules.
- V6: final GUIDs, SupplementalPolicySigners.
- V7: file paths.
-
V8:
ParseAppSettings
. - Read end tag =
version + 1
to confirm policy termination.
- Map numeric indices back to string IDs.
- Populate arrays and optional elements only if parsed.
- Elements not present in earlier versions remain unset or default.
-
Serialize only fields present in the object graph.
-
The resulting XML mirrors the original structure, enriched with generated IDs and version-specific extensions.
-
The following data will not be included in the XML since they weren't included in the XML in the first place:
- Signer Name
- FriendlyName
- ID

Version | Block Tag | Contents |
---|---|---|
1–2 | — | Core header + EKUs + FileRules + Signers + Scenarios + Settings |
3 | 3 | MaxFileVersion & AppIDs (FileRules); SignTimeAfter (Signers) |
4 | 4 | FileRule metadata (InternalName, FileDescription, ProductName) |
5 | 5 | PackageFamilyName & PackageVersion |
6 | 6 | PolicyID/BasePolicyID GUIDs; SupplementalPolicySigners |
7 | 7 | FilePath for each FileRule |
8 | 8 | AppSettings region (AppRoot + AppSetting entries) |
End | version+1 | Terminator tag |
- Create AppControl Policy
- Create Supplemental Policy
- System Information
- Configure Policy Rule Options
- Policy Editor
- Simulation
- Allow New Apps
- Build New Certificate
- Create Policy From Event Logs
- Create Policy From MDE Advanced Hunting
- Create Deny Policy
- Merge App Control Policies
- Deploy App Control Policy
- Get Code Integrity Hashes
- Get Secure Policy Settings
- Update
- Sidebar
- Validate Policies
- View File Certificates
- Microsoft Graph
- Protect
- Microsoft Security Baselines
- Microsoft Security Baselines Overrides
- Microsoft 365 Apps Security Baseline
- Microsoft Defender
- Attack Surface Reduction
- Bitlocker
- Device Guard
- TLS Security
- Lock Screen
- User Account Control
- Windows Firewall
- Optional Windows Features
- Windows Networking
- Miscellaneous Configurations
- Windows Update
- Edge Browser
- Certificate Checking
- Country IP Blocking
- Non Admin Measures
- Group Policy Editor
- Manage Installed Apps
- File Reputation
- Audit Policies
- Introduction
- How To Generate Audit Logs via App Control Policies
- How To Create an App Control Supplemental Policy
- The Strength of Signed App Control Policies
- How To Upload App Control Policies To Intune Using AppControl Manager
- How To Create and Maintain Strict Kernel‐Mode App Control Policy
- How to Create an App Control Deny Policy
- App Control Notes
- How to use Windows Server to Create App Control Code Signing Certificate
- Fast and Automatic Microsoft Recommended Driver Block Rules updates
- App Control policy for BYOVD Kernel mode only protection
- EKUs in App Control for Business Policies
- App Control Rule Levels Comparison and Guide
- Script Enforcement and PowerShell Constrained Language Mode in App Control Policies
- How to Use Microsoft Defender for Endpoint Advanced Hunting With App Control
- App Control Frequently Asked Questions (FAQs)
- System Integrity Policy Transformations | XML to CIP and Back
- Create Bootable USB flash drive with no 3rd party tools
- Event Viewer
- Group Policy
- How to compact your OS and free up extra space
- Hyper V
- Git GitHub Desktop and Mandatory ASLR
- Signed and Verified commits with GitHub desktop
- About TLS, DNS, Encryption and OPSEC concepts
- Things to do when clean installing Windows
- Comparison of security benchmarks
- BitLocker, TPM and Pluton | What Are They and How Do They Work
- How to Detect Changes in User and Local Machine Certificate Stores in Real Time Using PowerShell
- Cloning Personal and Enterprise Repositories Using GitHub Desktop
- Only a Small Portion of The Windows OS Security Apparatus
- Rethinking Trust: Advanced Security Measures for High‐Stakes Systems
- Clean Source principle, Azure and Privileged Access Workstations
- How to Securely Connect to Azure VMs and Use RDP
- Basic PowerShell tricks and notes
- Basic PowerShell tricks and notes Part 2
- Basic PowerShell tricks and notes Part 3
- Basic PowerShell tricks and notes Part 4
- Basic PowerShell tricks and notes Part 5
- How To Access All Stream Outputs From Thread Jobs In PowerShell In Real Time
- PowerShell Best Practices To Follow When Coding
- How To Asynchronously Access All Stream Outputs From Background Jobs In PowerShell
- Powershell Dynamic Parameters and How to Add Them to the Get‐Help Syntax
- RunSpaces In PowerShell
- How To Use Reflection And Prevent Using Internal & Private C# Methods in PowerShell