frida_daemon.js
3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
* 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()");
});