revenge-tweak/AUDIT.md
Ally Founder dba11af542 fix: restore compilability and add Bridgeless-mode injection hook
- Fix BunnyLog compile error in Sideloading.x (leftover from Bunny->Revenge
  rename; only RevengeLog is defined)
- Remove duplicated -loadConfig/+defaultConfig methods in LoaderConfig.m
- Repoint CI's ipa-patcher download at a live mirror with a pinned checksum
  (Cristiandis/ipa-patcher was deleted upstream)
- Add a second injection hook on RCTInstance/-_loadScriptFromSource: for
  React Native's Bridgeless mode, alongside the existing RCTCxxBridge hook.
  Discord's Android build already runs Bridgeless (confirmed via
  revenge-xposed's hook targets) and revenge-bundle now pins RN 0.81, which
  defaults to it - so the legacy bridge hook this tweak relied on was very
  likely dead. Unverified against a real device; see AUDIT.md.

See AUDIT.md for the full iOS-vs-Android comparison and remaining gaps.
2026-08-03 09:46:46 -04:00

5 KiB

iOS parity audit (2026-08-03)

Revenge dropped official iOS support; this repo was archived upstream in Nov 2024 (revenge-mod/revenge-tweak, last commit 35ad28b) while the Android loader (revenge-mod/revenge-xposed) kept shipping. This audit compares the two and fixes what could be fixed without a jailbroken device to test against.

Fixed in this fork

  • Sources/Sideloading.x: BunnyLog(...) referenced a macro that no longer exists (the project was renamed Bunny → Revenge; only RevengeLog is defined in Headers/Logger.h). This made the file fail to compile. Fixed by using RevengeLog.
  • Sources/LoaderConfig.m: -loadConfig and +defaultConfig were each defined twice in the same @implementation (leftover merge conflict). Removed the duplicates.
  • .github/workflows/deploy.yml: the CI patcher step pointed at Cristiandis/ipa-patcher release release-pyon, which 404s — that repo no longer exists. Repointed at the still-live fork bunny-mod/ipa-patcher (same tag, release build) and pinned it with a verified SHA-256 checksum. (These three match the fixes proposed in the never-merged upstream PR #8, which the sole maintainer confirmed worked but never landed before the repo was archived.)

The real blocker: this tweak's hook point is very likely dead

Sources/Tweak.x hooks RCTCxxBridge -executeApplicationScript:url:async: — the script-injection point for React Native's legacy bridge. Evidence points to Discord having moved off that architecture entirely:

  • revenge-bundle's package.json pins react-native: 0.81.4, which defaults to the New Architecture ("Bridgeless mode").
  • revenge-xposed (the actively-maintained Android loader) hooks com.facebook.react.runtime.ReactInstance$loadJSBundle$1 — the Bridgeless-mode class — not the legacy CatalystInstanceImpl, and its DiscordDevSupport.kt explicitly targets com.facebook.react.devsupport.BridgelessDevSupportManager.

Cross-platform RN apps built from one codebase almost always enable/disable the New Architecture together, so if Android Discord is Bridgeless, iOS Discord almost certainly is too — meaning RCTCxxBridge is likely never instantiated on current Discord iOS builds, and the tweak's only injection point silently never fires.

Fix applied (unverified): added a second hook, %hook RCTInstance / -_loadScriptFromSource:, the Bridgeless-mode equivalent, confirmed against React Native's actual upstream source (ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm). Both hooks now share one injection pipeline (runRevengeInjection, factored out of the old single hook body); Logos skips a %hook target gracefully if the class isn't present at runtime, so shipping both is safe regardless of which architecture the installed Discord build actually uses.

Since RCTSource (the type _loadScriptFromSource: takes) has no public initializer, makeRCTSource() (Sources/Utils.m) builds one by poking its private ivars directly — the same technique RN's own private RCTSourceCreate() uses internally.

This has not been run against a real device or the current Discord iOS binary. It's a best-effort port grounded in verified upstream RN source and the confirmed Android hook target, not a tested fix. Needs a jailbreak + Xcode/Theos to validate; if _loadScriptFromSource: turns out to have a different selector name in Discord's actual build (obfuscation is unlikely for framework code, but app-specific subclasses could differ), it'll need adjusting via class-dump/Frida against the live binary.

Remaining feature gap vs. Android (not attempted here)

Beyond the injection hook, revenge-xposed has grown a much larger feature set that iOS has no equivalent of. Porting these is real native engineering work, not something to guess blind without a device to test each step against:

Feature Android iOS
Script injection RevengeScriptLoader + Bridgeless hook Fixed above (unverified)
Theme/font/sideloading themes/fonts/sysColors, resource-ID fix Theme.m/Fonts.x/Sideloading.x (present, untouched)
Native↔JS RPC bridge RevengeBridge — bidirectional calls smuggled through existing exposed RN native modules (RNSVGRenderableManager.getBBox, FileReaderModule.readAsDataURL) none
Plugin system External plugin loading + repos (PluginLoader, RepoStore, RepoInstall, Resolver, ...) none
Internal plugins Recovery mode, no-track, OTA-update prevention, example plugin none
Dev support LogBox + shake-to-recovery wired into RN's dev menu none
Caches/lifecycle management Caches.kt, LifecycleSupport.kt none

The bridge + plugin system is the biggest gap and the most architecturally novel part of the Android side — it's what everything else (internal plugins, dev support) is built on. Recommend treating it as its own follow-up project once the injection hook above is confirmed working on-device, rather than attempting it speculatively.