/** * Frida Daemon — 注入微信进程,读取队列文件并发送消息 * * 运行方式(Mac 端): * frida -U -p $(adb shell "su -c 'pgrep -x com.tencent.mm'") -l frida_daemon.js * * 队列文件:/data/data/com.termux/files/home/hermes-wechat/.frida_queue/queue.jsonl * 格式:每行一个 JSON {"talker":"wxid","content":"msg","id":"msg_xxx"} */ // 统一放在 Termux 可访问目录,避免 /data/local/tmp 权限问题 var QUEUE_FILE = "/data/data/com.termux/files/home/hermes-wechat/.frida_queue/queue.jsonl"; var LOG_TAG = "[HermesDaemon]"; function log(msg) { console.log(LOG_TAG + " " + msg); } // ── Send message via WeChat internal API ── function sendMessage(talker, content) { log("Sending to " + talker + ": " + content.substring(0, 40)); Java.perform(function () { Java.scheduleOnMainThread(function () { try { var r1 = Java.use("u01.r1"); var builder = r1.a(talker); log("Builder class: " + builder.getClass().getName()); var q1 = Java.use("u01.q1"); q1.e.call(builder, content); q1.h.call(builder, 0); log("Content set, type=0"); var m1 = q1.a.call(builder); log("Built m1 class: " + m1.getClass().getName()); var m1cls = Java.use("u01.m1"); var ret = m1cls.a.call(m1); log("Submit result: " + ret); } catch (e) { log("Send FAILED: " + e); } }); }); } // ── Read and process queue file ── function processQueue() { try { var File = Java.use("java.io.File"); var FileInputStream = Java.use("java.io.FileInputStream"); var BufferedReader = Java.use("java.io.BufferedReader"); var InputStreamReader = Java.use("java.io.InputStreamReader"); var FileWriter = Java.use("java.io.FileWriter"); var file = File.$new(QUEUE_FILE); if (!file.exists()) { return; } var fis = FileInputStream.$new(file); var reader = BufferedReader.$new(InputStreamReader.$new(fis)); var lines = []; var line; while ((line = reader.readLine()) !== null) { lines.push(line); } reader.close(); fis.close(); if (lines.length === 0) { return; } log("Processing " + lines.length + " queued messages"); var successCount = 0; for (var i = 0; i < lines.length; i++) { try { var msg = JSON.parse(lines[i]); if (msg.talker && msg.content) { sendMessage(msg.talker, msg.content); successCount++; } } catch (e) { log("Parse error: " + e); } } // Clear queue after processing var fw = FileWriter.$new(file, false); fw.write(""); fw.close(); log("Processed " + successCount + "/" + lines.length + " messages, queue cleared"); } catch (e) { log("Queue processing error: " + e); } } // ── Main loop ── Java.perform(function () { log("Daemon injected into WeChat process"); log("Watching queue: " + QUEUE_FILE); log("Send API: u01.r1.a(talker) -> q1.e(content) -> q1.h(0) -> q1.a() -> m1.a()"); // Process queue every 500ms setInterval(processQueue, 500); // Also expose manual trigger via rpc rpc.exports = { send: function(talker, content) { sendMessage(talker, content); return "queued"; }, process: function() { processQueue(); return "done"; } }; log("Daemon ready. RPC exports: send(talker, content), process()"); });