3.0 KiB
3.0 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
Rust/WASM project that emulates ARM64 Android binaries (libstoreservicescore.so + libCoreADI.so) via a custom Unicorn Engine fork to generate Apple Anisette headers in the browser or Node.js.
The Android library blobs are not included — extract from an APK or obtain separately.
Build Commands
Must source Emscripten before building WASM:
source "/Users/libr/Desktop/Life/emsdk/emsdk_env.sh"
bun run build # WASM (debug) + JS bundle
bun run release # WASM (release) + JS bundle
bun run build:js # JS bundle only (no WASM rebuild)
bun run build:glue # WASM only
bun run build:unicorn # Rebuild Unicorn (rarely needed)
JS bundle outputs to dist/anisette.js. WASM glue outputs to dist/anisette_rs.node.{js,wasm} and dist/anisette_rs.{js,wasm}.
The js/package.json build script also outputs to ../dist/anisette.js directly.
Architecture
Rust → WASM layer (src/)
exports.rs— all#[no_mangle]C FFI exports. Every new public function must also be added toEXPORTED_FUNCTIONSinscript/build-glue.sh(bothWEB_EXPORTED_FUNCTIONSandNODE_EXPORTED_FUNCTIONSas appropriate).adi.rs— wraps the emulated ADI library callsemu.rs— Unicorn ARM64 emulator coreidbfs.rs— Emscripten IndexedDB FS integration (browser only)
JS/TS layer (js/src/)
anisette.ts— mainAnisetteclass. EachgetData()call fully reinits the WASM state (newWasmBridge, re-writes VFS files, re-callsinitFromBlobs) to work around a Unicorn emulator bug that causes illegal writes on repeated use.wasm-bridge.ts— raw pointer/length marshalling to WASM exportswasm-loader.ts— thin wrapper aroundModuleFactory; caller must passlocateFileviamoduleOverridesto resolve the.wasmpathprovisioning.ts— Apple provisioning HTTP flow (fetches SPIM, sends CPIM)device.ts— loads or generatesdevice.json
Key design decisions
adi.pb(provisioning state) lives in the WASM VFS. After provisioning, callanisette.getAdiPb()and persist it yourself — it is not automatically written to disk.fromSo()acceptsinit.adiPbandinit.deviceJsonBytesto restore a previous session into the VFS before init.loadWasm()is environment-agnostic — nonode:imports. PasslocateFileinmoduleOverrides.- Browser FS/path gotcha:
- Prefer
./anisette/(not/anisette) forlibraryPath/provisioningPath. - The Rust emulation stubs currently hardcode
./anisetteand./anisette/adi.pbchecks insrc/stub.rs(mkdir/open), so absolute paths can break provisioning (e.g.ADIProvisioningEnd -45054). - In browser flow, mount IDBFS and run
syncfs(true)before ADI init to avoid VFS state being overwritten later.
- Prefer
Example usage
NODE_TLS_REJECT_UNAUTHORIZED=0 bun example/anisette-api.mjs \
<libstoreservicescore.so> <libCoreADI.so> [library_path]