Skip to content

everettjf/WasmPatch

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

44 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

WasmPatch ๐Ÿงฑ

GitHub Stars GitHub Forks License Issues

WebAssembly-driven hot patching for iOS/macOS Objective-C apps

English

Hot-fix iOS/macOS apps using WebAssembly payloads. Compile C code to WASM and replace Objective-C methods at runtime.


๐ŸŽฏ What is WasmPatch?

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.

Capabilities at a glance

Patch & bridge

  • ๐Ÿ”„ Replace class & instance methods at runtime; call any Obj-C method from wasm
  • ๐Ÿ“ Pass/return structs by value โ€” CGPoint/CGSize/CGRect/NSRange and arbitrary structs via alloc_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 dynamic Swift methods; module-qualified class names
  • ๐Ÿ”Ž Tool/scan-hookable.sh lists a binary's hookable surface; clean Swift API via NS_SWIFT_NAME โ€” see SWIFT.md

Authoring & tooling

  • โœ๏ธ WAP_REPLACE_* macros (a misspelled target fails to compile), scope cleanup pools, call_*_3/4
  • ๐Ÿ› ๏ธ Tool/wasmpatch CLI โ€” 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

WasmPatch Architecture

Architecture at a glance

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
Loading

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.


โœจ Features

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

๐Ÿ—๏ธ How It Works

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
Loading
  1. Write your patch logic in C
  2. Compile to WebAssembly using clang/LLVM
  3. Load the wasm module in your app
  4. Call Objective-C classes/methods from WebAssembly
  5. Replace methods on the fly

๐Ÿš€ Quick Start

Prerequisites

# Install LLVM with WebAssembly target
brew install llvm

# Or use the provided script
sh Tool/install-llvm.sh

1. Clone and Setup

git clone https://github.com/everettjf/WasmPatch.git
cd WasmPatch

Integrate 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.

2. Compile a Patch

# 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.c

Patch 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;
}

3. Load in Your App

#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());
}

๐Ÿ“ Project Structure

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

๐Ÿ’ป Examples

Public Runtime API

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);

High-Level Objective-C API

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]);
}

Call Objective-C from WASM

// 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;
}

๐Ÿ› ๏ธ Development

Requirements

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

# 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

Test

# 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.wat

Production validation:

sh Tool/validate-production.sh

macOS host validation only:

sh Tool/run-macos-validation.sh

๐Ÿ“ฑ Platform Support

Platform Support Min Version
iOS โœ… Full 10.0
macOS โœ… Full 10.14
Simulator โœ… Full Same as above

๐Ÿงช Test Cases

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.sh

๐Ÿ“š Documentation

Full docs live in pages/ (a GitHub Pages site โ€” see pages/DEPLOY.md to publish it):

Reference: Architecture ยท Runtime API ยท Tooling ยท Roadmap

Remote Delivery

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
Loading
#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];
    }
}];

Patch Signing

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
Loading
# 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.pem
WAPPatchLoaderOptions *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");
}

Current Maturity

  • 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 dynamic hooking 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.

Release Checklist

sh Tool/build-patch.sh your_patch.c
sh Tool/validate-production.sh

If both pass, the current repo state is ready for internal release and integration validation.


๐Ÿค Contributing

Contributions are welcome! Areas to help:

  • ๐Ÿ› Bug fixes
  • โœจ New features
  • ๐Ÿ“ Documentation
  • ๐Ÿงช Test cases
  • ๐Ÿ’ก Performance improvements

๐Ÿ“œ License

WasmPatch is released under the MIT License.


๐Ÿ™ Acknowledgements

Inspired by:


๐Ÿ“ˆ Star History

Star History Chart


๐Ÿ“ž Support

GitHub Issues WeChat

ๆœ‰้—ฎ้ข˜๏ผŸๅŽป Issues ๆ้—ฎ๏ผ


Made with โค๏ธ by Everett

Project Link: https://github.com/everettjf/WasmPatch

About

๐ŸงฑYet Another Patch Module for iOS/macOS via WebAssembly

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors