'use strict'; // ============================================================================ // bypass_all.js — 虎牙 com.duowan.kiwi (13.4.22) 反 frida 绕过 · 一体化脚本 // ============================================================================ // 合并自 RE 仓库三脚本 (实测配方, R31-R39 全程验证): // 1. bypass_msaoaid_maps_art_callsite.js — libmsaoaidsec 线程名/fd 掩盖 + maps 清理分支跳转 + ART 完整性 callsite NOP // 2. mask_frida_maps_only.js — libc 层 /proc/self/maps 内容 'frida-agent' 掩写 // 3. patch_guard_block_termination.js — kill/tgkill/exit/abort 自终止拦截 + msaoaid crash-guard 补丁 // // 用法 (必须 spawn 挂起中、resume 之前加载!): // const opts = { patchGuardDelayMs: 3000 }; // B 层延迟, 传参可调 // const src = 'globalThis.BYPASS_OPTS=' + JSON.stringify(opts) + ';\n' + bypassSrc; // s.create_script(src).load(); // 然后 d.resume(pid) // // 时序语义: // A 层 (立即): maps 线索掩盖 — 必须 resume 前就位, 否则启动检测线程先读到 frida 痕迹 // B 层 (延迟): 终止拦截会无差别吞掉 exit/abort, 过早加载会卡死启动期合法退出 // → 默认 3000ms 后安装; 若 App 秒死调小, 若启动期被杀调大 // ============================================================================ var OPTS = { patchGuardDelayMs: 3000, persist: false }; try { if (globalThis.BYPASS_OPTS) OPTS = Object.assign(OPTS, globalThis.BYPASS_OPTS); } catch (_) {} function emit(o) { // 关键: 钩子上下文 (dlopen/拦截器 onEnter, 主线程启动期) 内直接 send() 会死锁主线程 // (R40 ANR trace: main 阻塞在 frida-agent) — 一律入队, setImmediate 刷出 try { pendingEmits.push(Object.assign({ src: 'bypass_all' }, o)); setImmediate(flushEmits); } catch (_) {} } var pendingEmits = []; function flushEmits() { while (pendingEmits.length) { try { send(pendingEmits.shift()); } catch (_) {} } } // ---------- 持久化调试日志 (默认关) ---------- var logFile = null; function persist(row) { if (!OPTS.persist) return; try { if (logFile === null) logFile = new File('/data/user/0/com.duowan.kiwi/cache/bypass-all-' + Process.id + '.jsonl', 'a'); row.tid = Process.getCurrentThreadId(); logFile.write(JSON.stringify(row) + '\n'); logFile.flush(); } catch (_) {} } function exportOf(name) { try { if (typeof Module.getGlobalExportByName === 'function') return Module.getGlobalExportByName(name); } catch (_) {} try { if (typeof Module.findGlobalExportByName === 'function') return Module.findGlobalExportByName(null, name); } catch (_) {} try { return Module.findExportByName(null, name); } catch (_) { return null; } } // ============================================================================ // A1) libmsaoaidsec.so: 线程名/fd 掩盖 + predicate 分支跳转 + ART callsite NOP // (原 bypass_msaoaid_maps_art_callsite.js, offsets 对应 13.4.22 arm64) // ============================================================================ var msaoaid = { installed: false }; function readStr(pointer, length) { try { return pointer.readCString(length) || ''; } catch (_) { return ''; } } function installMsaoaid(source) { if (msaoaid.installed) return; var module = Process.findModuleByName('libmsaoaidsec.so'); if (module === null) return; msaoaid.installed = true; var branch = module.base.add(0x1c4a0); var continuePath = module.base.add(0x1c4b4); var artCallsite = module.base.add(0x1ceec); persist({ event: 'installed', source: source, base: module.base.toString() }); emit({ event: 'A-msaoaid-patched', base: module.base.toString() }); // 线程名掩盖: /proc//cmdline 读取处的 gmain/gum-js-loop/linjector → pool-worker [0x1c0e4, 0x1c0f4].forEach(function (offset) { Interceptor.attach(module.base.add(offset), { onEnter() { var buffer = this.context.sp.add(0x18); var value = readStr(buffer, 96); if (!value.startsWith('Name:\tgmain') && !value.startsWith('Name:\tgum-js-loop') && !value.startsWith('Name:\tlinjector')) return; buffer.writeUtf8String('Name:\tpool-worker'); persist({ event: 'name-masked', offset: '0x' + offset.toString(16) }); }, }); }); // fd 扫描掩盖: 'linjector' → 'xxxxxxxxx' Interceptor.attach(module.base.add(0x1c214), { onEnter() { var buffer = this.context.sp.add(0x288); var value = readStr(buffer, 512); var index = value.indexOf('linjector'); if (index < 0) return; buffer.add(index).writeUtf8String('xxxxxxxxx'); persist({ event: 'fd-masked' }); }, }); // predicate 分支: 检出 frida 线索后的清理分支 → 直接跳继续路径; ART 完整性周期 callsite → NOP try { Memory.patchCode(branch, 4, function (code) { var w = new Arm64Writer(code, { pc: branch }); w.putBranchAddress(continuePath); w.flush(); }); Memory.patchCode(artCallsite, 4, function (code) { var w = new Arm64Writer(code, { pc: artCallsite }); w.putNop(); w.flush(); }); persist({ event: 'branches-patched' }); } catch (error) { emit({ event: 'A-patch-error', error: String(error) }); } } // msaoaid 可能在 spawn 时已加载, 也可能稍后 dlopen — 双路触发 ['dlopen', 'android_dlopen_ext'].forEach(function (name) { var address = exportOf(name); if (address !== null) Interceptor.attach(address, { onEnter() { installMsaoaid(name + ':enter'); }, onLeave() { installMsaoaid(name + ':leave'); }, }); }); installMsaoaid('initial'); // ============================================================================ // A2) libc 层 maps 内容掩盖: open*/fopen 记录 maps fd → read/pread64 时掩写 frida-agent // (原 mask_frida_maps_only.js) // ============================================================================ var trackedFds = {}; function trackOpen(pathPointer, fd) { var path = ''; try { path = pathPointer.isNull() ? '' : pathPointer.readCString(); } catch (_) { return; } if (fd >= 0 && /(?:^|\/)maps$/.test(path)) trackedFds[fd] = path; } ['open', 'open64', 'fopen'].forEach(function (name) { var address = exportOf(name); if (address === null) return; Interceptor.attach(address, { onEnter(args) { this.path = args[0]; }, onLeave(retval) { trackOpen(this.path, retval.toInt32()); }, }); }); var openat = exportOf('openat'); if (openat !== null) Interceptor.attach(openat, { onEnter(args) { this.path = args[1]; }, onLeave(retval) { trackOpen(this.path, retval.toInt32()); }, }); var closeFn = exportOf('close'); if (closeFn !== null) Interceptor.attach(closeFn, { onEnter(args) { delete trackedFds[args[0].toInt32()]; }, }); function maskMapsRead(buffer, length, path) { if (!path || length <= 0) return; try { var bytes = new Uint8Array(buffer.readByteArray(length)); var value = ''; for (var i = 0; i < bytes.length; i++) value += String.fromCharCode(bytes[i]); if (value.indexOf('frida-agent') < 0 && value.indexOf('gum-js-loop') < 0 && value.indexOf('linjector') < 0) return; value = value.replace(/frida-agent/gi, 'xxxxxxxxxxx') .replace(/gum-js-loop/g, 'pool-worker') .replace(/linjector/g, 'xxxxxxxxx'); for (var j = 0; j < length; j++) buffer.add(j).writeU8(value.charCodeAt(j) || 0); } catch (_) {} } ['read', 'pread64'].forEach(function (name) { var address = exportOf(name); if (address === null) return; Interceptor.attach(address, { onEnter(args) { this.buffer = args[1]; this.path = trackedFds[args[0].toInt32()] || ''; }, onLeave(retval) { maskMapsRead(this.buffer, retval.toInt32(), this.path); }, }); }); emit({ event: 'A-maps-mask-installed' }); // ============================================================================ // B 层 (延迟 OPTS.patchGuardDelayMs): 自终止拦截 + msaoaid crash-guard 补丁 // (原 patch_guard_block_termination.js) // ============================================================================ function installPatchGuard() { var keep = []; var ownPid = Process.id; function replaceFn(name, returnType, argumentTypes, callback) { var address = exportOf(name); if (address === null) return; try { var native = new NativeCallback(callback, returnType, argumentTypes); keep.push(native); Interceptor.replace(address, native); } catch (_) {} } replaceFn('kill', 'int', ['int', 'int'], function (pid) { return Number(pid) === ownPid ? 0 : -1; }); replaceFn('tgkill', 'int', ['int', 'int', 'int'], function (tgid, tid) { return (Number(tgid) === ownPid || Number(tid) === ownPid) ? 0 : -1; }); replaceFn('exit', 'void', ['int'], function () {}); replaceFn('_exit', 'void', ['int'], function () {}); replaceFn('_Exit', 'void', ['int'], function () {}); replaceFn('abort', 'void', [], function () {}); replaceFn('raise', 'int', ['int'], function () { return 0; }); // msaoaid crash-guard: 属性读取触发点后补丁 0x20ca8 (x0 → 0) var guardPatched = false; function patchGuard(module) { if (guardPatched) return; var address = module.base.add(0x20ca8); try { Memory.protect(address, 4, 'rwx'); var writer = new Arm64Writer(address); writer.putMovRegReg('x0', 'xzr'); writer.flush(); writer.dispose(); guardPatched = true; emit({ event: 'B-guard-patched' }); } catch (error) { emit({ event: 'B-guard-error', error: String(error) }); } } var prop = exportOf('__system_property_get'); if (prop !== null) Interceptor.attach(prop, { onEnter(args) { var name = ''; try { name = args[0].readCString(); } catch (_) { return; } if (name !== 'ro.build.version.sdk') return; var module = Process.findModuleByName('libmsaoaidsec.so'); if (module !== null) patchGuard(module); }, }); emit({ event: 'B-patch-guard-installed', delayMs: OPTS.patchGuardDelayMs }); } setTimeout(installPatchGuard, OPTS.patchGuardDelayMs); emit({ event: 'armed', pid: Process.id, patchGuardDelayMs: OPTS.patchGuardDelayMs });