Skip to content

Kext bundles ​

A kext bundle is a standard macOS or iOS kernel extension directory. UniXNU stages the bundle files and passes them to an XNU loader through UNIXNU_LOADER_PROTOCOL.

Bundle layout ​

MyDriver.kext/
  Contents/
    Info.plist
    MacOS/
      MyDriver

The bundle name ends with .kext. The plist path defaults to Contents/Info.plist. The executable path defaults to the value in CFBundleExecutable, relative to the bundle.

A codeless kext has a plist and no executable. It can declare dependencies or personalities that reference another binary.

Info.plist ​

xml
<dict>
  <key>CFBundleIdentifier</key><string>org.example.MyDriver</string>
  <key>CFBundleName</key><string>MyDriver</string>
  <key>CFBundleExecutable</key><string>MyDriver</string>
  <key>CFBundlePackageType</key><string>KEXT</string>
  <key>CFBundleVersion</key><string>1.0.0</string>
  <key>OSBundleRequired</key><string>Root</string>
  <key>OSBundleLibraries</key>
  <dict>
    <key>com.apple.kpi.iokit</key><string>23.6.0</string>
  </dict>
  <key>IOKitPersonalities</key>
  <dict>
    <key>MyDevice</key>
    <dict>
      <key>IOClass</key><string>MyDriver</string>
      <key>IOProviderClass</key><string>IOPlatformDevice</string>
      <key>IONameMatch</key><string>my-device</string>
      <key>IOProbeScore</key><integer>1000</integer>
    </dict>
  </dict>
</dict>

Register with UniXNU ​

An entry scoped kext:

toml
[[entry]]
id = "xnu"
type = "xnu"
path = "Payloads/kernel.macho"

[[entry.kext]]
bundle = "Kexts/MyDriver.kext"

A kit scoped kext names the entry:

toml
[[kext]]
bundle = "MyDriver.kext"
entry = "xnu"
plist = "Contents/Info.plist"
executable = "Contents/MacOS/MyDriver"
enabled = true
KeyMeaning
bundleBundle directory, relative to the configuration or kit
plistPlist path inside the bundle
executableExecutable path inside the bundle. Empty for a codeless kext
entryEntry id. Required for kit kexts
enabledfalse skips the bundle

Entry kexts and kit kexts are staged in configuration order, with kit kexts first, then entry kexts. A duplicate bundle path is refused.

Validation by UniXNU ​

UniXNU validates the bundle before staging:

  • The bundle name ends with .kext.
  • Every path is relative and contains no . or .. segment.
  • The plist is not empty.
  • The executable, when present, is a thin AArch64 MH_KEXT_BUNDLE:
    • Magic 0xfeedfacf.
    • CPU type 0x0100000c.
    • File type 11.
    • Subtype 0 or 2 (arm64 or arm64e).
    • Load commands fit inside the file.
  • A kit kext names an entry.
  • An entry kext does not name an entry.

The staged data is the unmodified plist and executable. The loader validates signatures, resolves dependencies and injects the code.

Loader protocol ​

The loader receives:

c
typedef struct {
  CONST CHAR16  *BundlePath;
  CONST CHAR16  *PlistPath;
  CONST CHAR16  *ExecutablePath;
  CONST VOID    *InfoPlist;
  UINTN         InfoPlistSize;
  CONST VOID    *Executable;
  UINTN         ExecutableSize;
} UNIXNU_LOADER_KEXT;

The buffers belong to UniXNU and stay valid for the duration of Boot().

A loader that accepts kexts implements protocol revision 2. The shipped DarwinBoot accepts kernels, the ADT sidecar, the trust cache and the optional ramdisk, and refuses a request with staged kexts.

Build a kext ​

Build the bundle with Xcode against the iOS kernel SDK:

sh
xcodebuild -target MyDriver -sdk iphoneos -configuration Release \
  ARCHS=arm64 ONLY_ACTIVE_ARCH=YES

The target uses MODULE_TYPE = KEXT and links the kernel frameworks:

SettingValue
MACH_O_TYPEmh_kext_bundle
PRODUCT_TYPEcom.apple.product-type.kernel-extension
SDKROOTiphoneos
ARCHSarm64 or arm64e
KERNEL_FRAMEWORKSKernel

The executable must match the kernel ABI of xnu-10063.141.1 and the iOS 17 deployment target.

Signing and trust ​

AMFI validates the kext against the trust cache and the code signature. A kext that is not trusted fails to load with a code signing error.

The trust cache bundle passed to the payload must include the kext CD hash. The payload step accepts the bundle only when its hash is listed in [payload] trust_cache_sha256.

Order and dependencies ​

UniXNU stages bundles in configuration order. The loader resolves OSBundleLibraries and starts the kexts in dependency order.

A codeless kext can sit in the list to express a dependency or to attach personalities to an existing binary.

Next steps ​

Released under the BSD-2-Clause-Patent License.