This project aims to allow packaging Win32 apps for use with Intune.
content-prep new --path "path/to/source" --setupFile "path/to/source/setupFile" --output "path/to/output"docker run ghcr.io/maxihafer/content-prep:latest \
-v /path/to/source:/src \
-v /path/to/output:/out \
content-prep new --path /src --setupFile /src/setupFile --output /outMicrosoft provides its closed-source Content-Prep-Tool for packaging Applications for intune.
Being just a checked in .exe file, it is neither possible to verify the code nor its behavior.
There exist great open-source projects that implement the same functionality. Even though some of them work cross-platform, they still rely on .NET and PowerShell which is not ideal for the use case of easily publishing intune packages from CI/CD.
The great work of @svrooij really helped alot in understanding the inner workings of Intune packages:
- Content-Prep -
.NET/PowerShellimplementation of the Content-Prep functionality. - WinTuner - Very sophisticated
PowerShellModule for creating, deploying and managing Intune packages. - The Intune series in his blog - Great insights into the inner workings of
.intunewinpackages. Very well written and concise.
A .intunewin package is constructed as follows:
foo.intunewin (zip)
└── IntuneWinPackage
├── Contents
│ └── IntunePackage.intunewin
└── Metadata
└── Detection.xmlThis is a encrypted .zip file containing the actual content of the package. More info can be found in the Encryption Section.
The Detection.xml file is read by Intune to extract all kinds of information about the package. It is a XML file with the following structure:
<ApplicationInfo ToolVersion="1.8.4.0">
<FileName>IntunePackage.intunewin</FileName> // This is the static name of the inner (encrypted) package
<Name>foo</Name> // Display name of the application
<UnencryptedContentSize>216404340</UnencryptedContentSize> // Size of the unencrypted content
<SetupFile>foo.exe</SetupFile> // Name of the setup file
<EncryptionInfo>
<EncryptionKey>QX7BMsPzNDBSWv836oYT+gpxKlDYyPDm6C55XZKm9Z8=</EncryptionKey> // Base64 encoded encryption key (32 byte)
<MacKey>Ysr5oRUvkgORiurV3zCf14jsj/baZOE/Xc6EEm/2wYA=</MacKey> // Base64 encoded HMAC key (32 byte)
<InitializationVector>/Nh7KHI5lYFyCTbGqBASPg==</InitializationVector> // Base64 encoded IV (16 byte)
<Mac>PmGnbIzb6/N4pc3zZJF70+PYEAkXezR9Q6PaC4CzBdY=</Mac> // Base64 encoded HMAC
<ProfileIdentifier>ProfileVersion1</ProfileIdentifier> // Static encoding Profile used by Intune to verify package integrity
<FileDigest>47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=</FileDigest> // Base64 encoded SHA256 hash of the encrypted content
<FileDigestAlgorithm>SHA256</FileDigestAlgorithm> // Hashing algorithm used
</EncryptionInfo>
</ApplicationInfo>- The source folder (
src) is zipped without any compression to a temporary file. - The output file (
out) is created and its write position is advanced by the length of the HMAC. - The IV is written to the output file (at position
0 + len(HMAC)) as well as the HMAC itself. - The zipped
srcis then XORKeyStreamed into the output file starting at position0 + len(HMAC) + len(IV)as well as into the HMAC. - The HMAC is then calculated and written to the output file at position
0. - The metadata is written to the
Detection.xmlfile.
NOTE: the digest changes from execution to execution,. This is due to the fact that the zip archive contains the file creation time in its header.
The resulting structure of the encrypted file is as follows:
| 32 Bytes | 16 Bytes | AES-CTR XORKeyStreamed Content |
|---|---|---|
| HMAC of IV + Encrypted Content | IV | Encrypted Content |