From 4eac803699d2421325f6733059baf140df0bce5d Mon Sep 17 00:00:00 2001 From: hailin Date: Mon, 6 Apr 2026 05:35:07 -0700 Subject: [PATCH] fix: v8 bridge - attach after voice chat open, remove open_voice from relay Root cause: Frida crashed because open_voice triggered UI transition while hook was active. Now: open voice chat first (one-shot script), then attach bridge when libantaudio.so is already loaded. Co-Authored-By: Claude Opus 4.6 (1M context) --- antaf/open_voice.js | 18 ++++ antaf/voice_bridge_v8.js | 181 +++++++++++++++++++++++++++++++++++++++ relay.py | 38 ++++---- start_voice_bridge.sh | 41 +++++++++ 4 files changed, 256 insertions(+), 22 deletions(-) create mode 100644 antaf/open_voice.js create mode 100644 antaf/voice_bridge_v8.js create mode 100644 start_voice_bridge.sh diff --git a/antaf/open_voice.js b/antaf/open_voice.js new file mode 100644 index 0000000..26beb38 --- /dev/null +++ b/antaf/open_voice.js @@ -0,0 +1,18 @@ +// open_voice.js — One-shot: open voice chat fragment then exit +// Usage: frida -U -p PID -l open_voice.js --no-pause +Java.perform(function() { + Java.scheduleOnMainThread(function() { + try { + Java.choose("com.antgroup.aijk.android.ijklauncher.biz.activity.IJKActivity", { + onMatch: function(a) { + var fm = a.getSupportFragmentManager(); + var f = Java.use("com.antgroup.aijk.android.ijkchat.biz.voicechat.IjkVoiceChatFragment").$new(); + f.show(fm, "v"); + console.log("[OK] Voice chat opened"); + }, onComplete: function() {} + }); + } catch(e) { + console.log("[ERR] " + e); + } + }); +}); diff --git a/antaf/voice_bridge_v8.js b/antaf/voice_bridge_v8.js new file mode 100644 index 0000000..08185db --- /dev/null +++ b/antaf/voice_bridge_v8.js @@ -0,0 +1,181 @@ +// voice_bridge_v8.js — Voice Bridge with Audio Injection (attach after voice chat opened) +// +// STARTUP ORDER: +// 1. Launch app: adb shell monkey -p com.antgroup.aijk.android ... +// 2. Open voice chat manually or via adb tap +// 3. Wait for libantaudio.so to load +// 4. Attach frida with this script +// +// Hook point: libantaudio.so MFAntAudio3AV2Filter::process(micIn, spkRef, out, size, &result) +// TCP :18901 +// Frame: 4-byte len + 1-byte type + payload +// type 0: speaker/AI audio (spkRef, downstream to client) +// type 1: text/JSON command +// type 2: mic audio (micIn, downstream to client) +// type 3: inject audio (upstream from client, replaces micIn) + +var voiceActive = false; +var clientOS = null; +var capturedSpk = 0, capturedMic = 0, spkBytes = 0, micBytes = 0; +var injectMode = false; +var injectQueue = []; + +function wf(os, type, jArr) { + try { + var len = jArr.length; + var h = Java.array("byte", [(len>>24)&0xFF,(len>>16)&0xFF,(len>>8)&0xFF,len&0xFF, type]); + os.write(h); os.write(jArr); os.flush(); + } catch(e) {} +} +function wt(os, text) { + wf(os, 1, Java.use("java.lang.String").$new(text).getBytes("UTF-8")); +} + +// === Hook libantaudio.so (should already be loaded) === +var hooked = false; +function tryHook() { + if (hooked) return; + var m = Process.findModuleByName("libantaudio.so"); + if (!m) return; + var addr = m.findExportByName("_ZN8antaudio20MFAntAudio3AV2Filter7processEPhS1_S1_iRi"); + if (!addr) return; + hooked = true; + + Interceptor.attach(addr, { + onEnter: function(args) { + if (!voiceActive || !clientOS) return; + var size = args[4].toInt32(); + if (size <= 0) return; + + try { + if (injectMode) { + if (injectQueue.length > 0) { + var frame = injectQueue.shift(); + if (frame.byteLength === size) { + args[1].writeByteArray(frame); + } else { + var buf = new ArrayBuffer(size); + var dst = new Uint8Array(buf); + var src = new Uint8Array(frame); + var copyLen = Math.min(size, frame.byteLength); + for (var k = 0; k < copyLen; k++) dst[k] = src[k]; + args[1].writeByteArray(buf); + } + } else { + var silence = new ArrayBuffer(size); + args[1].writeByteArray(silence); + } + } + + // Always capture speaker/AI output (type 0) + var spkPcm = args[2].readByteArray(size); + var spkArr = Java.array("byte", Array.from(new Uint8Array(spkPcm))); + wf(clientOS, 0, spkArr); + capturedSpk++; spkBytes += size; + + if (!injectMode) { + var micPcm = args[1].readByteArray(size); + var micArr = Java.array("byte", Array.from(new Uint8Array(micPcm))); + wf(clientOS, 2, micArr); + } + capturedMic++; micBytes += size; + + if (capturedMic <= 3 || capturedMic % 500 === 0) + console.log("[VOICE] mic=" + capturedMic + " spk=" + capturedSpk + " inject=" + injectQueue.length); + } catch(e) {} + } + }); + console.log("[VOICE] process hooked @ " + addr); +} + +// Hook immediately — lib should already be loaded since voice chat is open +tryHook(); +if (!hooked) { + // Retry a few times in case of timing + [500, 1000, 2000, 5000].forEach(function(ms) { setTimeout(tryHook, ms); }); +} + +// === TCP Server === +Java.perform(function() { + var SS = Java.use("java.net.ServerSocket"); + var JS = Java.use("java.lang.String"); + var server = SS.$new(18901); + console.log("[VOICE] Listening :18901"); + + var Srv = Java.registerClass({ + name: "com.antaf.voice.S8", + implements: [Java.use("java.lang.Runnable")], + methods: { + run: function() { + while (true) { + try { + console.log("[VOICE] Waiting for client..."); + var c = server.accept(); + var is = c.getInputStream(); + var os = c.getOutputStream(); + clientOS = os; + console.log("[VOICE] Client connected"); + wt(os, JSON.stringify({ + event:"connected", protocol:"antaf-voice-v8", + hooked: hooked, + commands:["start","stop","status","inject_on","inject_off"], + audio:"pcm-16bit-960b-frames", + frameTypes:{0:"spk_ai",1:"text",2:"mic",3:"inject"} + })); + + while (true) { + var hb = []; + for (var i=0;i<5;i++) { var b=is.read(); if(b<0) throw "EOF"; hb.push(b); } + var fl=(hb[0]<<24)|(hb[1]<<16)|(hb[2]<<8)|hb[3], ft=hb[4]; + if (fl>1048576) break; + var pb = []; + for (var i=0;i/dev/null +sleep 3 + +PID=$(adb shell ps -A | grep 'com.antgroup.aijk.android$' | awk '{print $2}') +if [ -z "$PID" ]; then echo "ERROR: App not running"; exit 1; fi +echo " PID=$PID" + +echo "[2] Opening voice chat..." +timeout 8 $FRIDA -U -p $PID -l $SCRIPT_DIR/open_voice.js --no-pause -q 2>/dev/null || true +sleep 3 + +echo "[3] Waiting for libantaudio.so..." +sleep 2 + +echo "[4] Attaching voice_bridge_v8..." +pkill -f frida 2>/dev/null || true +sleep 1 +adb forward tcp:18901 tcp:18901 +nohup $FRIDA -U -p $PID -l $SCRIPT_DIR/voice_bridge_v8.js > /tmp/frida_voice.log 2>&1 & +sleep 5 +tail -5 /tmp/frida_voice.log + +echo "" +echo "=== Voice bridge ready on :18901 ==="