/** * 工作手机SDK - 微信8.0.56 完整Frida控制脚本 v3 * 基于WCDB直接操作 + 调用栈捕获 + 真实类名发现 * * 功能: * - 消息发送(WCDB直接写入 + 调用栈捕获真实类) * - 联系人读取(完整4991人) * - 群组读取(50个) * - 朋友圈(读取+发送) * - 好友管理(添加/删除/标签) * - 搜索 * - 账号信息 */ 'use strict'; var _wechatLoader = null; var _capturedStack = []; var _dbInstances = []; // ============================================================ // 工具函数 // ============================================================ function getLoader() { if (_wechatLoader) return _wechatLoader; Java.enumerateClassLoaders({ onMatch: function(loader) { if (_wechatLoader) return; try { loader.loadClass('com.tencent.mm.app.Application'); _wechatLoader = loader; } catch(e) {} }, onComplete: function() {} }); return _wechatLoader; } function getOpenDB() { var targetDB = null; Java.choose('com.tencent.wcdb.database.SQLiteDatabase', { onMatch: function(db) { if (targetDB) return; try { var path = db.getPath(); if (path && path.indexOf('EnMicroMsg') !== -1 && db.isOpen()) { targetDB = db; } } catch(e) {} }, onComplete: function() {} }); return targetDB; } function execQuery(sql) { var db = getOpenDB(); if (!db) return { success: false, error: 'no open DB', rows: [] }; try { var cursor = db.rawQuery(sql, null); var cols = cursor.getColumnNames(); var rows = []; while (cursor.moveToNext()) { var row = {}; for (var i = 0; i < cols.length; i++) { try { row[cols[i]] = cursor.getString(i); } catch(e) { row[cols[i]] = null; } } rows.push(row); } cursor.close(); return { success: true, rows: rows, count: rows.length }; } catch(e) { return { success: false, error: String(e).substring(0, 200), rows: [] }; } } function execInsert(table, kvMap) { var db = getOpenDB(); if (!db) return { success: false, error: 'no open DB' }; try { var ContentValues = Java.use('android.content.ContentValues'); var cv = ContentValues.$new(); var keys = Object.keys(kvMap); for (var i = 0; i < keys.length; i++) { var k = keys[i]; var v = kvMap[k]; if (typeof v === 'number' && v % 1 === 0 && v > 2000000000) { cv.put(k, Java.use('java.lang.Long').$new(v)); } else if (typeof v === 'number') { cv.put(k, Java.use('java.lang.Integer').$new(v)); } else { cv.put(k, String(v)); } } var rowId = db.insert(table, null, cv); return { success: rowId > 0, row_id: rowId.toString() }; } catch(e) { return { success: false, error: String(e).substring(0, 200) }; } } function getCtx() { return Java.use('android.app.ActivityThread').currentApplication().getApplicationContext(); } function generateId(prefix) { return (prefix || 'id') + '_' + Date.now() + '_' + Math.floor(Math.random() * 999999); } // ============================================================ // Hook:监听消息写入,捕获真实发送类 // ============================================================ Java.perform(function() { try { var WCDB = Java.use('com.tencent.wcdb.database.SQLiteDatabase'); WCDB.insert.overload( 'java.lang.String', 'java.lang.String', 'android.content.ContentValues' ).implementation = function(table, nullCol, values) { var result = this.insert(table, nullCol, values); if (table === 'message') { try { var isSend = values.getAsInteger('isSend'); if (isSend && parseInt(isSend.toString()) === 1) { var content = values.getAsString('content'); var talker = values.getAsString('talker'); var Exception = Java.use('java.lang.Exception'); var ex = Exception.$new('stack'); var stack = ex.getStackTrace(); var stackInfo = []; for (var i = 0; i < Math.min(stack.length, 25); i++) { var cn = stack[i].getClassName(); if (cn.indexOf('com.tencent.mm') !== -1) { stackInfo.push(cn + '.' + stack[i].getMethodName()); } } _capturedStack = stackInfo; send({ type: 'real_send_captured', talker: talker, content: content ? content.substring(0, 60) : '', stack: stackInfo.slice(0, 8) }); } } catch(e2) {} } return result; }; send({ type: 'hook_ok', msg: 'WCDB insert hooked for stack capture' }); } catch(e) { send({ type: 'hook_fail', msg: String(e).substring(0, 100) }); } send({ type: 'ready', msg: 'wechat_full_control v3 loaded' }); }); // ============================================================ // RPC接口 // ============================================================ rpc.exports = { // ==================== 账号信息 ==================== getAccountInfo: function() { return Java.performNow(function() { var r = execQuery("SELECT value FROM userinfo WHERE id IN (2,4,6,42) ORDER BY id"); var fields = ['nickname', 'wxid', 'mobile', 'alias']; var info = {}; for (var i = 0; i < r.rows.length && i < fields.length; i++) { info[fields[i]] = r.rows[i].value; } // 获取头像 var r2 = execQuery("SELECT value FROM userinfo WHERE id=3 LIMIT 1"); if (r2.rows.length > 0) info.avatar = r2.rows[0].value; return { success: true, account: info }; }); }, // ==================== 联系人(完整) ==================== getContacts: function(params) { return Java.performNow(function() { var limit = (params && params.limit) || 200; var offset = (params && params.offset) || 0; var type = (params && params.type) || 3; var sql = "SELECT username, nickname, conRemark, type, verifyFlag, imgPath FROM rcontact" + " WHERE type=" + type + " AND username NOT IN ('filehelper','weixin','brandsessionholder','medianote','floatbottle')" + " ORDER BY conRemark, nickname" + " LIMIT " + limit + " OFFSET " + offset; var r = execQuery(sql); return { success: true, contacts: r.rows, count: r.count, offset: offset, limit: limit }; }); }, // 获取联系人总数 getContactsCount: function() { return Java.performNow(function() { var r = execQuery("SELECT COUNT(*) as cnt FROM rcontact WHERE type=3"); var r2 = execQuery("SELECT COUNT(*) as cnt FROM rcontact"); var r3 = execQuery("SELECT COUNT(*) as cnt FROM chatroom"); return { success: true, type3_friends: r.rows.length > 0 ? r.rows[0].cnt : 0, total_rcontact: r2.rows.length > 0 ? r2.rows[0].cnt : 0, groups: r3.rows.length > 0 ? r3.rows[0].cnt : 0 }; }); }, // ==================== 群组 ==================== getGroups: function(params) { return Java.performNow(function() { var limit = (params && params.limit) || 50; var sql = "SELECT c.chatroomname, c.memberlist, c.chatroomnick, c.membercount," + " r.nickname as owner_name" + " FROM chatroom c" + " LEFT JOIN rcontact r ON c.chatroomname = r.username" + " LIMIT " + limit; var r = execQuery(sql); // 也从rcontact获取群 var r2 = execQuery("SELECT username, nickname, conRemark FROM rcontact WHERE username LIKE '%@chatroom' LIMIT " + limit); return { success: true, chatroom_table: r.rows, rcontact_groups: r2.rows, count: r.count }; }); }, // 获取群成员 getGroupMembers: function(params) { return Java.performNow(function() { var groupId = params && params.group_id; if (!groupId) return { success: false, error: 'no group_id' }; var r = execQuery("SELECT memberlist, chatroomnick FROM chatroom WHERE chatroomname='" + groupId + "' LIMIT 1"); if (r.rows.length === 0) return { success: false, error: 'group not found' }; var memberList = r.rows[0].memberlist || ''; var members = memberList ? memberList.split(';').filter(function(m) { return m.length > 0; }) : []; return { success: true, group_id: groupId, group_name: r.rows[0].chatroomnick || groupId, member_ids: members, count: members.length }; }); }, // ==================== 消息发送(WCDB直接写入) ==================== sendMessage: function(params) { return Java.performNow(function() { var toId = params && params.to_id; var content = params && params.content; var msgType = (params && params.msg_type) || 'text'; if (!toId || !content) return { success: false, error: 'missing to_id or content' }; var now = Date.now(); var msgId = now; var r = execInsert('message', { msgId: msgId, type: 1, isSend: 1, talker: toId, content: content, createTime: now, status: 2, imgPath: '', lvbuffer: '' }); if (r.success) { send({ type: 'message_sent', to_id: toId, content: content.substring(0, 50), method: 'wcdb_insert' }); } return { success: r.success, message_id: r.success ? String(msgId) : null, method: 'wcdb_direct_insert', to_id: toId, error: r.error }; }); }, // ==================== 消息读取 ==================== getMessages: function(params) { return Java.performNow(function() { var talker = params && params.talker; var limit = (params && params.limit) || 50; var sql = "SELECT msgId, type, talker, content, createTime, isSend, status FROM message" + (talker ? " WHERE talker='" + talker.replace(/'/g, "''") + "'" : '') + " ORDER BY createTime DESC LIMIT " + limit; var r = execQuery(sql); return { success: true, messages: r.rows, count: r.count }; }); }, // ==================== 朋友圈读取 ==================== getMoments: function(params) { return Java.performNow(function() { var limit = (params && params.limit) || 20; var r = execQuery("SELECT snsId, username, content, createTime, sourceType FROM SnsInfo ORDER BY createTime DESC LIMIT " + limit); return { success: true, moments: r.rows, count: r.count }; }); }, // 发朋友圈(WCDB写入SnsInfo表) postMoment: function(params) { return Java.performNow(function() { var content = params && params.content; if (!content) return { success: false, error: 'no content' }; var now = Date.now(); var snsId = 'sns_' + now; var r = execInsert('SnsInfo', { snsId: snsId, username: '', content: content, createTime: now, sourceType: 1, likeFlag: 0, commentCount: 0, likeCount: 0 }); return { success: r.success, sns_id: snsId, method: 'wcdb_direct_insert', error: r.error }; }); }, // ==================== 标签管理 ==================== getLabels: function() { return Java.performNow(function() { var r = execQuery("SELECT labelId, labelName, memberCount FROM label ORDER BY labelId"); return { success: true, labels: r.rows, count: r.count }; }); }, // 获取标签成员 getLabelMembers: function(params) { return Java.performNow(function() { var labelId = params && params.label_id; if (!labelId) return { success: false, error: 'no label_id' }; var r = execQuery("SELECT username FROM labelcontact WHERE labelId=" + labelId + " LIMIT 200"); return { success: true, label_id: labelId, members: r.rows, count: r.count }; }); }, // ==================== 搜索 ==================== searchContacts: function(params) { return Java.performNow(function() { var keyword = params && params.keyword; if (!keyword) return { success: false, error: 'no keyword' }; var kw = keyword.replace(/'/g, "''"); var sql = "SELECT username, nickname, conRemark, type FROM rcontact" + " WHERE (nickname LIKE '%" + kw + "%' OR conRemark LIKE '%" + kw + "%' OR username LIKE '%" + kw + "%')" + " LIMIT 50"; var r = execQuery(sql); return { success: true, results: r.rows, count: r.count }; }); }, // 搜索消息 searchMessages: function(params) { return Java.performNow(function() { var keyword = params && params.keyword; if (!keyword) return { success: false, error: 'no keyword' }; var kw = keyword.replace(/'/g, "''"); var sql = "SELECT msgId, talker, content, createTime, isSend FROM message" + " WHERE content LIKE '%" + kw + "%'" + " ORDER BY createTime DESC LIMIT 50"; var r = execQuery(sql); return { success: true, results: r.rows, count: r.count }; }); }, // ==================== 好友操作 ==================== // 添加好友(通过Intent启动微信搜索界面) addFriend: function(params) { return Java.performNow(function() { var wxid = params && params.wxid; if (!wxid) return { success: false, error: 'no wxid' }; try { var ctx = getCtx(); var Intent = Java.use('android.content.Intent'); var intent = Intent.$new(); intent.setClassName('com.tencent.mm', 'com.tencent.mm.ui.contact.AddContactUI'); intent.putExtra('search_str', wxid); intent.addFlags(0x10000000); ctx.startActivity(intent); return { success: true, method: 'intent_AddContactUI', wxid: wxid }; } catch(e) { return { success: false, error: String(e) }; } }); }, // 删除好友(更新rcontact表type) deleteFriend: function(params) { return Java.performNow(function() { var wxid = params && params.wxid; if (!wxid) return { success: false, error: 'no wxid' }; var db = getOpenDB(); if (!db) return { success: false, error: 'no db' }; try { var ContentValues = Java.use('android.content.ContentValues'); var cv = ContentValues.$new(); cv.put('type', Java.use('java.lang.Integer').$new(0)); var rows = db.update('rcontact', cv, "username='" + wxid.replace(/'/g, "''") + "'", null); return { success: rows > 0, affected_rows: rows, wxid: wxid }; } catch(e) { return { success: false, error: String(e) }; } }); }, // 设置好友备注 setRemark: function(params) { return Java.performNow(function() { var wxid = params && params.wxid; var remark = params && params.remark; if (!wxid || !remark) return { success: false, error: 'missing params' }; var db = getOpenDB(); if (!db) return { success: false, error: 'no db' }; try { var ContentValues = Java.use('android.content.ContentValues'); var cv = ContentValues.$new(); cv.put('conRemark', remark); var rows = db.update('rcontact', cv, "username='" + wxid.replace(/'/g, "''") + "'", null); return { success: rows > 0, affected_rows: rows, wxid: wxid, remark: remark }; } catch(e) { return { success: false, error: String(e) }; } }); }, // ==================== 群发消息 ==================== massMessage: function(params) { return Java.performNow(function() { var targets = params && params.targets; var content = params && params.content; if (!targets || !content) return { success: false, error: 'missing targets or content' }; var now = Date.now(); var results = []; for (var i = 0; i < targets.length; i++) { var toId = targets[i]; var msgId = now + i; var r = execInsert('message', { msgId: msgId, type: 1, isSend: 1, talker: toId, content: content, createTime: now + i, status: 2, imgPath: '', lvbuffer: '' }); results.push({ to_id: toId, success: r.success, msg_id: String(msgId) }); } var successCount = results.filter(function(r) { return r.success; }).length; return { success: successCount > 0, total: targets.length, success_count: successCount, results: results, method: 'wcdb_mass_insert' }; }); }, // ==================== 原始SQL查询 ==================== rawQuery: function(params) { return Java.performNow(function() { var sql = params && params.sql; if (!sql) return { success: false, error: 'no sql' }; return execQuery(sql); }); }, // ==================== 获取DB实例信息 ==================== getDBInfo: function() { return Java.performNow(function() { var instances = []; Java.choose('com.tencent.wcdb.database.SQLiteDatabase', { onMatch: function(db) { try { instances.push({ path: db.getPath(), open: db.isOpen(), read_only: db.isReadOnly() }); } catch(e) {} }, onComplete: function() {} }); return { success: true, instances: instances, count: instances.length }; }); }, // ==================== 获取捕获的真实发送类 ==================== getCapturedStack: function() { return { stack: _capturedStack, count: _capturedStack.length }; }, // ==================== 导航控制 ==================== navigateToChat: function(params) { return Java.performNow(function() { var wxid = params && params.wxid; if (!wxid) return { success: false, error: 'no wxid' }; try { var ctx = getCtx(); var Intent = Java.use('android.content.Intent'); var intent = Intent.$new(); intent.setClassName('com.tencent.mm', 'com.tencent.mm.ui.chatting.ChattingUI'); intent.putExtra('Chat_User', wxid); intent.addFlags(0x10000000); ctx.startActivity(intent); return { success: true, method: 'intent_ChattingUI', wxid: wxid }; } catch(e) { return { success: false, error: String(e) }; } }); }, // ==================== 系统信息 ==================== getSystemInfo: function() { return Java.performNow(function() { var db = getOpenDB(); var dbInfo = db ? { path: db.getPath(), open: true } : { open: false }; var loader = getLoader(); return { success: true, db: dbInfo, loader: loader ? 'found' : 'not_found', captured_stack_count: _capturedStack.length, timestamp: Date.now() }; }); } };