English · 简体中文
A FairPlay toolkit for macOS. Its API mirrors the upstream Go unfairplay CLI.
The core lives in CUnfairPlay (plain C, no dependencies beyond libz), and
UnfairPlayCore provides the Swift wrappers, model-directory handling, and the
native enrollment pipeline.
- In-process engine: no external
unfairplaybinary and no child processes. - One set of method signatures and JSON contracts:
UnfairPlayCLI(subprocess) andUnfairPlayNativeCLI(in-process) both implementUnfairPlayClient, so they are interchangeable.
- macOS 14 or later
- Swift 6.0 toolchain
.package(url: "https://github.com/maclub-net/UnfairPlayKit.git", from: "0.2.0")Import UnfairPlayCore for the Swift layer (including resources) or
CUnfairPlay for the C API.
import UnfairPlayCore
let cli = UnfairPlayNativeCLI()
// Validation: an explicit directory must exist; nil skips model loading.
let report = try await cli.doctor(ipa: ipaURL, modelDirectory: modelDirectory)
// Decryption: a nil modelDirectory falls back to the upstream CLI default.
let result = try await cli.decrypt(
input: ipaURL,
output: outputURL,
report: reportURL,
modelDirectory: modelDirectory
)Use UnfairPlayCLI(binaryPath:) when you need the old subprocess behavior; the
calls are identical.
Decrypted (砸壳) IPA packages can be downloaded from the Mac Club website: https://www.maclub.net. The decryption path above accepts the encrypted IPAs you already own, so you can also decrypt them locally.
UnfairPlaySetup has four stages that map one-to-one onto the Go
validate → capture → recovery → install pipeline:
| Stage | Method | Privileges |
|---|---|---|
| Validate | validate(options:) |
User |
| Capture | capture(options:captureDirectory:owner:) |
root |
| Recover | recover(capture:options:) |
User |
| Install | installModel(directory:selector:model:owner:) |
User |
Run the whole pipeline at once (decryption validation excluded):
let options = UnfairPlaySetupOptions(
appURL: URL(fileURLWithPath: "/Applications/Foo.app"),
ipaURL: ipaURL,
modelDirectoryURL: modelDirectory
)
let capture = try UnfairPlaySetup.run(
options: options,
captureDirectory: captureURL
)Capture reads /private/var/db/fpsd, so it must run as root. Pass owner to
hand the whole capture tree back to that account. The model is written to
UnfairPlaySetupOptions.modelDirectoryURL.
<selector>-<sha256>.bin
Colons in the selector become dashes. The directory is 0700 and each file is 0600; an existing identical model is reused.
capture/
manifest.json # schema: unfairplay-native-enrollment-capture-v1
app/ # SINF / SUPF from the installed bundle
state/ # state files under /private/var/db/fpsd
hardware/iomac.bin # MAC read via IORegistry (hardwareInterface, en0)
fairplayd/fairplayd.arm64e
ipa/ # the matching SINF from the original IPA
App Store iOS apps on Apple silicon are nested as Wrapper/<name>.app, with
SC_Info inside the inner bundle; validate detects this automatically.
UnfairPlayModelStore scans a model directory. It is reported unusable as a
whole when there are more than 256 models, the directory is not 0700, or any
file is accessible beyond 0600.
let store = UnfairPlayModelStore(directoryURL: modelDirectory)
let report = store.scan()Sources/
CUnfairPlay/ C core: IPA/ZIP, SINF/SUPF, Mach-O, license, models, decrypt, recovery
UnfairPlayCore/ Swift API, manifest, model store, native setup/recovery
Resources/ recovery interpreter assets (model, compact CFG, frame, kernels)
Tests/UnfairPlayCoreTests/
swift build
swift testTests use synthetic fixtures. Account-level artifacts (*.ipa, *.sinf,
*.supf, hardware MACs, and so on) are never committed; see .gitignore. The
interpreter assets under Sources/UnfairPlayCore/Resources/ are package
resources and ship with the package.
Use this package only for content you lawfully obtained on hardware you own. Follow the laws of your jurisdiction and Apple's license terms; do not use it to bypass DRM you are not entitled to remove.