camera: libvcamcaptured + cameracaptured TweakLoader allowlist + install wiring

libvcamcaptured.dylib is loaded into /usr/libexec/cameracaptured via
TweakLoader and registers a synthetic FigCaptureSource backed by the
vphone shm region. From AVF's point of view there is now a normal
"vphone:vcam:0" camera device that streams BGRA frames at the
session's requested width/height.

  scripts/vcamcaptured/                .gitignore (drop built .dylib),
                                       Makefile, libvcamcaptured.m,
                                       libvcamcaptured.plist (filter:
                                       Executables=["cameracaptured"]).

  scripts/tweakloader/TweakLoader.m    add /usr/libexec/cameracaptured
                                       to kVPhoneAllowedDaemonPaths so
                                       TweakLoader engages in a daemon
                                       (not just .app/) processes.

  scripts/cfw_install_exp.sh           build_libvcamcaptured() helper
                                       (clang + CoreMedia/CoreVideo/
                                       Foundation, ldid-signed) and
                                       new [JB-4.1] section that
                                       scp's the dylib + plist into
                                       procursus/Library/MobileSubstrate/
                                       DynamicLibraries.

Pairs with the host vphone-cli camera server + vphoned vcam vsock
listener already in this branch.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
zqxwce
2026-06-20 01:12:16 +03:00
committed by zqxwce
co-authored by Claude Opus 4.7
parent c8de9c9cb7
commit 9b4e25569b
6 changed files with 3109 additions and 6 deletions
+80 -4
View File
@@ -87,20 +87,43 @@ elif [[ ! -f "$JB_SYSOS_DMG" ]]; then
fi
if [[ -f "$JB_SYSOS_DMG" ]]; then
# Mount, patch chunks, unmount. Idempotent: re-running on an already
# patched DMG is a no-op (patcher detects already-patched form).
echo "[*] hv_vmm DSC patch: mounting cached SystemOS DMG..."
# Mount, patch chunks (hv_vmm + camera, same cryptex), unmount.
# Idempotent: re-running on an already-patched DMG is a no-op
# (hv_vmm patcher detects already-patched cstrings; camera patcher
# refuses pre-patched function entries unless --force is passed).
echo "[*] DSC patches: mounting cached SystemOS DMG..."
mkdir -p "$JB_MNT_SYSOS"
sudo hdiutil detach "$JB_MNT_SYSOS" -force 2>/dev/null || true
sudo hdiutil attach -mountpoint "$JB_MNT_SYSOS" "$JB_SYSOS_DMG" -nobrowse -owners off
JB_DSC_CHUNKS_DIR="$JB_MNT_SYSOS/System/Library/Caches/com.apple.dyld"
JB_DSC_HEADER="$JB_DSC_CHUNKS_DIR/dyld_shared_cache_arm64e"
if [[ -d "$JB_DSC_CHUNKS_DIR" ]]; then
echo "[*] hv_vmm DSC patch: patching chunks under $JB_DSC_CHUNKS_DIR..."
"$SCRIPT_DIR/patch_hv_vmm_userland.sh" dsc "$JB_DSC_CHUNKS_DIR"
echo "[+] hv_vmm DSC patch: chunks patched"
# Camera DSC patches make `cameracaptured` return a single
# synthetic `vphone-cam` device and stub out the AVFoundation
# init-time validation that would otherwise crash on it. With
# these + the /product/camera DT node (added at fw_patch time),
# Camera.app's icon shows on Home Screen and the app launches
# without immediately bailing. Patch #6 (the BL-site sanity
# check inside _captureSourceServer_handleCopySourcesMessage)
# is 26.5-build-specific; the patcher aborts cleanly on any
# other build, leaving hv_vmm's changes intact.
if [[ -f "$JB_DSC_HEADER" ]]; then
echo "[*] camera DSC patch: patching chunks under $JB_DSC_CHUNKS_DIR..."
if "$SCRIPT_DIR/patch_camera_userland.sh" dsc "$JB_DSC_CHUNKS_DIR" "$JB_DSC_HEADER"; then
echo "[+] camera DSC patch: chunks patched"
else
echo "[!] camera DSC patch: failed (likely build-version mismatch); continuing"
fi
else
echo "[-] camera DSC patch: $JB_DSC_HEADER not found, skipping"
fi
else
echo "[-] hv_vmm DSC patch: $JB_DSC_CHUNKS_DIR not found, skipping"
echo "[-] DSC patches: $JB_DSC_CHUNKS_DIR not found, skipping"
fi
sudo hdiutil detach "$JB_MNT_SYSOS" -force
@@ -223,6 +246,35 @@ build_tweakloader() {
echo "$out"
}
# Builds the libvcamcaptured.dylib injected into /usr/libexec/cameracaptured
# via the TweakLoader allowlist. Output goes to TEMP_DIR; caller scp's the
# binary + companion plist to procursus/Library/MobileSubstrate/DynamicLibraries.
build_libvcamcaptured() {
local src="$SCRIPT_DIR/vcamcaptured/libvcamcaptured.m"
local out="$TEMP_DIR/libvcamcaptured.dylib"
local sdk cc
[[ -f "$src" ]] || die "Missing libvcamcaptured source at $src"
sdk="$(xcrun --sdk iphoneos --show-sdk-path)"
cc="$(xcrun --sdk iphoneos -f clang)"
"$cc" -isysroot "$sdk" \
-arch arm64e \
-miphoneos-version-min=15.0 \
-dynamiclib \
-fobjc-arc -Os \
-install_name /var/jb/usr/lib/libvcamcaptured.dylib \
-framework CoreMedia \
-framework CoreVideo \
-framework Foundation \
-o "$out" \
"$src"
ldid_sign "$out"
echo "$out"
}
remote_mount() {
local dev="$1" mnt="$2" opts="${3:-rw}"
ssh_cmd "/bin/mkdir -p $mnt"
@@ -479,6 +531,29 @@ ssh_cmd "/bin/chmod 0755 /mnt5/$BOOT_HASH/$JB_DIR_NAME/procursus/usr/lib/TweakLo
echo " [+] TweakLoader installed to procursus/usr/lib/TweakLoader.dylib"
# ═══════════ JB-4.1 INSTALL libvcamcaptured ═════════════════════════
# Inject dylib loaded into /usr/libexec/cameracaptured (via TweakLoader
# allowlist in TweakLoader.m's kVPhoneAllowedDaemonPaths). Hosts the
# synthetic FigCaptureSource + shm-frame reader paired with vphoned's
# vsock 1338 listener on the guest side.
echo ""
echo "[JB-4.1] Building and installing libvcamcaptured..."
LIBVCAM_OUT="$(build_libvcamcaptured)"
LIBVCAM_DIR="/mnt5/$BOOT_HASH/$JB_DIR_NAME/procursus/Library/MobileSubstrate/DynamicLibraries"
ssh_cmd "/bin/mkdir -p $LIBVCAM_DIR"
scp_to "$LIBVCAM_OUT" "$LIBVCAM_DIR/libvcamcaptured.dylib"
ssh_cmd "/usr/sbin/chown 0:0 $LIBVCAM_DIR/libvcamcaptured.dylib"
ssh_cmd "/bin/chmod 0755 $LIBVCAM_DIR/libvcamcaptured.dylib"
# The plist tells TweakLoader to load the dylib only inside cameracaptured
# (Filter.Executables = ["cameracaptured"]); keep it next to the dylib.
LIBVCAM_PLIST="$SCRIPT_DIR/vcamcaptured/libvcamcaptured.plist"
if [[ -f "$LIBVCAM_PLIST" ]]; then
scp_to "$LIBVCAM_PLIST" "$LIBVCAM_DIR/libvcamcaptured.plist"
ssh_cmd "/bin/chmod 0644 $LIBVCAM_DIR/libvcamcaptured.plist"
fi
echo " [+] libvcamcaptured installed to procursus/Library/MobileSubstrate/DynamicLibraries/"
# ═══════════ JB-5 DEPLOY FIRST-BOOT SETUP ══════════════════════
echo ""
echo "[JB-5] Deploying first-boot setup..."
@@ -609,6 +684,7 @@ fi
echo ""
echo "[*] Unmounting device filesystems..."
ssh_cmd "/sbin/umount /mnt1 2>/dev/null || true"
ssh_cmd "/sbin/umount /mnt2 2>/dev/null || true"
ssh_cmd "/sbin/umount /mnt3 2>/dev/null || true"
ssh_cmd "/sbin/umount /mnt5 2>/dev/null || true"
+21 -2
View File
@@ -44,16 +44,35 @@ static NSString *TLExecutablePath(void) {
return argv0 ?: @"";
}
// Daemons the loader is explicitly allowed to load tweaks into, even though
// they're not under a `.app/` path. Keep this list narrow — every daemon
// added here is a process where a buggy tweak can crash a launchd-managed
// service and destabilize boot.
//
// This list ONLY applies to tweaks WITHOUT Filter.Frameworks. Framework-
// filtered tweaks engage in every process and self-limit based on which
// frameworks are actually loaded — they don't need per-daemon entries here.
static NSString *const kVPhoneAllowedDaemonPaths[] = {
@"/usr/libexec/cameracaptured", // libvcamcaptured (Filter.Executables match)
};
static BOOL TLShouldRunInCurrentProcess(void) {
NSString *execPath = TLExecutablePath();
if (!execPath.length) return NO;
// vphone's hook runtime injects broadly, including launch-critical daemons
// like xpcproxy, logd, notifyd, sshd, shells, and helper tools. Restrict the
// user tweak loader to app binaries only so it does not destabilize boot or
// process launch paths.
// user tweak loader to app binaries only — plus an allowlist of daemons
// that explicitly opt in (see `kVPhoneAllowedDaemonPaths`).
if ([execPath containsString:@".app/"]) return YES;
for (size_t i = 0;
i < sizeof(kVPhoneAllowedDaemonPaths) /
sizeof(kVPhoneAllowedDaemonPaths[0]);
i++) {
if ([execPath isEqualToString:kVPhoneAllowedDaemonPaths[i]]) return YES;
}
return NO;
}
+1
View File
@@ -0,0 +1 @@
*.dylib
+23
View File
@@ -0,0 +1,23 @@
# libvcamcaptured — dylib loaded into cameracaptured via LC_LOAD_DYLIB
SRCS := $(wildcard *.m)
OUT := libvcamcaptured.dylib
.PHONY: all clean
all: $(OUT)
$(OUT): $(SRCS) $(wildcard *.h)
@echo "=== Building libvcamcaptured (arm64e, iphoneos, dylib) ==="
xcrun -sdk iphoneos clang -arch arm64e -Os -fobjc-arc \
-dynamiclib \
-install_name /var/jb/usr/lib/libvcamcaptured.dylib \
-I. \
-o $@ $(SRCS) \
-framework Foundation \
-framework CoreMedia \
-framework CoreVideo
@echo " built OK ($$(stat -f%z $(OUT)) bytes)"
clean:
rm -f $(OUT)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Filter</key>
<dict>
<key>Executables</key>
<array>
<string>cameracaptured</string>
</array>
</dict>
</dict>
</plist>