Skip to content

EolnMsuk/DiCoy

Repository files navigation

DiCoy

A rootless / rootful / Roothide iOS jailbreak tweak (iOS 15.0–17.0, arm64 / arm64e) that replaces the camera and microphone feeds in any AVFoundation-based app with either a live mirror of the device screen or a local video/audio file, using a zero-copy multi-process architecture.

banner

Features

  • Screen Mirroring — Mirrors the screen of the app you are using the camera in and injects it as the camera feed at 30 or 60 FPS. Works with any AVFoundation-based app. Note: mirroring is limited to the current app — switching to another app or the home screen will not be reflected in the feed.
  • Media Injection — Replaces the camera with a local .mp4 or .mov file; also replaces the microphone with the file's audio track. The file loops seamlessly at EOF.
  • Rotation & Flip — GPU-side CIImage transforms: rotate Default / Counter-Clockwise / Clockwise / Upside-Down; flip Horizontally or Vertically. Applies to both modes.
  • Zero-copy IPC — Only a 4-byte IOSurface ID crosses the process boundary; both processes map the same GPU memory pages, so no pixel data is ever copied.
  • Battery aware — DiCoyServer automatically pauses capture at 10 Hz when no app has the camera open and resumes at full frame rate when a session starts.
  • Rootless, rootful & Roothide — Ships as a rootless .deb (Dopamine, palera1n rootless) and a rootful .deb (checkra1n, unc0ver). Roothide users can convert the rootless .deb with RootHide Patcher.

How It Works

DiCoy is a two-binary design — no separate daemon process. DiCoyTweak is a MobileSubstrate dylib injected into every AVFoundation process and into SpringBoard. The SpringBoard instance runs DiCoyServer, which captures the screen and delivers frames to the camera app.

┌─────────────────────────────────────────────────────────────────┐
│  DiCoyTweak  (injected into SpringBoard — runs DiCoyServer)     │
│                                                                 │
│  Capture priority (first success wins):                         │
│  1. IOMobileFramebuffer — GPU front-buffer IOSurface            │
│  2. CGDisplayStream — full compositor, no permission prompt     │
│  3. RPScreenRecorder (SpringBoard context)                      │
│  4. CARenderServer — last resort                                │
│                                                                 │
│  Frame delivery:                                                │
│  ├─ mmap  /var/tmp/dicoy_frame.raw  (seqlock — background)      │
│  └─ XPC   com.dicoy.server          (IOSurface as Mach right)   │
└──────────────────┬──────────────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────────────────────────┐
│  DiCoyTweak  (injected into camera app)                         │
│                                                                 │
│  Foreground: in-process RPScreenRecorder → latestSurface        │
│  Background: mmap seqlock read → CVPixelBuffer (IOSurface)      │
│                                                                 │
│  [optional CIImage rotation / flip — GPU-side, CIContext]       │
│       │                                                         │
│       ▼                                                         │
│  CMSampleBufferCreateForImageBuffer ──► app delegate callback   │
└─────────────────────────────────────────────────────────────────┘

The tweak intercepts AVCaptureVideoDataOutput and AVCaptureAudioDataOutput via a unified delegate hook (dicoyHookDelegate). MSHookMessageEx patches captureOutput:didOutputSampleBuffer:fromConnection: on the concrete delegate class once per class, dispatching on output type to avoid double-wrap corruption.

In Media Inject mode no screen capture occurs. AVAssetReader decodes video frames to 32BGRA (matching the IOSurface format) and audio to interleaved 16-bit LPCM at the session's sample rate, restamped to CACurrentMediaTime() for seamless delivery.


Requirements

  • Rootless jailbreak (Dopamine 2.x, palera1n rootless) or rootful jailbreak (checkra1n, unc0ver) or Roothide (via RootHide Patcher on the rootless .deb)
  • iOS 15.0 – 17.0, arm64 / arm64e
  • libSandy (com.opa334.libsandy)

Installation

  1. Download the latest .deb from the Releases page — pick the rootless or rootful variant to match your jailbreak.
  2. Transfer the .deb to the device and install with your package manager (Sileo, Zebra, or dpkg -i).

After installation, respring the device. DiCoyTweak is injected automatically by MobileSubstrate; no daemon or launchd entry is required. Confirm the tweak is active by opening Settings → DiCoy and verifying the version footer appears.


Usage

Open Settings → DiCoy on the device.

Setting Description
Mode: Off DiCoy is inactive; all apps see the real camera and microphone.
Mode: Screen Mirror The current app's screen is injected as the camera feed. Only mirrors the app you are using the camera in. Requires a respring to take effect.
Mode: Media Inject A local media file is injected as both camera and microphone. Enter the full path below.
File Path Absolute path to a .mp4 or .mov file on the device filesystem. Only visible in Media Inject mode.
Rotate Rotate the output: Default / Counter-Clockwise / Clockwise / Upside-Down. Visible when mode is active.
Flip Mirror the output: None / Horizontally / Vertically. Visible when mode is active.
Frame Rate 30 FPS (default) or 60 FPS. Restart the camera app to apply. Visible when mode is active.

Tap Save after making changes. A respring confirmation will appear; confirm to apply settings.


Technical Notes

How does Screen Mirror work? In Screen Mirror mode, DiCoy starts RPScreenRecorder in-process inside the camera app (e.g. Discord). This asks the user for screen recording permission once, then delivers frames of the current app's own UI directly into the camera pipeline via latestSurface. Capture is limited to the app the camera is open in — other apps and the home screen are not captured. DiCoyServer in SpringBoard also runs a separate capture pipeline (IOMobileFramebuffer → CGDisplayStream → CARenderServer → RPScreenRecorder as a last resort) and writes to a mmap file, which is used as a fallback when the camera app is backgrounded.

Why IOSurface for IPC? Passing raw pixels at 30 FPS (1284×2778×4 bytes ≈ 14 MB/frame on a Pro Max) would cost ~420 MB/s of memory bandwidth. IOSurface is a kernel-managed shared memory object; only its integer ID (4 bytes) crosses the process boundary via XPC, and IOSurfaceLookup() maps the same physical GPU pages into the receiving process with no copy.

Unified delegate hook Hooking a protocol method generically in Logos requires knowing the concrete class at compile time. Instead, dicoyHookDelegate() intercepts setSampleBufferDelegate:queue: and patches captureOutput:didOutputSampleBuffer:fromConnection: on the concrete delegate class via MSHookMessageEx — once per class, guarded by gHookedDelegateClasses. The IMP dispatches on [output isKindOfClass:] to handle video and audio from a single hook, preventing the double-wrap corruption that occurs when two separate hook calls chain onto the same class and selector.

Audio injection format matching On the first microphone callback, DiCoyAudioProxy extracts the real session's AudioStreamBasicDescription (sample rate, channel count) from the CMAudioFormatDescriptionRef and passes it to AVAssetReaderTrackOutput as the output settings. This guarantees the injected LPCM audio is format-compatible with the session without any resampling.

Rotation and flip Both transforms are applied GPU-side via CIImage -imageByApplyingTransform: rendered into a new CVPixelBuffer with kCVPixelBufferIOSurfacePropertiesKey. A translation normalization step corrects the negative-origin extent produced by scale-based flip transforms before rendering.

libSandy / SandyXpc DiCoy uses libSandy (com.opa334.libsandy) to obtain a sandbox extension that lets the tweak dylib (running in a sandboxed app process) read the mmap frame file and connect to the com.dicoy.server XPC service running in SpringBoard. The SandyXpc MachXPC transport sources are compiled directly into DiCoyTweak; libSandy is linked at build time and must be installed on the device separately.


Developer

Support me BTCVenmo