macOS Code Signing for Patched Binaries

• Reverse Engineering

The Problem

When patching a macOS application binary (like modifying RobloxPlayer to disable purchase prompts), you'll quickly discover that modern macOS won't let you run modified binaries that still have their original code signature. The OS detects the mismatch and refuses to execute the binary.

But simply removing the signature isn't enough on macOS 15 and later - the system still refuses to launch unsigned binaries with cryptic errors.

The Journey

Step 1: Removing the Original Signature

First, verify the binary is signed:

codesign -d -vv "/Applications/Roblox.app/Contents/MacOS/RobloxPlayer"

You'll see output showing the developer's signature, authority chain, and team identifier. To remove it:

codesign --remove-signature "/Applications/Roblox.app/Contents/MacOS/RobloxPlayer"

Verify it's gone:

codesign -d "/Applications/Roblox.app/Contents/MacOS/RobloxPlayer"
# Output: code object is not signed at all

Step 2: The Launch Failure

Attempting to launch the unsigned binary results in:

Error Domain=RBSRequestErrorDomain Code=5 "Launch failed."
Error Domain=NSPOSIXErrorDomain Code=153 "Unknown error: 153"
Launchd job spawn failed

Error 153 indicates that macOS Gatekeeper or launchd is blocking the execution of the unsigned binary. Modern macOS versions are increasingly strict about running unsigned code.

Step 3: Ad-Hoc Re-Signing

The solution is to re-sign the binary with an ad-hoc signature. This tells macOS "yes, this binary is intentionally modified and I trust it":

codesign -s - -f --deep "/Applications/Roblox.app"

Breaking down the flags:

-s -
Sign with an ad-hoc signature (the dash means "no identity")
-f
Force - replace any existing signature
--deep
Sign nested code (frameworks, plugins, etc. within the app bundle)

Why This Works

An ad-hoc signature doesn't provide cryptographic verification of the code's origin (like a Developer ID does), but it does:

  • Satisfy launchd's requirement that binaries have some form of signature
  • Create a code directory hash that the OS can verify hasn't been tampered with after signing
  • Allow the binary to pass basic security checks without needing a developer certificate

The ad-hoc signature essentially says "I, the local user, vouch for this binary" rather than "Apple and this developer vouch for this binary".

Verification

After ad-hoc signing, verify the new signature:

codesign -d -vv "/Applications/Roblox.app/Contents/MacOS/RobloxPlayer"

You'll see it now has a signature, but with no authority chain - just "Signature=adhoc".

Important Considerations

System Integrity Protection (SIP)

This technique works for applications in user-writable locations like /Applications. System binaries protected by SIP cannot be modified even with root privileges.

Gatekeeper

First launch may still trigger a Gatekeeper warning. You can bypass this via:

  • Right-click → Open
  • System Settings → Privacy & Security → "Open Anyway"
  • Command line: xattr -cr /Applications/Roblox.app to remove quarantine attributes

Updates

Any update to the application will replace your patched binary with the official version. You'll need to reapply patches and re-sign after each update.

Online Verification

Some applications perform additional integrity checks (comparing hashes with a server, verifying specific code sections, etc.). Ad-hoc signing won't help with those - you'd need to patch the verification routines as well.

Complete Workflow

The full process for patching and running a macOS binary:

  1. Make a backup: cp -R /Applications/App.app /Applications/App.app.backup
  2. Patch the binary (with Ghidra, hex editor, etc.)
  3. Remove original signature: codesign --remove-signature /Applications/App.app/Contents/MacOS/Binary
  4. Ad-hoc re-sign: codesign -s - -f --deep /Applications/App.app
  5. Launch and test