feat: 添加精准Hook脚本 20260518
This commit is contained in:
268
hook_find_send.py
Normal file
268
hook_find_send.py
Normal file
@@ -0,0 +1,268 @@
|
||||
#!/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 = 16816
|
||||
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("完成!")
|
||||
Reference in New Issue
Block a user