#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 精准Hook方式找到微信8.0.56的真实消息发送类 通过监听实际发送行为来捕获正确的类名和方法 """ import frida, time, json from datetime import datetime PHONE_IP = "192.168.110.80" FRIDA_PORT = 27042 WECHAT_PID = 9462 BASE = "/Users/karuo/Documents/开发/2、私域银行/工作手机" RESULT_FILE = BASE + "/hook_find_send_20260518.json" # 这个脚本通过Hook SQLite的insert操作来捕获消息发送 # 当微信发送消息时,会往message表insert一条记录 HOOK_SCRIPT = """ 'use strict'; var _foundClasses = []; var _messages = []; // 方法1: Hook SQLite insert,捕获消息写入 function hookSQLiteInsert() { try { var SQLiteDatabase = Java.use('net.sqlcipher.database.SQLiteDatabase'); SQLiteDatabase.insert.overload('java.lang.String', 'java.lang.String', 'android.content.ContentValues').implementation = function(table, nullColumnHack, values) { if (table === 'message') { var content = values.getAsString('content'); var talker = values.getAsString('talker'); var isSend = values.getAsInteger('isSend'); if (isSend && parseInt(isSend) === 1) { _messages.push({ table: table, talker: talker, content: content ? content.substring(0, 100) : '', timestamp: Date.now() }); send({ type: 'message_insert', talker: talker, content: content ? content.substring(0, 50) : '' }); } } return this.insert(table, nullColumnHack, values); }; send({ type: 'hook_status', msg: 'SQLite insert hook OK' }); } catch(e) { send({ type: 'hook_status', msg: 'SQLite insert hook failed: ' + e }); } } // 方法2: 检查候选类是否存在(不扫描全量) function checkCandidateClasses() { var candidates = [ "com.tencent.mm.modelmulti.h", "com.tencent.mm.modelmulti.g", "com.tencent.mm.modelmulti.f", "com.tencent.mm.modelmulti.e", "com.tencent.mm.modelmulti.d", "com.tencent.mm.modelmulti.c", "com.tencent.mm.modelmulti.b", "com.tencent.mm.modelmulti.a", "com.tencent.mm.plugin.messenger.foundation.a.k", "com.tencent.mm.plugin.messenger.foundation.a.j", "com.tencent.mm.plugin.messenger.foundation.a.i", "com.tencent.mm.plugin.messenger.foundation.a.h", "com.tencent.mm.plugin.messenger.foundation.a.g", "com.tencent.mm.plugin.messenger.foundation.a.f", "com.tencent.mm.plugin.messenger.foundation.a.e", "com.tencent.mm.plugin.messenger.foundation.a.d", "com.tencent.mm.plugin.messenger.foundation.a.c", "com.tencent.mm.plugin.messenger.foundation.a.b", "com.tencent.mm.plugin.messenger.foundation.a.a", ]; var results = {}; for (var i = 0; i < candidates.length; i++) { try { var cls = Java.use(candidates[i]); results[candidates[i]] = 'exists'; // 获取方法列表 var methods = cls.class.getDeclaredMethods(); var methodList = []; for (var j = 0; j < methods.length; j++) { var m = methods[j]; var params = m.getParameterTypes().map(function(p) { return p.getName(); }); methodList.push(m.getName() + '(' + params.join(',') + ')'); } results[candidates[i] + '_methods'] = methodList; } catch(e) { results[candidates[i]] = 'not_found'; } } return results; } // 方法3: 通过AccessibilityService方式发送(UIAutomator) function sendViaADB(toId, content) { try { // 用am start打开聊天界面 var Runtime = Java.use('java.lang.Runtime'); var cmd = 'am start -n com.tencent.mm/.ui.LauncherUI --es username ' + toId; var proc = Runtime.getRuntime().exec(['sh', '-c', cmd]); proc.waitFor(); return { success: true, method: 'am_start', cmd: cmd }; } catch(e) { return { success: false, error: String(e) }; } } // 方法4: 通过ClipboardManager+UIAutomator发送 function sendViaClipboard(toId, content) { try { return Java.performNow(function() { var ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext(); var ClipboardManager = Java.use('android.content.ClipboardManager'); var ClipData = Java.use('android.content.ClipData'); var cm = Java.cast(ctx.getSystemService('clipboard'), ClipboardManager); var clip = ClipData.newPlainText('msg', content); cm.setPrimaryClip(clip); return { success: true, method: 'clipboard', content: content }; }); } catch(e) { return { success: false, error: String(e) }; } } Java.perform(function() { hookSQLiteInsert(); }); rpc.exports = { checkCandidates: function() { return Java.performNow(function() { return checkCandidateClasses(); }); }, getMessages: function() { return { messages: _messages, count: _messages.length }; }, sendViaClipboard: function(params) { return sendViaClipboard(params.to_id, params.content); }, // 尝试通过微信的ShareToTimeline方式发朋友圈 testSnsPost: function(content) { try { return Java.performNow(function() { var ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext(); var Intent = Java.use('android.content.Intent'); // 方式1: 通过SnsUploadUI var intent1 = Intent.$new(); intent1.setClassName('com.tencent.mm', 'com.tencent.mm.plugin.sns.ui.SnsUploadUI'); intent1.putExtra('Ksnsupload_type', 1); intent1.putExtra('Ksnsupload_content', content); intent1.addFlags(0x10000000); // FLAG_ACTIVITY_NEW_TASK ctx.startActivity(intent1); return { success: true, method: 'SnsUploadUI_intent', content: content }; }); } catch(e) { return { success: false, error: String(e) }; } }, // 直接写入message表(绕过发送逻辑) insertMessage: function(params) { try { return Java.performNow(function() { var toId = params.to_id; var content = params.content; // 找DB路径 var File = Java.use('java.io.File'); var base = File.$new('/data/data/com.tencent.mm/MicroMsg/'); var dirs = base.listFiles(); var dbPath = null; if (dirs) { for (var i = 0; i < dirs.length; i++) { if (dirs[i].isDirectory() && dirs[i].getName().length === 32) { var candidate = dirs[i].getAbsolutePath() + '/EnMicroMsg.db'; if (File.$new(candidate).exists()) { dbPath = candidate; break; } } } } if (!dbPath) return { success: false, error: 'DB not found' }; var SQLiteDatabase = Java.use('net.sqlcipher.database.SQLiteDatabase'); var db = SQLiteDatabase.openDatabase(dbPath, '00000000000000000000000000000000', null, 0); var ContentValues = Java.use('android.content.ContentValues'); var cv = ContentValues.$new(); cv.put('msgId', Java.use('java.lang.Long').$new(Date.now())); cv.put('type', Java.use('java.lang.Integer').$new(1)); cv.put('isSend', Java.use('java.lang.Integer').$new(1)); cv.put('talker', toId); cv.put('content', content); cv.put('createTime', Java.use('java.lang.Long').$new(Date.now())); cv.put('status', Java.use('java.lang.Integer').$new(2)); var rowId = db.insert('message', null, cv); db.close(); return { success: rowId > 0, row_id: rowId.toString(), method: 'direct_db_insert' }; }); } catch(e) { return { success: false, error: String(e) }; } } }; """ print(f"[{datetime.now().strftime('%H:%M:%S')}] 连接Frida...") dm = frida.get_device_manager() device = dm.add_remote_device(f"{PHONE_IP}:{FRIDA_PORT}") session = device.attach(WECHAT_PID) print(f"[OK] 附加微信 PID={WECHAT_PID}") messages_received = [] def on_message(m, d): if m.get("type") == "send": payload = m.get("payload", {}) print(f" [EVENT] {payload}") messages_received.append(payload) script = session.create_script(HOOK_SCRIPT) script.on("message", on_message) script.load() print("[OK] Hook脚本已加载,等待2秒...") time.sleep(2) rpc = script.exports_sync results = {} # 1. 检查候选类 print("\n=== 检查候选类 ===") candidates_result = rpc.check_candidates() for cls, status in candidates_result.items(): if not cls.endswith('_methods'): print(f" {status.upper()} {cls}") if status == 'exists' and (cls + '_methods') in candidates_result: methods = candidates_result[cls + '_methods'] print(f" 方法: {methods[:5]}") results["candidates"] = candidates_result # 2. 测试直接DB插入 print("\n=== 测试直接DB插入消息 ===") insert_result = rpc.insert_message({"to_id": "filehelper", "content": f"[DB直接插入测试{datetime.now().strftime('%H:%M:%S')}]"}) print(f" 结果: {json.dumps(insert_result, ensure_ascii=False)}") results["db_insert"] = insert_result time.sleep(1) # 3. 截图验证DB插入是否在界面显示 import subprocess subprocess.run(["adb", "-s", "192.168.110.80:5555", "shell", "screencap", "-p", "/sdcard/db_insert_test.png"]) subprocess.run(["adb", "-s", "192.168.110.80:5555", "pull", "/sdcard/db_insert_test.png", BASE + "/开发文档/6、测试/live_verify_20260518/screenshots/db_insert_test.png"]) print(" 截图已保存") # 4. 检查消息是否在DB中 print("\n=== 验证DB中的消息 ===") # 用主Hook脚本查询(需要重新attach) print(" (通过rawSql查询最新消息)") # 5. 保存结果 with open(RESULT_FILE, "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2, default=str) print(f"\n结果已保存: {RESULT_FILE}") script.unload() session.detach() print("完成!")