Hot-fix iOS/macOS apps using WebAssembly payloads. Compile C code to WASM and replace Objective-C methods at runtime.
WasmPatch bridges Objective-C and WebAssembly. It compiles C code into WebAssembly modules and lets those modules call any Objective-C class or method dynamically โ so you can hot-fix bugs, add features, and replace methods at runtime without shipping a new binary.
Patch & bridge
- ๐ Replace class & instance methods at runtime; call any Obj-C method from wasm
- ๐ Pass/return structs by value โ
CGPoint/CGSize/CGRect/NSRangeand arbitrary structs viaalloc_struct+ field accessors - ๐งฉ Blocks both ways โ invoke completion handlers you're handed (
invoke_block) and create blocks to pass into Obj-C (create_block) - ๐ข Value-type bridging for strings, numbers,
BOOL, objects, selectors, C strings
Swift support
- ๐ฆ
Hook
@objc dynamicSwift methods; module-qualified class names - ๐
Tool/scan-hookable.shlists a binary's hookable surface; clean Swift API viaNS_SWIFT_NAMEโ see SWIFT.md
Authoring & tooling
- โ๏ธ
WAP_REPLACE_*macros (a misspelled target fails to compile), scope cleanup pools,call_*_3/4 - ๐ ๏ธ
Tool/wasmpatchCLI โdoctor,build(auto-injects<wasmpatch.h>, emits sha256/meta),keygen,sign - ๐ฆ Integrates via Swift Package Manager and CocoaPods
Delivery & safety
- ๐
WAPPatchManagerโ fetch, verify, cache, and apply remote patches - ๐ EC P-256 signature verification (authenticity) on top of SHA-256 (integrity)
- ๐ฉบ Host log handler, strict-hook load policy, and structured load/runtime errors
flowchart LR
subgraph Author["โ๏ธ Author side"]
C["patch.c"]
CLI["Tool/wasmpatch build"]
WASM["patch.wasm<br/>+ .sha256 + signature"]
C --> CLI --> WASM
end
subgraph App["๐ฑ Your iOS / macOS app"]
LOADER["WAPPatchLoader /<br/>WAPPatchManager"]
RT["wasm3 interpreter"]
BRIDGE["Host export bridge (C ABI)"]
OBJC["Objective-C / Swift runtime"]
LOADER --> RT --> BRIDGE --> OBJC
end
WASM -->|"verify ยท cache ยท deliver"| LOADER
OBJC -. "replaced IMP via libffi" .-> RT
style WASM fill:#bbf,color:#000
style OBJC fill:#bfb,color:#000
A patch is just a C file compiled to a tiny WebAssembly module. The app
verifies and loads it into a sandboxed wasm3 interpreter, whose only powers
are the host functions WasmPatch exports โ that bridge is what reaches into the
live Objective-C / Swift runtime.
| Feature | Description |
|---|---|
| ๐งฑ WASM Compilation | Compile C code to WebAssembly with clang/LLVM |
| ๐ Objective-C Bridge | Call any Obj-C class or method from WebAssembly |
| ๐ Method Replacement | Hot-fix by replacing Obj-C methods at runtime |
| ๐ Cross-Platform | Works on both iOS and macOS |
| ๐ Struct Bridging | Pass/return geometry structs and arbitrary by-value structs โ including unions & bitfields (alloc_struct, field + bit accessors) |
| โ๏ธ Author Ergonomics | WAP_REPLACE_* macros (typos fail to compile), scope cleanup pools, call_*_3/4 |
| ๐ ๏ธ Runtime Diagnostics | Host log handler, strict-hook policy, structured load/runtime errors |
| ๐ฆ SPM + CocoaPods | Swift Package Manager and CocoaPods integration |
| ๐งฉ Block Callbacks | Invoke received blocks (invoke_block) and create blocks to pass into Obj-C (create_block) |
| ๐ฆ Swift Support | @objc dynamic hooking + hookable-surface scanner โ see SWIFT.md |
| ๐ Remote Delivery | WAPPatchManager โ fetch, SHA-256 verify, cache, apply |
| ๐ Patch Signing | EC P-256 / ECDSA signature verification before load (Tool/wasmpatch sign) |
| ๐งช Regression Assets | Test case bundle and fixture hosts for bridge validation |
graph TD
A[C Code] --> B[clang/LLVM]
B --> C[WebAssembly Module]
C --> D[WasmPatch Runtime]
D --> E[Objective-C Runtime]
E --> F[Hot-fix Applied!]
style A fill:#f9f,color:#000
style C fill:#bbf,color:#000
style F fill:#bfb,color:#000
- Write your patch logic in C
- Compile to WebAssembly using
clang/LLVM - Load the wasm module in your app
- Call Objective-C classes/methods from WebAssembly
- Replace methods on the fly
# Install LLVM with WebAssembly target
brew install llvm
# Or use the provided script
sh Tool/install-llvm.shgit clone https://github.com/everettjf/WasmPatch.git
cd WasmPatchIntegrate into your app via Swift Package Manager:
// Package.swift
.package(url: "https://github.com/everettjf/WasmPatch.git", branch: "master")or CocoaPods (pod 'WasmPatch'). Swift apps: see SWIFT.md for what
@objc dynamic methods can be hooked and how value/struct types bridge.
# Check the toolchain is ready (clang wasm target, wasm-ld, SDK header)
Tool/wasmpatch doctor
# Compile a patch โ the author SDK header <wasmpatch.h> is on the include path
# automatically, and a .sha256 + .meta.json are emitted next to the .wasm.
Tool/wasmpatch build your_patch.c
Tool/wasmpatch build your_patch.c build/your_patch.wasm
# (the older entry point still works)
sh Tool/build-patch.sh your_patch.cPatch sources just #include <wasmpatch.h> and use the ergonomic macros:
#include <wasmpatch.h>
WAPObject my_token(WAPObject self, const char *cmd) {
return new_objc_nsstring("patched");
}
int entry() {
// Registered name is derived from the real symbol โ a typo won't compile.
WAP_REPLACE_CLASS(MyClass, "token", my_token);
WAP_POOL_BEGIN(pool); // scope-based cleanup
WAPObject s = WAP_KEEP(pool, new_objc_nsstring("hi"));
call_class_method_1("Logger", "log:", s);
WAP_POOL_END(pool); // frees everything kept
return 0;
}#import <WasmPatch/WAPPatchLoader.h>
NSError *error = nil;
WAPPatchLoaderOptions *options = [WAPPatchLoader recommendedOptions];
options.expectedSHA256Hex = @"<optional sha256>";
BOOL success = [WAPPatchLoader loadPatchNamed:@"your_patch"
inBundle:NSBundle.mainBundle
options:options
error:&error];
if (!success) {
NSLog(@"load failed: %@", error.localizedDescription);
NSLog(@"runtime detail: %@", error.userInfo[WAPPatchLoaderRuntimeMessageKey]);
}Low-level C API is still available when you need it:
#import <WasmPatch/WasmPatch.h>
BOOL success = wap_load_file("your_patch.wasm");
if (success) {
NSLog(@"loaded");
} else {
NSLog(@"load failed: %s", wap_last_error());
}WasmPatch/
โโโ WasmPatch/ # Core framework
โ โโโ WasmPatch.h # Public C API
โ โโโ core/runtime/ # WASM runtime and exports
โ โโโ core/method/ # Obj-C method bridge and hooks
โโโ Tool/ # Build tools
โ โโโ c2wasm.sh # C to WASM compiler
โ โโโ install-llvm.sh # LLVM installer
โโโ TestCase/ # Test cases
โ โโโ compile-testcase.sh # Test compiler
โ โโโ WasmPatch-TestCase/ # Sample host classes and wasm fixtures
โโโ Image/ # Documentation images
โโโ Demo/ # Demo projects
โ โโโ iOS/ # iOS demo (Objective-C)
โ โโโ macOS/ # macOS demo (Objective-C)
โ โโโ WasmPatch-SwiftUI/ # macOS SwiftUI app โ live hot-patch of an @objc dynamic method
โโโ README.md
bool wap_load_file(const char * path);
bool wap_load_data(const void * bytes, unsigned int size);
void wap_reset_runtime(void);
bool wap_runtime_is_loaded(void);
const char * wap_last_error(void);NSError *error = nil;
WAPPatchLoaderOptions *options = [WAPPatchLoader recommendedOptions];
options.allowReload = YES;
options.resetBeforeLoad = YES;
[WAPPatchLoader loadPatchAtPath:path options:options error:&error];
[WAPPatchLoader loadPatchNamed:@"objc" inBundle:bundle options:options error:&error];
[WAPPatchLoader loadPatchData:data options:options error:&error];
[WAPPatchLoader reset];Error handling:
if (error.code == WAPPatchLoaderErrorCodeSHA256Mismatch) {
NSLog(@"patch tampered: %@", error.userInfo[WAPPatchLoaderRuntimeMessageKey]);
}// advanced_patch.c
#include <wasmpatch.h>
int entry() {
WAPObject message = new_objc_nsstring("WasmPatch detected request");
call_class_method_1("NetworkManager", "logRequest:", message);
dealloc_object(message);
replace_instance_method("NetworkManager", "sendRequest:", "wasm_custom_send_request");
return 0;
}| Requirement | Version | Description |
|---|---|---|
| macOS | 10.14+ | Development environment |
| Xcode | 11+ | iOS/macOS SDK |
| LLVM/Clang | 14+ | C to WASM compilation |
| wabt | latest | wasm2wat tooling |
# Build the framework
cd WasmPatch/WasmPatch.xcodeproj
xcodebuild -project WasmPatch.xcodeproj \
-scheme WasmPatch \
-configuration Release \
-sdk iphoneos build
# Build for macOS
xcodebuild -project WasmPatch.xcodeproj \
-scheme WasmPatch \
-configuration Release \
-sdk macosx build# Compile testcase wasm fixtures
cd TestCase
sh compile-testcase.sh
# Compile your own patch with defaults
sh Tool/build-patch.sh path/to/patch.c
# Verify wasm modules
wasm2wat your_patch.wasm -o your_patch.watProduction validation:
sh Tool/validate-production.shmacOS host validation only:
sh Tool/run-macos-validation.sh| Platform | Support | Min Version |
|---|---|---|
| iOS | โ Full | 10.0 |
| macOS | โ Full | 10.14 |
| Simulator | โ Full | Same as above |
| Test Case | Description |
|---|---|
objc.c |
Objective-C bridge coverage and method replacement fixture |
CallMe |
Host methods for bridge argument/result validation |
ReplaceMe |
Host methods used for runtime replacement verification |
Run all tests:
sh TestCase/compile-testcase.shFull docs live in pages/ (a GitHub Pages site โ see
pages/DEPLOY.md to publish it):
- Tutorial โ install โ write โ build โ load
- Authoring patches โ SDK, macros, cleanup pools, calling methods
- Swift support โ what's hookable, value/struct/block bridging (SWIFT.md)
- Structs ยท Blocks ยท Diagnostics
- Remote delivery ยท Patch signing ยท Integration
Reference: Architecture ยท Runtime API ยท Tooling ยท Roadmap
WAPPatchManager downloads a patch, verifies its SHA-256, caches it, and applies it:
sequenceDiagram
participant App
participant Mgr as WAPPatchManager
participant Srv as Patch server
App->>Mgr: fetchPatchFromURL:named:sha256:
Mgr->>Srv: GET patch.wasm
Srv-->>Mgr: bytes
Mgr->>Mgr: verify SHA-256 (integrity)
Mgr->>Mgr: cache on disk
Mgr-->>App: completion(cachedPath)
App->>Mgr: applyCachedPatchNamed:
Mgr->>Mgr: load + apply via libffi
#import <WasmPatch/WAPPatchManager.h>
[[WAPPatchManager sharedManager] fetchPatchFromURL:url
named:@"login_fix"
sha256:expectedHash
completion:^(NSString *cachedPath, NSError *error) {
if (cachedPath) {
[[WAPPatchManager sharedManager] applyCachedPatchNamed:@"login_fix" options:nil error:nil];
}
}];SHA-256 (expectedSHA256Hex) proves integrity; an EC P-256 signature proves
authenticity โ a tampered patch can't be re-signed without the private key.
flowchart TD
KG["wasmpatch keygen<br/>โ patch_key.pem (private)"] --> SIGN["wasmpatch sign"]
BUILD["wasmpatch build<br/>โ patch.wasm"] --> SIGN
SIGN --> PUB["publicKeyECBase64<br/>(embedded in app)"]
SIGN --> SIG["signatureBase64<br/>(shipped with patch)"]
PUB --> GATE
SIG --> GATE
PATCH["delivered patch.wasm"] --> GATE{"verify SHA-256<br/>+ EC P-256"}
GATE -->|valid| OK["โ
load and apply"]
GATE -->|invalid| NO["โ refuse:<br/>SignatureInvalid"]
style OK fill:#bfb,color:#000
style NO fill:#fbb,color:#000
# one-time: create a signing key (keep the .pem private)
Tool/wasmpatch keygen patch_key.pem
# sign a built patch; prints publicKeyECBase64 + signatureBase64
Tool/wasmpatch sign your_patch.wasm patch_key.pemWAPPatchLoaderOptions *options = [WAPPatchLoader recommendedOptions];
options.publicKeyECBase64 = @"<embedded public key (uncompressed point), base64>";
options.signatureBase64 = @"<signature delivered alongside the patch>";
NSError *error = nil;
if (![WAPPatchLoader loadPatchAtPath:path options:options error:&error] &&
error.code == WAPPatchLoaderErrorCodeSignatureInvalid) {
NSLog(@"refused: patch is not authentically signed");
}- Author ergonomics (macros, cleanup pools, CLI), struct bridging, completion-
handler (block) invocation, host log handler, strict-hook load policy, SPM
support, remote delivery (
WAPPatchManager), and Swift@objc dynamichooking are in place and exercised end-to-end (Tool/validate-*.sh). - Bidirectional block bridging, EC P-256 patch signing, and generic by-value struct bridging (including structs with unions and bitfields) are all in place. Remaining edges (bare top-level unions, signing-key distribution tooling) are noted in ROADMAP.md.
sh Tool/build-patch.sh your_patch.c
sh Tool/validate-production.shIf both pass, the current repo state is ready for internal release and integration validation.
Contributions are welcome! Areas to help:
- ๐ Bug fixes
- โจ New features
- ๐ Documentation
- ๐งช Test cases
- ๐ก Performance improvements
WasmPatch is released under the MIT License.
Inspired by:
- WebAssembly - Binary format
- Fishhook - Symbol rebinding
- JSPatch - Hot-fix concept
ๆ้ฎ้ข๏ผๅป Issues ๆ้ฎ๏ผ
Made with โค๏ธ by Everett
Project Link: https://github.com/everettjf/WasmPatch
