163 lines
6.4 KiB
Python
163 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
工作手机SDK - 深度数据调试脚本
|
||
目标:查明联系人/群组真实数量,验证SQL过滤条件
|
||
"""
|
||
import frida, time, json, os
|
||
from datetime import datetime
|
||
|
||
PHONE_IP = "192.168.110.80"
|
||
FRIDA_PORT = 27042
|
||
WECHAT_PID = 16816
|
||
BASE = "/Users/karuo/Documents/开发/2、私域银行/工作手机"
|
||
RESULT_FILE = BASE + "/debug_data_20260518.json"
|
||
|
||
# 内联调试脚本 - 直接执行原始SQL
|
||
DEBUG_SCRIPT = """
|
||
'use strict';
|
||
|
||
// 复用 _execSQL 逻辑
|
||
function findDbPath() {
|
||
var paths = [
|
||
'/data/data/com.tencent.mm/MicroMsg/',
|
||
];
|
||
var dbPath = null;
|
||
try {
|
||
var File = Java.use('java.io.File');
|
||
var base = File.$new('/data/data/com.tencent.mm/MicroMsg/');
|
||
var dirs = base.listFiles();
|
||
if (dirs) {
|
||
for (var i = 0; i < dirs.length; i++) {
|
||
var d = dirs[i];
|
||
if (d.isDirectory() && d.getName().length === 32) {
|
||
var candidate = d.getAbsolutePath() + '/EnMicroMsg.db';
|
||
if (File.$new(candidate).exists()) {
|
||
dbPath = candidate;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch(e) {}
|
||
return dbPath;
|
||
}
|
||
|
||
function execSQL(sql, dbPath) {
|
||
try {
|
||
var SQLiteDatabase = Java.use('net.sqlcipher.database.SQLiteDatabase');
|
||
var db = SQLiteDatabase.openDatabase(dbPath, '00000000000000000000000000000000', null, 0);
|
||
var cursor = db.rawQuery(sql, null);
|
||
var rows = [];
|
||
var colCount = cursor.getColumnCount();
|
||
var colNames = [];
|
||
for (var i = 0; i < colCount; i++) colNames.push(cursor.getColumnName(i));
|
||
while (cursor.moveToNext()) {
|
||
var row = {};
|
||
for (var j = 0; j < colCount; j++) {
|
||
try { row[colNames[j]] = cursor.getString(j); } catch(_) { row[colNames[j]] = null; }
|
||
}
|
||
rows.push(row);
|
||
}
|
||
cursor.close();
|
||
db.close();
|
||
return rows;
|
||
} catch(e) {
|
||
return [{error: String(e)}];
|
||
}
|
||
}
|
||
|
||
rpc.exports = {
|
||
debugContacts: function() {
|
||
return Java.performNow(function() {
|
||
var dbPath = findDbPath();
|
||
if (!dbPath) return {error: 'DB not found'};
|
||
|
||
var results = {};
|
||
|
||
// 1. 总联系人数(无过滤)
|
||
var r1 = execSQL("SELECT COUNT(*) as cnt FROM rcontact", dbPath);
|
||
results.total_rcontact = r1[0] ? r1[0].cnt : 'err';
|
||
|
||
// 2. type分布
|
||
var r2 = execSQL("SELECT type, COUNT(*) as cnt FROM rcontact GROUP BY type ORDER BY cnt DESC LIMIT 20", dbPath);
|
||
results.type_distribution = r2;
|
||
|
||
// 3. verifyFlag分布
|
||
var r3 = execSQL("SELECT verifyFlag, COUNT(*) as cnt FROM rcontact GROUP BY verifyFlag ORDER BY cnt DESC", dbPath);
|
||
results.verifyflag_distribution = r3;
|
||
|
||
// 4. 真实好友(type=3是好友)
|
||
var r4 = execSQL("SELECT COUNT(*) as cnt FROM rcontact WHERE type=3", dbPath);
|
||
results.type3_friends = r4[0] ? r4[0].cnt : 'err';
|
||
|
||
// 5. 放宽条件的好友
|
||
var r5 = execSQL("SELECT COUNT(*) as cnt FROM rcontact WHERE type NOT IN (0,33) AND username NOT LIKE '%@chatroom' AND username NOT LIKE 'gh_%'", dbPath);
|
||
results.relaxed_friends = r5[0] ? r5[0].cnt : 'err';
|
||
|
||
// 6. 完全无过滤的好友(排除系统账号)
|
||
var r6 = execSQL("SELECT COUNT(*) as cnt FROM rcontact WHERE username NOT LIKE '%@chatroom' AND username NOT LIKE 'gh_%' AND username NOT IN ('filehelper','fmessage','medianote','weibo','qqfriend','qmessage','tmessage','newsapp','brandsessionholder','notification_messages','officialaccounts','revokemsg','gh_792c1a7a1923','voipnotify','voipmessage','systemnotice','weapp','weixinofficialaccounts')", dbPath);
|
||
results.no_filter_friends = r6[0] ? r6[0].cnt : 'err';
|
||
|
||
// 7. 群组数量 - rcontact表
|
||
var r7 = execSQL("SELECT COUNT(*) as cnt FROM rcontact WHERE username LIKE '%@chatroom'", dbPath);
|
||
results.groups_in_rcontact = r7[0] ? r7[0].cnt : 'err';
|
||
|
||
// 8. 群组数量 - chatroom表
|
||
try {
|
||
var r8 = execSQL("SELECT COUNT(*) as cnt FROM chatroom", dbPath);
|
||
results.groups_in_chatroom = r8[0] ? r8[0].cnt : 'err';
|
||
} catch(e) {
|
||
results.groups_in_chatroom = 'table_not_found: ' + String(e);
|
||
}
|
||
|
||
// 9. 前20个联系人(type=3)
|
||
var r9 = execSQL("SELECT username, nickname, conRemark, type, verifyFlag FROM rcontact WHERE type=3 LIMIT 20", dbPath);
|
||
results.sample_type3 = r9;
|
||
|
||
// 10. 前5个群组
|
||
var r10 = execSQL("SELECT username, nickname FROM rcontact WHERE username LIKE '%@chatroom' LIMIT 10", dbPath);
|
||
results.sample_groups = r10;
|
||
|
||
// 11. 检查chatroom表结构
|
||
try {
|
||
var r11 = execSQL("SELECT chatroomname, chatroomnick FROM chatroom LIMIT 10", dbPath);
|
||
results.chatroom_sample = r11;
|
||
} catch(e) {
|
||
results.chatroom_sample = 'error: ' + String(e);
|
||
}
|
||
|
||
// 12. 所有type值的样本
|
||
var r12 = execSQL("SELECT username, nickname, type, verifyFlag FROM rcontact WHERE type NOT IN (0,33) LIMIT 5", dbPath);
|
||
results.sample_non_system = r12;
|
||
|
||
return {success: true, dbPath: dbPath, results: results};
|
||
});
|
||
}
|
||
};
|
||
"""
|
||
|
||
print(f"[{datetime.now().strftime('%H:%M:%S')}] 连接Frida: {PHONE_IP}:{FRIDA_PORT}")
|
||
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}")
|
||
|
||
script = session.create_script(DEBUG_SCRIPT)
|
||
script.on("message", lambda m, d: print(f"[MSG] {m}"))
|
||
script.load()
|
||
print("[OK] 调试脚本已加载,等待2秒...")
|
||
time.sleep(2)
|
||
rpc = script.exports_sync
|
||
|
||
print("\n=== 深度数据调试 ===")
|
||
result = rpc.debug_contacts()
|
||
print(json.dumps(result, ensure_ascii=False, indent=2, default=str))
|
||
|
||
with open(RESULT_FILE, "w", encoding="utf-8") as f:
|
||
json.dump(result, f, ensure_ascii=False, indent=2, default=str)
|
||
print(f"\n已保存: {RESULT_FILE}")
|
||
|
||
script.unload()
|
||
session.detach()
|