/** * wechat_hook_v2.js — 微信 Frida Hook 脚本(机擎 SDK v3.0) * * 全功能微信控制引擎 — 110 个操作 / 24 模块 * * 模块清单: * H15 消息接收(SQLite insert Hook) * H16 联系人获取(DB 直读) * H17 消息发送(内部方法 / Intent 降级) * H18 好友请求监听(SQLite + BroadcastReceiver) * H19 添加/通过好友(XmlParser Hook + Intent) * H20 朋友圈发布(SnsService Hook + Intent) * H21 朋友圈浏览/点赞(DB + RPC) * H22 群管理(创建/邀请/踢人/公告) * H23 账号管理(昵称/头像/签名/状态/性别/地区) * H24 账号安全(解封/改密/绑定手机/登录设备管理) * H25 支付(红包/转账/收款/零钱) * H26 二维码(扫码/生成个人码/群码) * H27 视频号(浏览/点赞/评论/关注) * H28 标签管理(创建/删除/打标/查询) * H29 收藏管理(添加/删除/列表) * H30 设置(隐私/通知/通用/聊天背景) * H31 搜索(全局搜索/联系人/消息/公众号) * H32 小程序(打开/最近/收藏) * H33 文件传输(发送文件/图片/视频/语音) * H34 消息转发(单条/合并转发) * H35 注册/登录(新号注册/登录/切号) * H36 公众号(关注/取关/列表/文章) * H37 表情管理(发送/收藏/自定义) * H38 浮窗/多任务 * H39 设备信息 * * 技术路线:Frida 17.x + Java.perform + SQLite Hook + rpc.exports * * @version 3.1.0 (版本自适应) */ 'use strict'; var PLATFORM = 'wechat'; var CONFIG = { WECHAT_PACKAGE: 'com.tencent.mm', LOG_LEVEL: 'info', DB_NAME: 'EnMicroMsg.db', SNS_DB_NAME: 'SnsMicroMsg.db', HOOK_SQLITE: true, HOOK_NETWORK: true, HOOK_FRIEND_REQUEST: true, HOOK_MOMENTS: true, MESSAGE_BUFFER_MAX: 500, FRIEND_REQUEST_BUFFER_MAX: 100, }; // ============================================================ // § 0 版本兼容系统 (H24) // ============================================================ var _versionCompat = null; var _VERSION_TABLE = { '8.0.44': { send_message: [ { cls: 'com.tencent.mm.modelmulti.h', method: 'b', sig: ['java.lang.String', 'java.lang.String', 'java.lang.String'] } ], friend_request: [{ cls: 'com.tencent.mm.plugin.messenger.foundation.a.b' }], sns_upload: [{ cls: 'com.tencent.mm.plugin.sns.ui.SnsUploadUI' }], add_friend: [{ cls: 'com.tencent.mm.plugin.profile.ui.ContactInfoUI' }] }, '8.0.49': { send_message: [ { cls: 'com.tencent.mm.modelmulti.g', method: 'b', sig: ['java.lang.String', 'java.lang.String', 'java.lang.String'] }, { cls: 'com.tencent.mm.modelmulti.h', method: 'b', sig: ['java.lang.String', 'java.lang.String', 'java.lang.String'] } ], friend_request: [ { cls: 'com.tencent.mm.plugin.messenger.foundation.a.c' }, { cls: 'com.tencent.mm.plugin.messenger.foundation.a.b' } ], sns_upload: [{ cls: 'com.tencent.mm.plugin.sns.ui.SnsUploadUI' }], add_friend: [{ cls: 'com.tencent.mm.plugin.profile.ui.ContactInfoUI' }] }, '8.0.51': { send_message: [ { cls: 'com.tencent.mm.plugin.messenger.foundation.a.k', method: 'a', sig: ['java.lang.String', 'java.lang.String'] }, { cls: 'com.tencent.mm.modelmulti.g', method: 'b', sig: ['java.lang.String', 'java.lang.String', 'java.lang.String'] } ], friend_request: [{ cls: 'com.tencent.mm.plugin.messenger.foundation.a.c' }], sns_upload: [{ cls: 'com.tencent.mm.plugin.sns.ui.SnsUploadUI' }], add_friend: [{ cls: 'com.tencent.mm.plugin.profile.ui.ContactInfoUI' }] }, '8.0.56': { send_message: [ { cls: 'com.tencent.mm.plugin.messenger.foundation.a.j', method: 'a', sig: ['java.lang.String', 'java.lang.String'] }, { cls: 'com.tencent.mm.plugin.messenger.foundation.a.k', method: 'a', sig: ['java.lang.String', 'java.lang.String'] } ], friend_request: [{ cls: 'com.tencent.mm.plugin.messenger.foundation.a.c' }], sns_upload: [{ cls: 'com.tencent.mm.plugin.sns.ui.SnsUploadUI' }, { cls: 'com.tencent.mm.plugin.sns.ui.SnsTimeLineUI' }], add_friend: [{ cls: 'com.tencent.mm.plugin.profile.ui.ContactInfoUI' }] }, '8.0.58': { send_message: [ { cls: 'com.tencent.mm.plugin.messenger.foundation.a.j', method: 'a', sig: ['java.lang.String', 'java.lang.String'] } ], friend_request: [{ cls: 'com.tencent.mm.plugin.messenger.foundation.a.c' }], sns_upload: [{ cls: 'com.tencent.mm.plugin.sns.ui.SnsUploadUI' }], add_friend: [{ cls: 'com.tencent.mm.plugin.profile.ui.ContactInfoUI' }] }, '8.0.60': { send_message: [ { cls: 'com.tencent.mm.plugin.messenger.foundation.a.j', method: 'a', sig: ['java.lang.String', 'java.lang.String'] } ], friend_request: [{ cls: 'com.tencent.mm.plugin.messenger.foundation.a.c' }], sns_upload: [{ cls: 'com.tencent.mm.plugin.sns.ui.SnsUploadUI' }], add_friend: [{ cls: 'com.tencent.mm.plugin.profile.ui.ContactInfoUI' }] }, '8.0.69': { send_message: [ { cls: 'com.tencent.mm.plugin.messenger.foundation.a.j', method: 'a', sig: ['java.lang.String', 'java.lang.String'] } ], friend_request: [{ cls: 'com.tencent.mm.plugin.messenger.foundation.a.c' }], sns_upload: [{ cls: 'com.tencent.mm.plugin.sns.ui.SnsUploadUI' }], add_friend: [{ cls: 'com.tencent.mm.plugin.profile.ui.ContactInfoUI' }] } }; var _DEFAULT_COMPAT = { send_message: [ { cls: 'com.tencent.mm.modelmulti.h', method: 'b', sig: ['java.lang.String', 'java.lang.String', 'java.lang.String'] }, { cls: 'com.tencent.mm.modelmulti.g', method: 'b', sig: ['java.lang.String', 'java.lang.String', 'java.lang.String'] }, { cls: 'com.tencent.mm.plugin.messenger.foundation.a.k', method: 'a', sig: ['java.lang.String', 'java.lang.String'] }, { cls: 'com.tencent.mm.plugin.messenger.foundation.a.j', method: 'a', sig: ['java.lang.String', 'java.lang.String'] } ], friend_request: [ { cls: 'com.tencent.mm.plugin.messenger.foundation.a.b' }, { cls: 'com.tencent.mm.plugin.messenger.foundation.a.c' } ], sns_upload: [ { cls: 'com.tencent.mm.plugin.sns.ui.SnsUploadUI' }, { cls: 'com.tencent.mm.plugin.sns.ui.SnsTimeLineUI' } ], add_friend: [ { cls: 'com.tencent.mm.plugin.profile.ui.ContactInfoUI' }, { cls: 'com.tencent.mm.protocal.protobuf.add$ContactType' } ] }; function _parseVersion(v) { var parts = (v || '').split('.').map(function(x) { return parseInt(x, 10) || 0; }); return parts[0] * 10000 + parts[1] * 100 + (parts[2] || 0); } function _resolveVersionCompat(version) { if (_VERSION_TABLE[version]) { _versionCompat = _VERSION_TABLE[version]; log('info', 'compat', '精确匹配版本 ' + version); return; } var vNum = _parseVersion(version); var bestKey = null, bestDist = 999999; var keys = Object.keys(_VERSION_TABLE); for (var i = 0; i < keys.length; i++) { var kNum = _parseVersion(keys[i]); var dist = Math.abs(vNum - kNum); if (dist < bestDist) { bestDist = dist; bestKey = keys[i]; } } if (bestKey && bestDist <= 10) { _versionCompat = _VERSION_TABLE[bestKey]; log('info', 'compat', '近似匹配 ' + version + ' → ' + bestKey + ' (距离=' + bestDist + ')'); } else { _versionCompat = _DEFAULT_COMPAT; log('warn', 'compat', '未匹配版本 ' + version + ',使用 default 全候选'); } } function getCompat(key) { return (_versionCompat && _versionCompat[key]) || (_DEFAULT_COMPAT[key]) || []; } // ============================================================ // § 1 工具函数 // ============================================================ var LOG_LEVEL_MAP = { debug: 0, info: 1, warn: 2, error: 3 }; function log(level, tag, message, extra) { if ((LOG_LEVEL_MAP[level] || 0) < (LOG_LEVEL_MAP[CONFIG.LOG_LEVEL] || 0)) return; send({ type: 'log', level: level, tag: tag, message: String(message || ''), extra: extra || {}, timestamp: Date.now(), }); } function emitEvent(eventType, payload) { send({ type: 'hook_event', event_type: eventType, platform: PLATFORM, payload: payload || {}, timestamp: new Date().toISOString(), }); } function safeStr(v) { if (v === null || v === undefined) return ''; try { return v.toString(); } catch (_) { return ''; } } function safeInt(v, fallback) { var n = parseInt(v, 10); return isNaN(n) ? (fallback || 0) : n; } function jStr(s) { return Java.use('java.lang.String').$new(String(s)); } function getCtx() { return Java.use('android.app.ActivityThread').currentApplication().getApplicationContext(); } function generateId(prefix) { return (prefix || 'wh') + '_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6); } // ============================================================ // § 2 全局状态 // ============================================================ var _messageBuffer = []; var _friendRequestBuffer = []; var _contactCache = []; var _dbPath = null; var _snsDbPath = null; var _wechatVersion = ''; var _hooked = { sqlite_insert: false, sqlite_update: false, network: false, friend_request: false, sns_upload: false, }; // ============================================================ // § 3 数据库通用层 // ============================================================ function _findDbPath(dbName) { try { var File = Java.use('java.io.File'); var dir = File.$new('/data/data/com.tencent.mm/MicroMsg/'); if (!dir.exists()) return null; var subs = dir.listFiles(); if (!subs) return null; for (var i = 0; i < subs.length; i++) { var p = subs[i].getAbsolutePath() + '/' + dbName; if (File.$new(p).exists()) return p; } } catch (_) {} return null; } function _getDbPath() { if (_dbPath) return _dbPath; _dbPath = _findDbPath(CONFIG.DB_NAME); return _dbPath; } function _getSnsDbPath() { if (_snsDbPath) return _snsDbPath; _snsDbPath = _findDbPath(CONFIG.SNS_DB_NAME); return _snsDbPath; } var _lastDbDiag = { ok: false, error: '', path: '', found: false, rows: 0 }; function _emptyReadFailure(kind, diag, force) { var d = diag || _lastDbDiag || {}; var hasDbError = d && (d.ok === false) && (d.error || d.found === false); if (!hasDbError && !force) return null; return { success: false, error: kind + '读取为空:DB 未打开或查询不稳定', count: 0, _diag: d, }; } function _execSQL(sql, dbPathOverride) { var results = []; _lastDbDiag = { ok: false, error: '', path: '', found: false, rows: 0 }; try { var path = dbPathOverride || _getDbPath(); _lastDbDiag.path = path || ''; if (!path) { _lastDbDiag.error = 'db_path 未找到'; return results; } var db = _findOpenWcdb(path); if (!db) { _lastDbDiag.error = '未找到已打开的 WCDB 实例'; log('warn', 'db', '未找到已打开的 WCDB 实例: ' + path); return results; } _lastDbDiag.found = true; // WCDB 1.x: rawQuery(sql, args);2.x core.Database 无 rawQuery if (typeof db.rawQuery !== 'function') { _lastDbDiag.error = '实例无 rawQuery(疑 WCDB 2.x core.Database),需 2.x 查询适配'; log('warn', 'db', _lastDbDiag.error); return results; } var cursor = db.rawQuery(sql, null); while (cursor.moveToNext()) { var row = {}; for (var c = 0; c < cursor.getColumnCount(); c++) { row[cursor.getColumnName(c)] = safeStr(cursor.getString(c)); } results.push(row); } cursor.close(); _lastDbDiag.ok = true; _lastDbDiag.rows = results.length; } catch (e) { _lastDbDiag.error = String(e); log('warn', 'db', 'SQL 执行失败: ' + e); } return results; } // WCDB 实例查找(兼容 8.0.69 WCDB 2.x):多候选类名 + 放宽路径匹配 var _WCDB_CLASS_CANDIDATES = [ 'com.tencent.wcdb.database.SQLiteDatabase', // WCDB 1.x 'com.tencent.wcdb.core.Database', // WCDB 2.x(新版微信) ]; var _wcdbClassResolved = null; function _wcdbInstancePath(instance) { // WCDB 1.x: getPath();2.x: getPath() 或 path 字段 try { if (typeof instance.getPath === 'function') return safeStr(instance.getPath()); } catch (_) {} try { if (instance.path) return safeStr(instance.path.value); } catch (_) {} return ''; } function _wcdbIsOpen(instance) { try { if (typeof instance.isOpen === 'function') return instance.isOpen(); } catch (_) {} try { if (typeof instance.isOpened === 'function') return instance.isOpened(); } catch (_) {} return true; // 2.x 无显式 isOpen 时按打开处理 } function _findOpenWcdb(path) { var normalizedPath = path.replace('/data/data/', '/data/user/0/'); var baseName = path.split('/').pop(); var found = null; // 始终遍历全部候选类:不同库分属不同 WCDB 版本(EnMicroMsg=1.x,SnsMicroMsg=2.x), // 不能用单一 _wcdbClassResolved 缓存限制,否则跨版本库找不到。 var classes = _WCDB_CLASS_CANDIDATES; for (var ci = 0; ci < classes.length && !found; ci++) { var cls = classes[ci]; try { Java.use(cls); } catch (_) { continue; } // 类不存在则跳过 try { Java.choose(cls, { onMatch: function (instance) { if (found) return; try { var p = _wcdbInstancePath(instance); var match = (p === path || p === normalizedPath || (baseName && p.indexOf(baseName) >= 0)); if (match && _wcdbIsOpen(instance)) { found = instance; _wcdbClassResolved = cls; } } catch (_) {} }, onComplete: function () {} }); } catch (e) { log('warn', 'db', 'Java.choose 失败 ' + cls + ': ' + e); } } if (!found) log('warn', 'db', '未找到已打开的 WCDB 实例(已试' + classes.length + '类): ' + baseName); return found; } // 诊断:枚举所有 WCDB 实例路径(真机排查 8.0.69 DB 读空) function _diagWcdbInstances() { var out = []; for (var ci = 0; ci < _WCDB_CLASS_CANDIDATES.length; ci++) { var cls = _WCDB_CLASS_CANDIDATES[ci]; try { Java.use(cls); } catch (_) { out.push({cls: cls, exists: false}); continue; } var paths = []; try { Java.choose(cls, { onMatch: function (inst) { try { paths.push({ path: _wcdbInstancePath(inst), open: _wcdbIsOpen(inst) }); } catch (_) {} }, onComplete: function () {} }); } catch (_) {} out.push({ cls: cls, exists: true, instances: paths }); } return out; } function _execSnsSQL(sql) { return _execSQL(sql, _getSnsDbPath()); } // ============================================================ // § 4 rpc.exports — Python 端通过 FridaManager.call_rpc 调用 // ============================================================ rpc.exports = { // ==================== 系统 ==================== ping: function () { return 'pong from wechat_hook_v3.0 — 96 actions / 26 modules'; }, getProcessInfo: function () { return { pid: Process.id, arch: Process.arch, platform: Process.platform, modules: Process.enumerateModules().length, wechat_version: _wechatVersion, hooked: _hooked, }; }, getHookStatus: function () { return _hooked; }, // 诊断:枚举真机 WCDB 实例路径(排查 8.0.69 DB 读空) diagWcdb: function () { try { return Java.performNow(function () { var out = { success: true, db_path: _getDbPath(), sns_path: _getSnsDbPath(), instances: _diagWcdbInstances() }; // 实读测试:main 库 rcontact、sns 库 SnsInfo,回传 _lastDbDiag 定位查询错误 try { _execSQL('SELECT count(*) AS n FROM rcontact'); out.main_query = JSON.parse(JSON.stringify(_lastDbDiag)); } catch (e1) { out.main_query = { error: String(e1) }; } try { _execSnsSQL('SELECT count(*) AS n FROM SnsInfo'); out.sns_query = JSON.parse(JSON.stringify(_lastDbDiag)); } catch (e2) { out.sns_query = { error: String(e2) }; } return out; }); } catch (e) { return { success: false, error: String(e) }; } }, // 诊断:运行时反射枚举 8.0.69 朋友圈(SNS)发布/读取相关类与方法 // 用于在混淆版定位 post_moments(sns.model.ay 等价) / get_moments(m3.onGYNetEnd 等价) 真实类名。 diagSns: function (params) { // 安全诊断:**禁用** Java.enumerateLoadedClassesSync(微信 10万+ 类,桥接枚举会阻塞 // agent 事件循环导致 WS 掉线 — 2026-05-30 实测)。改为「试探调用方提供的候选类名」: // params.candidates = ["com.tencent.mm.plugin.sns.model.xx", ...](来自离线 apk/smali 分析) // 对每个候选 Java.use + 反射方法名(最多 8 个,快速、不枚举),定位发/读朋友圈真实类。 var cands = (params && params.candidates) || []; if (!cands || cands.length === 0) { return { success: false, error: 'no_candidates', hint: '运行时全量枚举已禁用(会致 WS 掉线)。请先离线对 8.0.69 apk 跑 jadx/baksmali 取候选类名,再传 params.candidates=[...] 试探。', }; } var lim = Math.min(cands.length, 8); try { return Java.performNow(function () { var out = []; for (var i = 0; i < lim; i++) { var cn = cands[i]; var info = { cls: cn, exists: false, methods: [], ctors: 0, hasCommit: false }; try { var k = Java.use(cn); info.exists = true; var ms = k.class.getDeclaredMethods(); var names = {}; for (var m = 0; m < ms.length; m++) { var mn = ms[m].getName(); names[mn] = 1; if (mn.toLowerCase().indexOf('commit') >= 0) info.hasCommit = true; } info.methods = Object.keys(names).slice(0, 30); info.ctors = k.class.getDeclaredConstructors().length; } catch (e0) { info.err = String(e0); } out.push(info); } return { success: true, tested: lim, classes: out }; }); } catch (e) { return { success: false, error: String(e) }; } }, getWechatVersion: function () { if (_wechatVersion) return { success: true, version: _wechatVersion }; try { return Java.performNow(function () { var ctx = getCtx(); var pm = ctx.getPackageManager(); var info = pm.getPackageInfo(jStr('com.tencent.mm'), 0); _wechatVersion = safeStr(info.versionName.value); _resolveVersionCompat(_wechatVersion); return { success: true, version: _wechatVersion }; }); } catch (e) { return { success: false, error: String(e) }; } }, getVersionCompat: function () { return { wechat_version: _wechatVersion, matched: _versionCompat !== _DEFAULT_COMPAT, supported_versions: Object.keys(_VERSION_TABLE), active_compat: _versionCompat ? Object.keys(_versionCompat) : [], }; }, // ==================== H17 消息发送 ==================== sendMessage: function (params) { 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: '缺少 to_id 或 content' }; } log('info', 'rpc', 'sendMessage', { to_id: toId, content_len: content.length, msg_type: msgType }); try { return Java.performNow(function () { return _sendMessageInternal(toId, content, msgType); }); } catch (e) { return { success: false, error: String(e) }; } }, // ==================== H15 消息获取 ==================== getMessages: function (params) { var limit = (params && params.limit) || 50; var conversationId = (params && params.conversation_id) || ''; var dbRes = null; try { dbRes = Java.performNow(function () { return _getMessagesFromDB(conversationId, limit); }); } catch (e) { dbRes = { success: false, error: String(e), messages: [] }; } // DB 读为空时 fallback 到 hook 实时缓冲(insert hook 已拦截新消息) if (!dbRes || !dbRes.messages || dbRes.messages.length === 0) { var buf = _messageBuffer.filter(function (m) { return !conversationId || m.from_id === conversationId; }).slice(-limit); if (buf.length > 0) { return { success: true, messages: buf.map(function (m) { return { id: m.msg_svr_id, type: m.msg_type, from_id: m.from_id, content: m.content, timestamp: m.timestamp, is_send: m.is_send }; }), count: buf.length, source: 'hook_buffer', db_diag: _lastDbDiag, }; } if (dbRes) dbRes.db_diag = _lastDbDiag; } return dbRes; }, getRecentMessages: function (params) { var limit = (params && params.limit) || 20; var msgs = _messageBuffer.slice(-limit); return { success: true, messages: msgs, count: msgs.length }; }, searchMessages: function (params) { var keyword = (params && params.keyword) || ''; var limit = (params && params.limit) || 50; if (!keyword) return { success: false, error: '缺少 keyword' }; try { return Java.performNow(function () { var sql = "SELECT msgId, type, talker, content, createTime FROM message" + " WHERE content LIKE '%" + keyword.replace(/'/g, "''") + "%'" + " ORDER BY createTime DESC LIMIT " + limit; var rows = _execSQL(sql); return { success: true, messages: rows.map(function (r) { return { id: r.msgId, type: r.type, from_id: r.talker, content: r.content, timestamp: r.createTime }; }), count: rows.length, }; }); } catch (e) { return { success: false, error: String(e), messages: [] }; } }, // ==================== H16 联系人 ==================== getContacts: function (params) { var limit = (params && params.limit) || 200; try { return Java.performNow(function () { return _getContactsFromDB(limit); }); } catch (e) { return { success: false, error: String(e), contacts: [] }; } }, getContactInfo: function (params) { var wxid = (params && params.wxid) || ''; if (!wxid) return { success: false, error: '缺少 wxid' }; try { return Java.performNow(function () { return _getContactInfoFromDB(wxid); }); } catch (e) { return { success: false, error: String(e) }; } }, searchContacts: function (params) { var keyword = (params && params.keyword) || ''; var limit = (params && params.limit) || 50; if (!keyword) return { success: false, error: '缺少 keyword' }; try { return Java.performNow(function () { 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 + "%')" + " AND type NOT IN (0, 33) AND verifyFlag=0" + " ORDER BY nickname LIMIT " + limit; var rows = _execSQL(sql); return { success: true, contacts: rows.map(function (r) { return { wxid: r.username, nickname: r.nickname, remark: r.conRemark, type: r.type }; }), count: rows.length, }; }); } catch (e) { return { success: false, error: String(e), contacts: [] }; } }, // ==================== H18/H19 好友请求 ==================== getFriendRequests: function (params) { var limit = (params && params.limit) || 50; var cached = _friendRequestBuffer.slice(-limit); if (cached.length > 0) return { success: true, requests: cached, count: cached.length, source: 'buffer' }; try { return Java.performNow(function () { return _getFriendRequestsFromDB(limit); }); } catch (e) { return { success: false, error: String(e), requests: [] }; } }, addFriend: function (params) { var userId = (params && params.user_id) || ''; var message = (params && params.message) || '你好,我想加你为好友'; var sourceType = (params && params.source_type) || 3; // 3=搜索 if (!userId) return { success: false, error: '缺少 user_id' }; log('info', 'rpc', 'addFriend', { user_id: userId, source_type: sourceType }); try { return Java.performNow(function () { return _addFriendInternal(userId, message, sourceType); }); } catch (e) { return { success: false, error: String(e) }; } }, acceptFriend: function (params) { var encryptUserName = (params && params.encrypt_username) || ''; var ticket = (params && params.ticket) || ''; if (!encryptUserName && !ticket) return { success: false, error: '缺少 encrypt_username 或 ticket' }; log('info', 'rpc', 'acceptFriend', { encrypt_username: encryptUserName }); try { return Java.performNow(function () { return _acceptFriendInternal(encryptUserName, ticket); }); } catch (e) { return { success: false, error: String(e) }; } }, deleteFriend: function (params) { var wxid = (params && params.wxid) || ''; if (!wxid) return { success: false, error: '缺少 wxid' }; try { return Java.performNow(function () { return _deleteFriendInternal(wxid); }); } catch (e) { return { success: false, error: String(e) }; } }, setFriendRemark: function (params) { var wxid = (params && params.wxid) || ''; var remark = (params && params.remark) || ''; if (!wxid) return { success: false, error: '缺少 wxid' }; try { return Java.performNow(function () { return _setRemarkInternal(wxid, remark); }); } catch (e) { return { success: false, error: String(e) }; } }, // ==================== H22 群管理 ==================== getGroups: function (params) { var limit = (params && params.limit) || 100; try { return Java.performNow(function () { return _getGroupsFromDB(limit); }); } catch (e) { return { success: false, error: String(e), groups: [] }; } }, getGroupMembers: function (params) { var groupId = (params && params.group_id) || ''; if (!groupId) return { success: false, error: '缺少 group_id' }; try { return Java.performNow(function () { return _getGroupMembersFromDB(groupId); }); } catch (e) { return { success: false, error: String(e), members: [] }; } }, getGroupInfo: function (params) { var groupId = (params && params.group_id) || ''; if (!groupId) return { success: false, error: '缺少 group_id' }; try { return Java.performNow(function () { return _getGroupInfoFromDB(groupId); }); } catch (e) { return { success: false, error: String(e) }; } }, sendGroupMessage: function (params) { var groupId = (params && params.group_id) || ''; var content = (params && params.content) || ''; if (!groupId || !content) return { success: false, error: '缺少 group_id 或 content' }; try { return Java.performNow(function () { return _sendMessageInternal(groupId, content, 'text'); }); } catch (e) { return { success: false, error: String(e) }; } }, createGroup: function (params) { var memberIds = (params && params.member_ids) || []; var topic = (params && params.topic) || ''; if (memberIds.length < 2) return { success: false, error: '建群至少需要2个成员' }; try { return Java.performNow(function () { return _createGroupInternal(memberIds, topic); }); } catch (e) { return { success: false, error: String(e) }; } }, inviteToGroup: function (params) { var groupId = (params && params.group_id) || ''; var memberIds = (params && params.member_ids) || []; if (!groupId || memberIds.length === 0) return { success: false, error: '缺少 group_id 或 member_ids' }; try { return Java.performNow(function () { return _inviteToGroupInternal(groupId, memberIds); }); } catch (e) { return { success: false, error: String(e) }; } }, removeFromGroup: function (params) { var groupId = (params && params.group_id) || ''; var memberIds = (params && params.member_ids) || []; if (!groupId || memberIds.length === 0) return { success: false, error: '缺少 group_id 或 member_ids' }; try { return Java.performNow(function () { return _removeFromGroupInternal(groupId, memberIds); }); } catch (e) { return { success: false, error: String(e) }; } }, setGroupAnnouncement: function (params) { var groupId = (params && params.group_id) || ''; var announcement = (params && params.announcement) || ''; if (!groupId) return { success: false, error: '缺少 group_id' }; try { return Java.performNow(function () { return _setGroupAnnouncementInternal(groupId, announcement); }); } catch (e) { return { success: false, error: String(e) }; } }, setGroupName: function (params) { var groupId = (params && params.group_id) || ''; var name = (params && params.name) || ''; if (!groupId || !name) return { success: false, error: '缺少 group_id 或 name' }; try { return Java.performNow(function () { return _setGroupNameInternal(groupId, name); }); } catch (e) { return { success: false, error: String(e) }; } }, quitGroup: function (params) { var groupId = (params && params.group_id) || ''; if (!groupId) return { success: false, error: '缺少 group_id' }; try { return Java.performNow(function () { return _quitGroupInternal(groupId); }); } catch (e) { return { success: false, error: String(e) }; } }, // ==================== H20/H21 朋友圈 ==================== postMoments: function (params) { var content = (params && params.content) || ''; var imageUrls = (params && params.image_urls) || []; if (!content && imageUrls.length === 0) return { success: false, error: '朋友圈内容不能为空' }; var autoSend = !(params && params.auto_send === false); try { return Java.performNow(function () { return _postMomentsInternal(content, imageUrls, autoSend); }); } catch (e) { return { success: false, error: '发朋友圈失败: ' + String(e) }; } }, getMoments: function (params) { var limit = (params && params.limit) || 20; var wxid = (params && params.wxid) || ''; try { return Java.performNow(function () { return _getMomentsFromDB(wxid, limit); }); } catch (e) { return { success: false, error: String(e), moments: [] }; } }, likeMoments: function (params) { var snsId = (params && params.sns_id) || ''; if (!snsId) return { success: false, error: '缺少 sns_id' }; try { return Java.performNow(function () { return _likeMomentsInternal(snsId); }); } catch (e) { return { success: false, error: String(e) }; } }, commentMoments: function (params) { var snsId = (params && params.sns_id) || ''; var comment = (params && params.comment) || ''; if (!snsId || !comment) return { success: false, error: '缺少 sns_id 或 comment' }; try { return Java.performNow(function () { return _commentMomentsInternal(snsId, comment); }); } catch (e) { return { success: false, error: String(e) }; } }, deleteMoments: function (params) { var snsId = (params && params.sns_id) || ''; if (!snsId) return { success: false, error: '缺少 sns_id' }; return _intentAction('com.workphone.DELETE_MOMENTS', { sns_id: snsId }, 'moments_deleted', { sns_id: snsId }); }, // ==================== H23 账号管理 ==================== getProfile: function () { try { return Java.performNow(function () { var sql = "SELECT value FROM userinfo WHERE id=4"; var rows = _execSQL(sql); var nickname = rows.length > 0 ? rows[0].value : ''; var sql2 = "SELECT value FROM userinfo WHERE id=2"; var rows2 = _execSQL(sql2); var wxid = rows2.length > 0 ? rows2[0].value : ''; var sql3 = "SELECT value FROM userinfo WHERE id=6"; var rows3 = _execSQL(sql3); var phone = rows3.length > 0 ? rows3[0].value : ''; var sql4 = "SELECT value FROM userinfo WHERE id=12290"; var rows4 = _execSQL(sql4); var signature = rows4.length > 0 ? rows4[0].value : ''; var sql5 = "SELECT value FROM userinfo WHERE id=3"; var rows5 = _execSQL(sql5); var sex = rows5.length > 0 ? rows5[0].value : ''; var sql6 = "SELECT value FROM userinfo WHERE id=5"; var rows6 = _execSQL(sql6); var region = rows6.length > 0 ? rows6[0].value : ''; return { success: true, profile: { nickname: nickname, wxid: wxid, phone: phone, signature: signature, sex: sex, region: region, wechat_version: _wechatVersion }, }; }); } catch (e) { return { success: false, error: String(e) }; } }, checkAccountStatus: function () { try { return Java.performNow(function () { var ctx = getCtx(); var pm = ctx.getPackageManager(); var info = pm.getPackageInfo(jStr('com.tencent.mm'), 0); var sql = "SELECT value FROM userinfo WHERE id=2"; var rows = _execSQL(sql); return { success: true, status: 'active', nickname: rows.length > 0 ? rows[0].value : '', version: safeStr(info.versionName.value), pid: Process.id, }; }); } catch (e) { return { success: false, error: String(e), status: 'unknown' }; } }, setNickname: function (params) { var nickname = (params && params.nickname) || ''; if (!nickname) return { success: false, error: '缺少 nickname' }; return _intentAction('com.workphone.SET_NICKNAME', { nickname: nickname }, 'nickname_changed', { nickname: nickname }); }, setSignature: function (params) { var signature = (params && params.signature) || ''; return _intentAction('com.workphone.SET_SIGNATURE', { signature: signature }, 'signature_changed', { signature: signature }); }, setAvatar: function (params) { var imagePath = (params && params.image_path) || ''; if (!imagePath) return { success: false, error: '缺少 image_path' }; return _intentAction('com.workphone.SET_AVATAR', { image_path: imagePath }, 'avatar_changed', { image_path: imagePath }); }, setSex: function (params) { var sex = (params && params.sex) || '1'; return _intentAction('com.workphone.SET_SEX', { sex: sex }, 'sex_changed', { sex: sex }); }, setRegion: function (params) { var region = (params && params.region) || ''; if (!region) return { success: false, error: '缺少 region' }; return _intentAction('com.workphone.SET_REGION', { region: region }, 'region_changed', { region: region }); }, setWhatUp: function (params) { var status = (params && params.status) || ''; return _intentAction('com.workphone.SET_WHATSUP', { status: status }, 'status_changed', { status: status }); }, // ==================== H24 账号安全 ==================== unblockSelf: function () { return _intentAction('com.workphone.UNBLOCK_SELF', {}, 'unblock_started', {}); }, changePassword: function (params) { var oldPwd = (params && params.old_password) || ''; var newPwd = (params && params.new_password) || ''; if (!oldPwd || !newPwd) return { success: false, error: '缺少 old_password 或 new_password' }; return _intentAction('com.workphone.CHANGE_PASSWORD', { old_password: oldPwd, new_password: newPwd }, 'password_changed', {}); }, bindPhone: function (params) { var phone = (params && params.phone) || ''; if (!phone) return { success: false, error: '缺少 phone' }; return _intentAction('com.workphone.BIND_PHONE', { phone: phone }, 'phone_bind_started', { phone: phone }); }, unbindPhone: function () { return _intentAction('com.workphone.UNBIND_PHONE', {}, 'phone_unbind_started', {}); }, getLoginDevices: function () { return _intentAction('com.workphone.GET_LOGIN_DEVICES', {}, null, {}); }, removeLoginDevice: function (params) { var deviceId = (params && params.device_id) || ''; if (!deviceId) return { success: false, error: '缺少 device_id' }; return _intentAction('com.workphone.REMOVE_LOGIN_DEVICE', { device_id: deviceId }, 'login_device_removed', { device_id: deviceId }); }, enableFingerprint: function (params) { var enable = params && params.enable !== undefined ? params.enable : true; return _intentAction('com.workphone.TOGGLE_FINGERPRINT', { enable: String(enable) }, 'fingerprint_toggled', { enable: enable }); }, setAccountProtection: function (params) { var enable = params && params.enable !== undefined ? params.enable : true; return _intentAction('com.workphone.SET_ACCOUNT_PROTECTION', { enable: String(enable) }, 'account_protection_set', { enable: enable }); }, // ==================== H25 支付 ==================== sendRedPacket: function (params) { var toId = (params && params.to_id) || ''; var amount = (params && params.amount) || ''; var message = (params && params.message) || '恭喜发财'; if (!toId || !amount) return { success: false, error: '缺少 to_id 或 amount' }; return _intentAction('com.workphone.SEND_RED_PACKET', { to_id: toId, amount: amount, message: message }, 'red_packet_sent', { to_id: toId, amount: amount }); }, receiveRedPacket: function (params) { var msgSvrId = (params && params.msg_svr_id) || ''; if (!msgSvrId) return { success: false, error: '缺少 msg_svr_id' }; return _intentAction('com.workphone.RECEIVE_RED_PACKET', { msg_svr_id: msgSvrId }, 'red_packet_received', { msg_svr_id: msgSvrId }); }, sendTransfer: function (params) { var toId = (params && params.to_id) || ''; var amount = (params && params.amount) || ''; var desc = (params && params.description) || ''; if (!toId || !amount) return { success: false, error: '缺少 to_id 或 amount' }; return _intentAction('com.workphone.SEND_TRANSFER', { to_id: toId, amount: amount, description: desc }, 'transfer_sent', { to_id: toId, amount: amount }); }, receiveTransfer: function (params) { var msgSvrId = (params && params.msg_svr_id) || ''; if (!msgSvrId) return { success: false, error: '缺少 msg_svr_id' }; return _intentAction('com.workphone.RECEIVE_TRANSFER', { msg_svr_id: msgSvrId }, 'transfer_received', { msg_svr_id: msgSvrId }); }, getWalletBalance: function () { return _intentAction('com.workphone.GET_WALLET_BALANCE', {}, null, {}); }, getTransactionHistory: function (params) { var limit = (params && params.limit) || 20; return _intentAction('com.workphone.GET_TRANSACTIONS', { limit: String(limit) }, null, {}); }, showPaymentCode: function () { return _intentAction('com.workphone.SHOW_PAYMENT_CODE', {}, null, {}); }, getSafetyCenter: function () { return _intentAction('com.workphone.GET_SAFETY_CENTER', {}, null, {}); }, checkRestrictions: function () { try { return Java.performNow(function () { var kws = ['账号暂时无法使用聊天功能', '功能受限', '账号异常', '暂时限制', '封禁', '被限制']; var sql = 'SELECT content FROM message ORDER BY createTime DESC LIMIT 200'; var rows = _execSQL(sql); var hits = []; for (var i = 0; i < rows.length; i++) { var c = String(rows[i].content || ''); for (var j = 0; j < kws.length; j++) { if (c.indexOf(kws[j]) >= 0 && hits.indexOf(kws[j]) < 0) hits.push(kws[j]); } } return { success: true, restricted: hits.length > 0, restrictions: hits }; }); } catch (e) { return { success: false, error: String(e), restricted: false, restrictions: [] }; } }, getTopStories: function () { return _intentAction('com.workphone.GET_TOP_STORIES', {}, null, {}); }, getWechatSteps: function () { return _intentAction('com.workphone.GET_WECHAT_STEPS', {}, null, {}); }, getStickerList: function (params) { // 真实现:读 EnMicroMsg.db 的 EmojiInfo 表(自定义表情);动态列探测兼容 8.0.x; // 表不存在/无自定义表情时诚实返回空,不假成功(真机铁律 #3/#14)。 var limit = (params && params.limit) || 100; try { return Java.performNow(function () { var cols; try { cols = _execSQL("PRAGMA table_info(EmojiInfo)"); } catch (e0) { cols = []; } if (!cols || cols.length === 0) { return { success: true, stickers: [], count: 0, channel: 'frida_db', note: 'EmojiInfo 表不存在或无自定义表情(版本/账号差异)' }; } var names = cols.map(function (c) { return c.name; }); var want = ['md5', 'catalog', 'name', 'type', 'size', 'cdnUrl', 'groupId', 'reserved4']; var sel = want.filter(function (w) { return names.indexOf(w) >= 0; }); if (sel.length === 0) sel = names.slice(0, 6); var rows = _execSQL("SELECT " + sel.join(',') + " FROM EmojiInfo LIMIT " + limit); return { success: true, channel: 'frida_db', columns: sel, stickers: rows.map(function (r) { return { md5: r.md5, name: r.name, catalog: r.catalog, type: r.type, size: r.size, cdn_url: r.cdnUrl || r.reserved4 || '', group_id: r.groupId }; }), count: rows.length, }; }); } catch (e) { return { success: false, error: String(e), stickers: [] }; } }, likeWechatSteps: function (params) { var userId = (params && params.user_id) || ''; return _intentAction('com.workphone.LIKE_WECHAT_STEPS', { user_id: userId }, 'steps_liked', { user_id: userId }); }, clearCache: function () { return _intentAction('com.workphone.CLEAR_CACHE', {}, 'cache_cleared', {}); }, checkForUpdate: function () { return _intentAction('com.workphone.CHECK_UPDATE', {}, 'update_checked', {}); }, // ==================== H26 二维码 ==================== scanQrCode: function (params) { var imagePath = (params && params.image_path) || ''; return _intentAction('com.workphone.SCAN_QR', { image_path: imagePath }, 'qr_scanned', {}); }, generateMyQrCode: function () { return _intentAction('com.workphone.GENERATE_MY_QR', {}, 'my_qr_generated', {}); }, generateGroupQrCode: function (params) { var groupId = (params && params.group_id) || ''; if (!groupId) return { success: false, error: '缺少 group_id' }; return _intentAction('com.workphone.GENERATE_GROUP_QR', { group_id: groupId }, 'group_qr_generated', { group_id: groupId }); }, addFriendByQr: function (params) { var qrContent = (params && params.qr_content) || ''; if (!qrContent) return { success: false, error: '缺少 qr_content' }; return _intentAction('com.workphone.ADD_FRIEND_QR', { qr_content: qrContent }, 'friend_added_qr', {}); }, // ==================== H27 视频号 ==================== browseChannels: function (params) { var limit = (params && params.limit) || 10; return _intentAction('com.workphone.BROWSE_CHANNELS', { limit: String(limit) }, null, {}); }, likeChannelVideo: function (params) { var videoId = (params && params.video_id) || ''; if (!videoId && params && params.index !== undefined) { videoId = 'index_' + params.index; } if (!videoId) return { success: false, error: '缺少 video_id' }; return _intentAction('com.workphone.LIKE_CHANNEL', { video_id: videoId }, 'channel_liked', { video_id: videoId }); }, commentChannelVideo: function (params) { var videoId = (params && params.video_id) || ''; var comment = (params && params.comment) || ''; if (!videoId || !comment) return { success: false, error: '缺少 video_id 或 comment' }; return _intentAction('com.workphone.COMMENT_CHANNEL', { video_id: videoId, comment: comment }, 'channel_commented', { video_id: videoId }); }, followChannel: function (params) { var channelId = (params && params.channel_id) || ''; if (!channelId) return { success: false, error: '缺少 channel_id' }; return _intentAction('com.workphone.FOLLOW_CHANNEL', { channel_id: channelId }, 'channel_followed', { channel_id: channelId }); }, unfollowChannel: function (params) { var channelId = (params && params.channel_id) || ''; if (!channelId) return { success: false, error: '缺少 channel_id' }; return _intentAction('com.workphone.UNFOLLOW_CHANNEL', { channel_id: channelId }, 'channel_unfollowed', { channel_id: channelId }); }, shareChannelVideo: function (params) { var videoId = (params && params.video_id) || ''; var toId = (params && params.to_id) || ''; if (!videoId || !toId) return { success: false, error: '缺少 video_id 或 to_id' }; return _intentAction('com.workphone.SHARE_CHANNEL', { video_id: videoId, to_id: toId }, 'channel_shared', { video_id: videoId, to_id: toId }); }, // ==================== H28 标签管理 ==================== getLabels: function () { try { return Java.performNow(function () { var sql = "SELECT labelID, labelName FROM contactlabel ORDER BY labelID"; var rows = _execSQL(sql); return { success: true, labels: rows.map(function (r) { return { id: r.labelID, name: r.labelName }; }), count: rows.length }; }); } catch (e) { return { success: false, error: String(e), labels: [] }; } }, createLabel: function (params) { var name = (params && params.name) || ''; if (!name) return { success: false, error: '缺少 name' }; return _intentAction('com.workphone.CREATE_LABEL', { name: name }, 'label_created', { name: name }); }, deleteLabel: function (params) { var labelId = (params && params.label_id) || ''; if (!labelId) return { success: false, error: '缺少 label_id' }; return _intentAction('com.workphone.DELETE_LABEL', { label_id: labelId }, 'label_deleted', { label_id: labelId }); }, setContactLabel: function (params) { var wxid = (params && params.wxid) || ''; var labelIds = (params && params.label_ids) || []; if (!wxid) return { success: false, error: '缺少 wxid' }; return _intentAction('com.workphone.SET_CONTACT_LABEL', { wxid: wxid, label_ids: (labelIds || []).join(',') }, 'contact_labeled', { wxid: wxid, label_ids: labelIds }); }, getContactsByLabel: function (params) { var labelId = (params && params.label_id) || ''; if (!labelId) return { success: false, error: '缺少 label_id' }; try { return Java.performNow(function () { var sql = "SELECT username, nickname, conRemark FROM rcontact WHERE contactLabelIds LIKE '%" + labelId + "%' AND type NOT IN (0,33) AND verifyFlag=0 ORDER BY nickname LIMIT 200"; var rows = _execSQL(sql); return { success: true, contacts: rows.map(function (r) { return { wxid: r.username, nickname: r.nickname, remark: r.conRemark }; }), count: rows.length }; }); } catch (e) { return { success: false, error: String(e), contacts: [] }; } }, // ==================== H29 收藏管理 ==================== getFavorites: function (params) { var limit = (params && params.limit) || 50; try { return Java.performNow(function () { var names = ['Favorite.db', 'favorite.db', 'EnFavorite.db']; var favPath = null; for (var i = 0; i < names.length; i++) { favPath = _findDbPath(names[i]); if (favPath) break; } if (!favPath) { var sql2 = "SELECT localId, type, sourceUserName, updateTime FROM FavItemInfo ORDER BY updateTime DESC LIMIT " + limit; var rows2 = _execSQL(sql2); if (rows2.length > 0) return { success: true, favorites: rows2, count: rows2.length, source: 'main_db' }; return { success: true, favorites: [], count: 0, note: 'Favorite 表不在独立数据库中,主库也未找到' }; } var sql = "SELECT localId, type, sourceUserName, updateTime FROM FavItemInfo ORDER BY updateTime DESC LIMIT " + limit; var rows = _execSQL(sql, favPath); return { success: true, favorites: rows, count: rows.length }; }); } catch (e) { return { success: false, error: String(e), favorites: [] }; } }, addFavorite: function (params) { var msgSvrId = (params && params.msg_svr_id) || ''; var type = (params && params.type) || 'message'; var content = (params && params.content) || (params && params.content_desc) || ''; if (!msgSvrId && content) { return _intentAction('com.workphone.ADD_FAVORITE_TEXT', { content: content, type: type }, 'favorite_added', { content: content }); } if (!msgSvrId) return { success: false, error: '缺少 msg_svr_id 或 content' }; return _intentAction('com.workphone.ADD_FAVORITE', { msg_svr_id: msgSvrId, type: type }, 'favorite_added', { msg_svr_id: msgSvrId }); }, deleteFavorite: function (params) { var localId = (params && params.local_id) || ''; if (!localId) return { success: false, error: '缺少 local_id' }; return _intentAction('com.workphone.DELETE_FAVORITE', { local_id: localId }, 'favorite_deleted', { local_id: localId }); }, // ==================== H30 设置 ==================== setPrivacy: function (params) { var setting = (params && params.setting) || ''; var value = params && params.value !== undefined ? params.value : true; if (!setting) return { success: false, error: '缺少 setting' }; return _intentAction('com.workphone.SET_PRIVACY', { setting: setting, value: String(value) }, 'privacy_set', { setting: setting, value: value }); }, setNotification: function (params) { var type = (params && params.type) || 'all'; var enable = params && params.enable !== undefined ? params.enable : true; return _intentAction('com.workphone.SET_NOTIFICATION', { type: type, enable: String(enable) }, 'notification_set', { type: type, enable: enable }); }, clearChatHistory: function (params) { var wxid = (params && params.wxid) || ''; if (!wxid) return { success: false, error: '缺少 wxid' }; return _intentAction('com.workphone.CLEAR_CHAT', { wxid: wxid }, 'chat_cleared', { wxid: wxid }); }, setChatBackground: function (params) { var wxid = (params && params.wxid) || ''; var imagePath = (params && params.image_path) || ''; return _intentAction('com.workphone.SET_CHAT_BG', { wxid: wxid, image_path: imagePath }, 'chat_bg_set', { wxid: wxid }); }, setDoNotDisturb: function (params) { var wxid = (params && params.wxid) || ''; var enable = params && params.enable !== undefined ? params.enable : true; if (!wxid) { return _intentAction('com.workphone.SET_GLOBAL_DND', { enable: String(enable) }, 'dnd_set', { enable: enable }); } return _intentAction('com.workphone.SET_DND', { wxid: wxid, enable: String(enable) }, 'dnd_set', { wxid: wxid, enable: enable }); }, pinChat: function (params) { var wxid = (params && params.wxid) || ''; var pin = params && params.pin !== undefined ? params.pin : true; if (!wxid) return { success: false, error: '缺少 wxid' }; return _intentAction('com.workphone.PIN_CHAT', { wxid: wxid, pin: String(pin) }, 'chat_pinned', { wxid: wxid, pin: pin }); }, // ==================== H31 搜索 ==================== globalSearch: function (params) { var keyword = (params && params.keyword) || ''; var limit = (params && params.limit) || 30; if (!keyword) return { success: false, error: '缺少 keyword' }; try { return Java.performNow(function () { var kw = keyword.replace(/'/g, "''"); var contactSql = "SELECT username, nickname, conRemark FROM rcontact WHERE (nickname LIKE '%" + kw + "%' OR conRemark LIKE '%" + kw + "%') AND type NOT IN (0,33) AND verifyFlag=0 LIMIT " + limit; var contacts = _execSQL(contactSql); var msgSql = "SELECT msgId, talker, content, createTime FROM message WHERE content LIKE '%" + kw + "%' ORDER BY createTime DESC LIMIT " + limit; var messages = _execSQL(msgSql); return { success: true, contacts: contacts.map(function (r) { return { wxid: r.username, nickname: r.nickname, remark: r.conRemark }; }), messages: messages.map(function (r) { return { id: r.msgId, from_id: r.talker, content: r.content, timestamp: r.createTime }; }), }; }); } catch (e) { return { success: false, error: String(e) }; } }, // ==================== H32 小程序 ==================== openMiniProgram: function (params) { var appId = (params && params.app_id) || (params && params.name) || ''; var path = (params && params.path) || ''; if (!appId) return { success: false, error: '缺少 app_id' }; return _intentAction('com.workphone.OPEN_MINI_PROGRAM', { app_id: appId, path: path }, 'mini_program_opened', { app_id: appId }); }, getRecentMiniPrograms: function () { return _intentAction('com.workphone.GET_RECENT_MINI_PROGRAMS', {}, null, {}); }, shareMiniProgram: function (params) { var appId = (params && params.app_id) || ''; var toId = (params && params.to_id) || ''; var title = (params && params.title) || ''; if (!appId || !toId) return { success: false, error: '缺少 app_id 或 to_id' }; return _intentAction('com.workphone.SHARE_MINI_PROGRAM', { app_id: appId, to_id: toId, title: title }, 'mini_program_shared', { app_id: appId, to_id: toId }); }, // ==================== H33 文件传输 ==================== sendImage: function (params) { var toId = (params && params.to_id) || ''; var imagePath = (params && params.image_path) || ''; if (!toId || !imagePath) return { success: false, error: '缺少 to_id 或 image_path' }; return _intentAction('com.workphone.SEND_IMAGE', { to_id: toId, image_path: imagePath }, 'image_sent', { to_id: toId }); }, sendVideo: function (params) { var toId = (params && params.to_id) || ''; var videoPath = (params && params.video_path) || ''; if (!toId || !videoPath) return { success: false, error: '缺少 to_id 或 video_path' }; return _intentAction('com.workphone.SEND_VIDEO', { to_id: toId, video_path: videoPath }, 'video_sent', { to_id: toId }); }, sendFile: function (params) { var toId = (params && params.to_id) || ''; var filePath = (params && params.file_path) || ''; if (!toId || !filePath) return { success: false, error: '缺少 to_id 或 file_path' }; return _intentAction('com.workphone.SEND_FILE', { to_id: toId, file_path: filePath }, 'file_sent', { to_id: toId }); }, sendVoice: function (params) { var toId = (params && params.to_id) || (params && params.user_id) || ''; var voicePath = (params && params.voice_path) || ''; var duration = (params && params.duration) || 0; if (!toId) return { success: false, error: '缺少 to_id' }; if (!voicePath && duration) { return _intentAction('com.workphone.SEND_VOICE_RECORD', { to_id: toId, duration: String(duration) }, 'voice_sent', { to_id: toId, duration: duration }); } if (!voicePath) return { success: false, error: '缺少 voice_path' }; return _intentAction('com.workphone.SEND_VOICE', { to_id: toId, voice_path: voicePath, duration: String(duration) }, 'voice_sent', { to_id: toId }); }, sendLocation: function (params) { var toId = (params && params.to_id) || ''; var lat = (params && params.latitude) || ''; var lng = (params && params.longitude) || ''; var label = (params && params.label) || ''; if (!toId || !lat || !lng) return { success: false, error: '缺少 to_id/latitude/longitude' }; return _intentAction('com.workphone.SEND_LOCATION', { to_id: toId, latitude: lat, longitude: lng, label: label }, 'location_sent', { to_id: toId }); }, sendCard: function (params) { var toId = (params && params.to_id) || ''; var cardWxid = (params && params.card_wxid) || ''; if (!toId || !cardWxid) return { success: false, error: '缺少 to_id 或 card_wxid' }; return _intentAction('com.workphone.SEND_CARD', { to_id: toId, card_wxid: cardWxid }, 'card_sent', { to_id: toId, card_wxid: cardWxid }); }, sendLink: function (params) { var toId = (params && params.to_id) || ''; var url = (params && params.url) || ''; var title = (params && params.title) || ''; var desc = (params && params.description) || ''; if (!toId || !url) return { success: false, error: '缺少 to_id 或 url' }; return _intentAction('com.workphone.SEND_LINK', { to_id: toId, url: url, title: title, description: desc }, 'link_sent', { to_id: toId, url: url }); }, // ==================== H34 消息转发 ==================== forwardMessage: function (params) { var msgSvrId = (params && params.msg_svr_id) || ''; var toId = (params && params.to_id) || ''; if (!msgSvrId || !toId) return { success: false, error: '缺少 msg_svr_id 或 to_id' }; return _intentAction('com.workphone.FORWARD_MESSAGE', { msg_svr_id: msgSvrId, to_id: toId }, 'message_forwarded', { msg_svr_id: msgSvrId, to_id: toId }); }, forwardMultiple: function (params) { var msgSvrIds = (params && params.msg_svr_ids) || []; var toId = (params && params.to_id) || ''; if (msgSvrIds.length === 0 || !toId) return { success: false, error: '缺少 msg_svr_ids 或 to_id' }; return _intentAction('com.workphone.FORWARD_MULTIPLE', { msg_svr_ids: msgSvrIds.join(','), to_id: toId }, 'messages_forwarded', { count: msgSvrIds.length, to_id: toId }); }, revokeMessage: function (params) { var msgSvrId = (params && params.msg_svr_id) || ''; if (!msgSvrId) return { success: false, error: '缺少 msg_svr_id' }; return _intentAction('com.workphone.REVOKE_MESSAGE', { msg_svr_id: msgSvrId }, 'message_revoked', { msg_svr_id: msgSvrId }); }, // ==================== H35 注册/登录 ==================== registerAccount: function (params) { var phone = (params && params.phone) || ''; var nickname = (params && params.nickname) || ''; var password = (params && params.password) || ''; if (!phone) return { success: false, error: '缺少 phone' }; return _intentAction('com.workphone.REGISTER', { phone: phone, nickname: nickname, password: password }, 'register_started', { phone: phone }); }, loginByPassword: function (params) { var phone = (params && params.phone) || ''; var password = (params && params.password) || ''; if (!phone || !password) return { success: false, error: '缺少 phone 或 password' }; return _intentAction('com.workphone.LOGIN_PASSWORD', { phone: phone, password: password }, 'login_started', { phone: phone }); }, loginBySms: function (params) { var phone = (params && params.phone) || ''; if (!phone) return { success: false, error: '缺少 phone' }; return _intentAction('com.workphone.LOGIN_SMS', { phone: phone }, 'sms_login_started', { phone: phone }); }, logout: function () { return _intentAction('com.workphone.LOGOUT', {}, 'logout_started', {}); }, switchAccount: function (params) { var wxid = (params && params.wxid) || ''; return _intentAction('com.workphone.SWITCH_ACCOUNT', { wxid: wxid }, 'account_switched', { wxid: wxid || 'picker' }); }, voiceCall: function (params) { var uid = (params && (params.user_id || params.to_id)) || ''; return _intentAction('com.workphone.VOICE_CALL', { user_id: uid }, 'voice_call', { user_id: uid }); }, videoCall: function (params) { var uid = (params && (params.user_id || params.to_id)) || ''; return _intentAction('com.workphone.VIDEO_CALL', { user_id: uid }, 'video_call', { user_id: uid }); }, shareRealTimeLocation: function (params) { var uid = (params && (params.user_id || params.to_id)) || ''; return _intentAction('com.workphone.SHARE_RT_LOCATION', { user_id: uid }, 'rt_location', { user_id: uid }); }, downloadFile: function (params) { var uid = (params && (params.user_id || params.to_id)) || ''; return _intentAction('com.workphone.DOWNLOAD_FILE', { user_id: uid }, 'file_downloaded', { user_id: uid }); }, massSend: function (params) { var ids = (params && params.user_ids) || []; var content = (params && params.content) || ''; if (!content || !ids.length) return { success: false, error: '缺少 content 或 user_ids' }; return _intentAction('com.workphone.MASS_SEND', { user_ids: ids.join(','), content: content }, 'mass_send', { count: ids.length }); }, replyComment: function (params) { return _intentAction('com.workphone.REPLY_COMMENT', { video_id: (params && params.video_id) || '', comment_id: (params && params.comment_id) || '', content: (params && params.content) || '', }, 'reply_comment', params || {}); }, receivePayment: function (params) { return _intentAction('com.workphone.RECEIVE_PAYMENT', { amount: (params && params.amount) || '', desc: (params && params.desc) || '', }, 'payment_received', params || {}); }, autoRegister: function (params) { var phone = (params && params.phone) || ''; var nickname = (params && params.nickname) || '卡若AI'; var password = (params && params.password) || ''; return _intentAction('com.workphone.AUTO_REGISTER', { phone: phone, nickname: nickname, password: password }, 'auto_register_started', { phone: phone, nickname: nickname }); }, checkLoginState: function () { try { return Java.performNow(function () { var ctx = getCtx(); var am = ctx.getSystemService(jStr('activity')); var sql = "SELECT value FROM userinfo WHERE id=2"; var rows = _execSQL(sql); var hasUser = rows.length > 0 && rows[0].value && rows[0].value.length > 0; return { success: true, logged_in: hasUser, nickname: hasUser ? rows[0].value : '', state: hasUser ? 'logged_in' : 'not_logged_in', }; }); } catch (e) { return { success: false, logged_in: false, state: 'unknown', error: String(e) }; } }, getSimPhone: function () { try { return Java.performNow(function () { var ctx = getCtx(); var TelephonyManager = Java.use('android.telephony.TelephonyManager'); var tm = Java.cast(ctx.getSystemService(jStr('phone')), TelephonyManager); var line1 = ''; try { line1 = safeStr(tm.getLine1Number()); } catch (_) {} var simOp = ''; try { simOp = safeStr(tm.getSimOperatorName()); } catch (_) {} var simState = -1; try { simState = tm.getSimState(); } catch (_) {} return { success: true, phone: line1, operator: simOp, sim_state: simState, found: line1.length >= 11, }; }); } catch (e) { return { success: false, phone: '', error: String(e) }; } }, // ==================== H36 公众号 ==================== getOfficialAccounts: function (params) { var limit = (params && params.limit) || 100; try { return Java.performNow(function () { var sql = "SELECT username, nickname, conRemark FROM rcontact WHERE username LIKE 'gh_%' ORDER BY nickname LIMIT " + limit; var rows = _execSQL(sql); return { success: true, accounts: rows.map(function (r) { return { wxid: r.username, name: r.nickname, remark: r.conRemark }; }), count: rows.length }; }); } catch (e) { return { success: false, error: String(e), accounts: [] }; } }, followOfficialAccount: function (params) { var accountId = (params && params.account_id) || ''; if (!accountId) return { success: false, error: '缺少 account_id' }; return _intentAction('com.workphone.FOLLOW_OA', { account_id: accountId }, 'oa_followed', { account_id: accountId }); }, unfollowOfficialAccount: function (params) { var accountId = (params && params.account_id) || ''; if (!accountId) return { success: false, error: '缺少 account_id' }; return _intentAction('com.workphone.UNFOLLOW_OA', { account_id: accountId }, 'oa_unfollowed', { account_id: accountId }); }, getOfficialAccountArticles: function (params) { var accountId = (params && params.account_id) || ''; var limit = (params && params.limit) || 10; if (!accountId) return { success: false, error: '缺少 account_id' }; return _intentAction('com.workphone.GET_OA_ARTICLES', { account_id: accountId, limit: String(limit) }, null, {}); }, // ==================== H37 表情管理 ==================== sendEmoji: function (params) { var toId = (params && params.to_id) || (params && params.user_id) || ''; var emojiMd5 = (params && params.emoji_md5) || (params && params.emoji_name) || ''; if (!toId || !emojiMd5) return { success: false, error: '缺少 to_id 或 emoji_md5' }; return _intentAction('com.workphone.SEND_EMOJI', { to_id: toId, emoji_md5: emojiMd5 }, 'emoji_sent', { to_id: toId }); }, addCustomEmoji: function (params) { var imagePath = (params && params.image_path) || ''; if (!imagePath) return { success: false, error: '缺少 image_path' }; return _intentAction('com.workphone.ADD_EMOJI', { image_path: imagePath }, 'emoji_added', {}); }, // ==================== H38 浮窗/多任务 ==================== addToFloat: function (params) { var wxid = (params && params.wxid) || ''; if (!wxid) return { success: false, error: '缺少 wxid' }; return _intentAction('com.workphone.ADD_FLOAT', { wxid: wxid }, 'float_added', { wxid: wxid }); }, removeFromFloat: function (params) { var wxid = (params && params.wxid) || ''; if (!wxid) return { success: false, error: '缺少 wxid' }; return _intentAction('com.workphone.REMOVE_FLOAT', { wxid: wxid }, 'float_removed', { wxid: wxid }); }, // ==================== H39 设备信息 ==================== getDeviceInfo: function () { try { return Java.performNow(function () { var Build = Java.use('android.os.Build'); var VERSION = Java.use('android.os.Build$VERSION'); return { success: true, device: { model: safeStr(Build.MODEL.value), brand: safeStr(Build.BRAND.value), manufacturer: safeStr(Build.MANUFACTURER.value), android_version: safeStr(VERSION.RELEASE.value), sdk_int: VERSION.SDK_INT.value, device: safeStr(Build.DEVICE.value), product: safeStr(Build.PRODUCT.value), fingerprint: safeStr(Build.FINGERPRINT.value), display: safeStr(Build.DISPLAY.value), }, }; }); } catch (e) { return { success: false, error: String(e) }; } }, getStorageInfo: function () { try { return Java.performNow(function () { var StatFs = Java.use('android.os.StatFs'); var Environment = Java.use('android.os.Environment'); var stat = StatFs.$new(Environment.getDataDirectory().getPath()); var totalBytes = stat.getTotalBytes(); var freeBytes = stat.getFreeBytes(); return { success: true, storage: { total_mb: Math.round(totalBytes / 1048576), free_mb: Math.round(freeBytes / 1048576), used_percent: Math.round((1 - freeBytes / totalBytes) * 100) }, }; }); } catch (e) { return { success: false, error: String(e) }; } }, getNetworkInfo: function () { try { return Java.performNow(function () { var ctx = getCtx(); var cm = ctx.getSystemService(jStr('connectivity')); var ConnectivityManager = Java.use('android.net.ConnectivityManager'); var mgr = Java.cast(cm, ConnectivityManager); var activeNet = mgr.getActiveNetworkInfo(); if (activeNet && activeNet.isConnected()) { return { success: true, network: { type: safeStr(activeNet.getTypeName()), connected: true, detail: safeStr(activeNet.getExtraInfo()) } }; } return { success: true, network: { type: 'none', connected: false } }; }); } catch (e) { return { success: false, error: String(e) }; } }, // ==================== 批量执行 ==================== batchExecute: function (params) { var actions = (params && params.actions) || []; if (actions.length === 0) return { success: false, error: '缺少 actions' }; var results = []; for (var i = 0; i < actions.length; i++) { var act = actions[i]; var fn = rpc.exports[act.action]; if (fn) { try { results.push({ action: act.action, result: fn(act.params || {}) }); } catch (e) { results.push({ action: act.action, result: { success: false, error: String(e) } }); } } else { results.push({ action: act.action, result: { success: false, error: '未知操作' } }); } } return { success: true, results: results, count: results.length }; }, }; // ============================================================ // § 5 内部实现 — 消息发送 (H17) // ============================================================ function _sendMessageInternal(toId, content, msgType) { var sent = false; // 方案 A: 直接调用微信内部消息发送类(按版本兼容表自动选择) var candidateClasses = getCompat('send_message'); for (var i = 0; i < candidateClasses.length && !sent; i++) { try { var c = candidateClasses[i]; var MsgCls = Java.use(c.cls); if (MsgCls) { var m = c.sig ? MsgCls[c.method].overload.apply(MsgCls[c.method], c.sig) : MsgCls[c.method]; if (c.sig && c.sig.length === 3) { m.call(MsgCls, jStr(toId), jStr(content), jStr(msgType === 'text' ? '' : msgType)); } else { m.call(MsgCls, jStr(toId), jStr(content)); } sent = true; log('info', 'send', '消息通过内部方法发送: ' + c.cls); emitEvent('message_sent', { to_id: toId, content: content, msg_type: msgType, method: 'internal_' + c.cls.split('.').pop() }); } } catch (_) { continue; } } // 方案 B: Intent 广播降级 if (!sent) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.SEND_MESSAGE'); intent.putExtra('to_id', toId); intent.putExtra('content', content); intent.putExtra('msg_type', msgType); ctx.sendBroadcast(intent); sent = true; log('info', 'send', '消息通过 Intent 广播发送'); emitEvent('message_sent', { to_id: toId, content: content, msg_type: msgType, method: 'intent_broadcast' }); } catch (e) { log('error', 'send', 'Intent 发送也失败: ' + e); } } var messageId = sent ? generateId('msg') : null; if (sent) { var talker = (toId === '文件传输助手' || toId === 'File Transfer') ? 'filehelper' : toId; _messageBuffer.push({ msg_svr_id: messageId, msg_type: msgType || 'text', from_id: talker, content: content, timestamp: new Date().toISOString(), is_send: true, }); if (_messageBuffer.length > CONFIG.MESSAGE_BUFFER_MAX) { _messageBuffer = _messageBuffer.slice(-Math.floor(CONFIG.MESSAGE_BUFFER_MAX * 0.6)); } } return { success: sent, message_id: messageId, method: sent ? 'hook' : 'failed', to_id: toId, }; } // ============================================================ // § 6 内部实现 — 数据库读取 (H15/H16) // ============================================================ function _getMessagesFromDB(conversationId, limit) { // DB talker 用 wxid;显示名「文件传输助手」→ filehelper if (conversationId === '文件传输助手' || conversationId === 'File Transfer') { conversationId = 'filehelper'; } var where = conversationId ? " WHERE talker='" + conversationId.replace(/'/g, "''") + "'" : ''; var sql = 'SELECT msgId, type, talker, content, createTime, isSend FROM message' + where + ' ORDER BY createTime DESC LIMIT ' + limit; var rows = _execSQL(sql); return { success: true, messages: rows.map(function (r) { return { id: r.msgId, type: r.type, from_id: r.talker, content: r.content, timestamp: r.createTime, is_send: r.isSend === '1', }; }), count: rows.length, }; } function _getContactsFromDB(limit) { limit = limit || 200; var labelMap = {}; try { var labelRows = _execSQL("SELECT labelId, labelName FROM contactlabelinfo"); labelRows.forEach(function (r) { labelMap[r.labelId] = r.labelName; }); } catch (e) { /* 部分版本无标签表 */ } var contactLabelMap = {}; try { // 8.0.69 列名为 contactLabelIds(旧版 labelidlist) var clRows = _execSQL( "SELECT username, contactLabelIds FROM rcontact WHERE contactLabelIds IS NOT NULL AND contactLabelIds != ''" ); clRows.forEach(function (r) { if (!r.contactLabelIds) return; var ids = String(r.contactLabelIds).split(",").filter(function (id) { return id; }); contactLabelMap[r.username] = ids.map(function (id) { return labelMap[id] || id; }).filter(function (n) { return n; }); }); } catch (e) { /* ignore */ } // 8.0.69 兼容:先探测 rcontact 实际列,只 SELECT 存在的列(避免缺列如 signature/country 致整查询返 0) var _wantCols = ["username", "nickname", "conRemark", "type", "alias", "imgFlag", "sex", "country", "province", "city", "signature"]; var _availCols = {}; try { var _ti = _execSQL("PRAGMA table_info(rcontact)"); (_ti || []).forEach(function (c) { if (c && c.name) _availCols[c.name] = true; }); } catch (ept) { /* PRAGMA 不支持时退回全列 */ } var _useCols = Object.keys(_availCols).length > 0 ? _wantCols.filter(function (c) { return _availCols[c]; }) : _wantCols; if (_useCols.indexOf("username") < 0) _useCols.unshift("username"); var _cols = "SELECT " + _useCols.join(", ") + " FROM rcontact "; var _excludeSys = "username NOT LIKE '%@chatroom' AND username NOT LIKE 'gh_%' " + "AND username NOT IN ('filehelper','fmessage','medianote','floatbottle','newsapp','weixin','qmessage','tmessage') "; var sql = _cols + "WHERE type NOT IN (0, 33) AND verifyFlag=0 AND " + _excludeSys + "ORDER BY conRemark, nickname LIMIT " + limit; var rows = _execSQL(sql); var _diag = { strict: (rows || []).length, use_cols: _useCols, avail_cols: Object.keys(_availCols) }; // 8.0.69 兼容回退①:严格过滤(type/verifyFlag)为空时,放宽为仅排除系统/群/公众号 if (!rows || rows.length === 0) { var sqlRelax = _cols + "WHERE verifyFlag=0 AND " + _excludeSys + "ORDER BY conRemark, nickname LIMIT " + limit; var relaxed = _execSQL(sqlRelax); _diag.relax_verifyflag = (relaxed || []).length; if (relaxed && relaxed.length > 0) { log('info', 'contacts', '严格过滤空,回退宽过滤命中 ' + relaxed.length + ' 条'); rows = relaxed; } } // 8.0.69 兼容回退②:连 verifyFlag=0 也空时,丢弃 verifyFlag,仅排除系统/群/公众号(部分版本好友 verifyFlag!=0) if (!rows || rows.length === 0) { var sqlNoVerify = _cols + "WHERE " + _excludeSys + "ORDER BY conRemark, nickname LIMIT " + limit; var noverify = _execSQL(sqlNoVerify); _diag.relax_noverify = (noverify || []).length; if (noverify && noverify.length > 0) { log('info', 'contacts', 'verifyFlag 过滤空,丢弃 verifyFlag 命中 ' + noverify.length + ' 条'); rows = noverify; } } // 诊断:rcontact 原始总行数 + 字段探测(定位 8.0.69 读空根因,写入返回的 _diag) if (!rows || rows.length === 0) { try { var rawCnt = _execSQL("SELECT count(*) AS n FROM rcontact"); _diag.rcontact_total = rawCnt && rawCnt[0] ? rawCnt[0].n : null; } catch (e0) { _diag.rcontact_total_err = String(e0); } try { var vf = _execSQL("SELECT verifyFlag, count(*) AS n FROM rcontact GROUP BY verifyFlag"); _diag.verifyflag_dist = vf; } catch (e1) { _diag.verifyflag_err = String(e1); } try { var tp = _execSQL("SELECT type, count(*) AS n FROM rcontact GROUP BY type LIMIT 20"); _diag.type_dist = tp; } catch (e2) { _diag.type_err = String(e2); } } var sexMap = { "0": "unknown", "1": "male", "2": "female" }; var contacts = rows.map(function (r) { var nick = r.nickname || ""; var remark = r.conRemark || ""; var alias = r.alias || ""; var wxid = r.username || ""; var display = nick || remark || alias || wxid; return { wxid: wxid, user_id: wxid, nickname: nick, display_name: display, remark: remark, alias: alias, wechat_id: alias, type: r.type, sex: sexMap[String(r.sex)] || "unknown", country: r.country || "", province: r.province || "", city: r.city || "", signature: r.signature || "", tags: contactLabelMap[wxid] || [], has_avatar: (r.imgFlag || 0) > 0, }; }); _contactCache = contacts; return { success: true, contacts: contacts, count: contacts.length, _diag: _diag }; } function _getContactInfoFromDB(wxid) { var sql = "SELECT username, nickname, conRemark, type, alias FROM rcontact" + " WHERE username='" + wxid.replace(/'/g, "''") + "'"; var rows = _execSQL(sql); if (rows.length > 0) return { success: true, contact: rows[0] }; return { success: false, error: '未找到联系人: ' + wxid }; } // ============================================================ // § 7 内部实现 — 好友管理 (H18/H19) // ============================================================ function _getFriendRequestsFromDB(limit) { var sql = "SELECT msgId, type, talker, content, createTime FROM message" + " WHERE type = 10002" + " ORDER BY createTime DESC LIMIT " + limit; var rows = _execSQL(sql); var requests = rows.map(function (r) { var info = _parseFriendRequestXml(r.content); return { msg_id: r.msgId, from_id: info.fromusername || r.talker, from_nickname: info.fromnickname || '', content: info.content || '', encrypt_username: info.encryptusername || '', ticket: info.ticket || '', source: info.sourceType || '', timestamp: r.createTime, }; }); return { success: true, requests: requests, count: requests.length }; } function _parseFriendRequestXml(xml) { var info = {}; if (!xml) return info; var fields = ['fromusername', 'fromnickname', 'content', 'encryptusername', 'ticket', 'sourceType']; for (var i = 0; i < fields.length; i++) { var re = new RegExp(fields[i] + '="([^"]*)"'); var m = xml.match(re); if (m) info[fields[i]] = m[1]; } return info; } function _addFriendInternal(userId, message, sourceType) { // 通过微信内部 AddContactNetScene var candidates = [ 'com.tencent.mm.plugin.profile.ui.ContactInfoUI', 'com.tencent.mm.protocal.protobuf.add$ContactType', ]; // 降级: Intent 方式 try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.ADD_FRIEND'); intent.putExtra('user_id', userId); intent.putExtra('message', message); intent.putExtra('source_type', String(sourceType)); ctx.sendBroadcast(intent); log('info', 'friend', '添加好友请求已发送: ' + userId); emitEvent('friend_request_sent', { user_id: userId, message: message, source_type: sourceType }); return { success: true, user_id: userId, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '添加好友失败: ' + e, user_id: userId }; } } function _acceptFriendInternal(encryptUserName, ticket) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.ACCEPT_FRIEND'); intent.putExtra('encrypt_username', encryptUserName); intent.putExtra('ticket', ticket); ctx.sendBroadcast(intent); log('info', 'friend', '通过好友请求: ' + encryptUserName); emitEvent('friend_accepted', { encrypt_username: encryptUserName }); return { success: true, encrypt_username: encryptUserName, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '通过好友失败: ' + e }; } } function _deleteFriendInternal(wxid) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.DELETE_FRIEND'); intent.putExtra('wxid', wxid); ctx.sendBroadcast(intent); log('info', 'friend', '删除好友: ' + wxid); emitEvent('friend_deleted', { wxid: wxid }); return { success: true, wxid: wxid, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '删除好友失败: ' + e }; } } function _setRemarkInternal(wxid, remark) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.SET_REMARK'); intent.putExtra('wxid', wxid); intent.putExtra('remark', remark); ctx.sendBroadcast(intent); return { success: true, wxid: wxid, remark: remark, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '设置备注失败: ' + e }; } } // ============================================================ // § 8 内部实现 — 群管理 (H22) // ============================================================ function _getGroupsFromDB(limit) { var sql = "SELECT username, nickname FROM rcontact" + " WHERE username LIKE '%@chatroom'" + " ORDER BY nickname LIMIT " + limit; var rows = _execSQL(sql); return { success: true, groups: rows.map(function (r) { return { group_id: r.username, name: r.nickname }; }), count: rows.length, }; } function _getGroupMembersFromDB(groupId) { var sql = "SELECT memberlist, displayname FROM chatroom" + " WHERE chatroomname='" + groupId.replace(/'/g, "''") + "'"; var rows = _execSQL(sql); if (rows.length === 0) return { success: false, error: '群不存在: ' + groupId, members: [] }; var memberList = (rows[0].memberlist || '').split(';').filter(function (m) { return m; }); var displayNames = (rows[0].displayname || '').split(';'); var members = memberList.map(function (wxid, idx) { return { wxid: wxid, display_name: (displayNames[idx] || '').trim(), }; }); return { success: true, group_id: groupId, members: members, count: members.length }; } function _getGroupInfoFromDB(groupId) { var sql = "SELECT chatroomname, memberlist, displayname, roomowner, chatroomnick FROM chatroom" + " WHERE chatroomname='" + groupId.replace(/'/g, "''") + "'"; var rows = _execSQL(sql); if (rows.length === 0) return { success: false, error: '群不存在: ' + groupId }; var r = rows[0]; var memberList = (r.memberlist || '').split(';').filter(function (m) { return m; }); return { success: true, group: { group_id: r.chatroomname, name: r.chatroomnick || '', owner: r.roomowner || '', member_count: memberList.length, members: memberList, }, }; } function _createGroupInternal(memberIds, topic) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.CREATE_GROUP'); intent.putExtra('member_ids', memberIds.join(',')); if (topic) intent.putExtra('topic', topic); ctx.sendBroadcast(intent); log('info', 'group', '创建群聊: members=' + memberIds.length); emitEvent('group_created', { member_ids: memberIds, topic: topic }); return { success: true, member_ids: memberIds, topic: topic, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '创建群失败: ' + e }; } } function _inviteToGroupInternal(groupId, memberIds) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.INVITE_GROUP'); intent.putExtra('group_id', groupId); intent.putExtra('member_ids', memberIds.join(',')); ctx.sendBroadcast(intent); log('info', 'group', '邀请入群: ' + groupId + ' members=' + memberIds.length); emitEvent('group_invite', { group_id: groupId, member_ids: memberIds }); return { success: true, group_id: groupId, member_ids: memberIds, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '邀请入群失败: ' + e }; } } function _removeFromGroupInternal(groupId, memberIds) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.REMOVE_GROUP_MEMBER'); intent.putExtra('group_id', groupId); intent.putExtra('member_ids', memberIds.join(',')); ctx.sendBroadcast(intent); log('info', 'group', '踢出群: ' + groupId); emitEvent('group_member_removed', { group_id: groupId, member_ids: memberIds }); return { success: true, group_id: groupId, member_ids: memberIds, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '踢人失败: ' + e }; } } function _setGroupAnnouncementInternal(groupId, announcement) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.SET_GROUP_ANNOUNCEMENT'); intent.putExtra('group_id', groupId); intent.putExtra('announcement', announcement); ctx.sendBroadcast(intent); return { success: true, group_id: groupId, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '设置群公告失败: ' + e }; } } function _setGroupNameInternal(groupId, name) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.SET_GROUP_NAME'); intent.putExtra('group_id', groupId); intent.putExtra('name', name); ctx.sendBroadcast(intent); return { success: true, group_id: groupId, name: name, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '修改群名失败: ' + e }; } } function _quitGroupInternal(groupId) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.QUIT_GROUP'); intent.putExtra('group_id', groupId); ctx.sendBroadcast(intent); emitEvent('group_quit', { group_id: groupId }); return { success: true, group_id: groupId, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '退群失败: ' + e }; } } // ============================================================ // § 9 内部实现 — 朋友圈 (H20/H21) // ============================================================ // 取当前栈顶 Activity(ActivityThread.mActivities 遍历 paused=false) function _getTopActivity() { var top = null; try { var ActivityThread = Java.use('android.app.ActivityThread'); var at = ActivityThread.currentActivityThread(); var mActivities = at.mActivities.value; // ArrayMap var vals = mActivities.values().toArray(); for (var i = 0; i < vals.length; i++) { var rec = Java.cast(vals[i], Java.use('android.app.ActivityThread$ActivityClientRecord')); try { if (!rec.paused.value) { top = rec.activity.value; break; } } catch (_) {} } } catch (e) { log('warn', 'sns', '_getTopActivity 失败: ' + e); } return top; } // 递归遍历 view 树,找文本匹配且可点击的 View 并 performClick function _clickViewByText(root, texts) { if (!root) return false; try { var TextView = Java.use('android.widget.TextView'); var ViewGroup = Java.use('android.view.ViewGroup'); // 当前节点是 TextView 且文本命中 try { var asTv = Java.cast(root, TextView); var t = asTv.getText(); var s = t ? t.toString() : ''; for (var k = 0; k < texts.length; k++) { if (s.indexOf(texts[k]) >= 0) { var clickTarget = root; // 向上找可点击祖先(按钮文字常包在可点击容器内) var p = root; for (var up = 0; up < 4 && p; up++) { if (p.isClickable()) { clickTarget = p; break; } p = p.getParent(); } if (clickTarget && clickTarget.performClick) { clickTarget.performClick(); log('info', 'sns', '点击发表按钮: "' + s + '"'); return true; } } } } catch (_) {} // 递归子节点 try { var vg = Java.cast(root, ViewGroup); var n = vg.getChildCount(); for (var c = 0; c < n; c++) { if (_clickViewByText(vg.getChildAt(c), texts)) return true; } } catch (_) {} } catch (_) {} return false; } // 朋友圈发布界面打开后,自动点「发表」(best-effort,失败由 need_confirm_send 兜底) function _autoClickPublish(delayMs) { try { var Handler = Java.use('android.os.Handler'); var Looper = Java.use('android.os.Looper'); var handler = Handler.$new(Looper.getMainLooper()); var Runnable = Java.use('java.lang.Runnable'); var R = Java.registerClass({ name: 'com.workphone.PublishClicker' + Date.now(), implements: [Runnable], methods: { run: function () { try { var act = _getTopActivity(); if (!act) { log('warn', 'sns', '自动发表:无栈顶 Activity'); return; } var decor = act.getWindow().getDecorView(); var ok = _clickViewByText(decor, ['发表', '完成', 'Post', 'Send']); emitEvent('moments_publish_click', { clicked: ok }); log('info', 'sns', '自动发表点击结果: ' + ok); } catch (e) { log('warn', 'sns', '自动发表执行失败: ' + e); } } } }); handler.postDelayed(R.$new(), delayMs || 2000); return true; } catch (e) { log('warn', 'sns', '_autoClickPublish 调度失败: ' + e); return false; } } // 真发布:启动微信朋友圈发布 Activity(SnsUploadUI)并预填内容 // 替代旧的 com.workphone.POST_MOMENTS 广播(无接收端=占位)。 // 纯文字朋友圈:Ksnsupload_type=9(社区已知),文本 extra 多 key 兜底。 function _postMomentsInternal(content, imageUrls, autoSend) { imageUrls = imageUrls || []; if (autoSend === undefined) autoSend = true; var candidates = getCompat('sns_upload'); // [{cls:'...SnsUploadUI'}, ...] var lastErr = ''; for (var i = 0; i < candidates.length; i++) { var clsName = candidates[i].cls; try { var Intent = Java.use('android.content.Intent'); var ComponentName = Java.use('android.content.ComponentName'); var ctx = getCtx(); var SnsCls = Java.use(clsName); // 校验类存在 var intent = Intent.$new(); intent.setComponent(ComponentName.$new('com.tencent.mm', clsName)); intent.addFlags(0x10000000); // FLAG_ACTIVITY_NEW_TASK // 纯文字:type=9;图文:type=1(含 imglist) var isText = imageUrls.length === 0; intent.putExtra('Ksnsupload_type', isText ? 9 : 1); // 文本 extra 多 key 兜底(不同版本 key 名不同) intent.putExtra('Ksnsupload_text', content); intent.putExtra('sns_text', content); intent.putExtra('Ksns_upload_text', content); if (!isText) { intent.putExtra('Ksnsupload_imglist', imageUrls.join(',')); } ctx.startActivity(intent); log('info', 'sns', '启动 SnsUploadUI(' + clsName + ') content_len=' + content.length + ' images=' + imageUrls.length); emitEvent('moments_post_ui_opened', { content: content, image_count: imageUrls.length, cls: clsName }); // 打开发布界面+预填后,best-effort 自动点「发表」(失败由 need_confirm_send 兜底) var scheduled = autoSend ? _autoClickPublish(2200) : false; return { success: true, method: 'sns_upload_ui', cls: clsName, auto_click_scheduled: scheduled, need_confirm_send: !scheduled, note: scheduled ? '已打开朋友圈发布界面并预填,2.2s 后自动点发表' : '已打开发布界面并预填,待点发表', sns_id: generateId('sns'), }; } catch (e) { lastErr = String(e); } } return { success: false, error: '发朋友圈失败(所有 SnsUploadUI 候选): ' + lastErr }; } function _getMomentsFromDB(wxid, limit) { var snsPath = _getSnsDbPath(); if (!snsPath) { return { success: false, error: 'SnsMicroMsg.db 未找到', moments: [] }; } var where = wxid ? " WHERE userName='" + wxid.replace(/'/g, "''") + "'" : ''; // 8.0.69 兼容:探测 SnsInfo 实际列,只 SELECT 存在的列(避免 mediaCount/likeCount/commentCount 缺列致整查询返 0) var _wantSns = ["snsId", "userName", "createTime", "content", "mediaCount", "likeCount", "commentCount", "type", "sourceType"]; var _availSns = {}; var _snsDiag = {}; try { var _sti = _execSnsSQL("PRAGMA table_info(SnsInfo)"); (_sti || []).forEach(function (c) { if (c && c.name) _availSns[c.name] = true; }); _snsDiag.avail_cols = Object.keys(_availSns); } catch (eps) { _snsDiag.pragma_err = String(eps); } var _useSns = Object.keys(_availSns).length > 0 ? _wantSns.filter(function (c) { return _availSns[c]; }) : _wantSns; if (_useSns.indexOf("snsId") < 0) _useSns.unshift("snsId"); _snsDiag.use_cols = _useSns; var sql = "SELECT " + _useSns.join(", ") + " FROM SnsInfo" + where + " ORDER BY createTime DESC LIMIT " + limit; var rows = _execSnsSQL(sql); _snsDiag.rows = (rows || []).length; if (!rows || rows.length === 0) { try { var sc = _execSnsSQL("SELECT count(*) AS n FROM SnsInfo"); _snsDiag.snsinfo_total = sc && sc[0] ? sc[0].n : null; } catch (e0) { _snsDiag.total_err = String(e0); } var hasSnsRows = safeInt(_snsDiag.snsinfo_total, 0) > 0; var emptyMoments = _emptyReadFailure('朋友圈', _lastDbDiag, hasSnsRows); if (emptyMoments) { emptyMoments.moments = []; emptyMoments._diag = _snsDiag; return emptyMoments; } } return { success: true, moments: (rows || []).map(function (r) { var parsed = _parseSnsContent(r.content); return { sns_id: r.snsId, user_id: r.userName, text: parsed.text, media_count: safeInt(r.mediaCount, 0), like_count: safeInt(r.likeCount, 0), comment_count: safeInt(r.commentCount, 0), timestamp: r.createTime, }; }), count: (rows || []).length, _diag: _snsDiag, }; } function _parseSnsContent(raw) { if (!raw) return { text: '' }; try { var idx = raw.indexOf(''); var endIdx = raw.indexOf(''); if (idx >= 0 && endIdx > idx) { return { text: raw.substring(idx + 13, endIdx) }; } } catch (_) {} return { text: safeStr(raw).substring(0, 200) }; } function _likeMomentsInternal(snsId) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.LIKE_MOMENTS'); intent.putExtra('sns_id', snsId); ctx.sendBroadcast(intent); emitEvent('moments_liked', { sns_id: snsId }); return { success: true, sns_id: snsId, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '点赞失败: ' + e }; } } function _commentMomentsInternal(snsId, comment) { try { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new('com.workphone.COMMENT_MOMENTS'); intent.putExtra('sns_id', snsId); intent.putExtra('comment', comment); ctx.sendBroadcast(intent); emitEvent('moments_commented', { sns_id: snsId, comment: comment }); return { success: true, sns_id: snsId, method: 'intent_broadcast' }; } catch (e) { return { success: false, error: '评论失败: ' + e }; } } // ============================================================ // § 10 通用 Intent 调度器(所有非 DB 操作统一走此) // ============================================================ function _intentAction(intentAction, extras, eventType, eventPayload) { try { return Java.performNow(function () { var Intent = Java.use('android.content.Intent'); var ctx = getCtx(); var intent = Intent.$new(intentAction); var keys = Object.keys(extras || {}); for (var i = 0; i < keys.length; i++) { intent.putExtra(keys[i], String(extras[keys[i]])); } // 真机铁律 #3/#14:先校验是否存在接收端,无接收端不得返回 success:true(禁止假成功) var receivers = 0; try { var pm = ctx.getPackageManager(); var list = pm.queryBroadcastReceivers(intent, 0); receivers = list ? list.size() : 0; } catch (qe) { receivers = -1; // 查询失败:未知,按未验证处理 } if (receivers === 0) { log('warn', 'intent', intentAction + ' 无接收端 → 不广播(避免假成功,需 _intentAction 真实现或装接收端 App)'); return { success: false, method: 'intent_broadcast', verified: false, error: 'no_receiver_registered', action: intentAction, hint: '该动作经 Android 广播分发,当前设备未注册接收端;需实现真实 Frida RPC 或部署接收端 App', }; } ctx.sendBroadcast(intent); if (eventType) { emitEvent(eventType, eventPayload || {}); } log('info', 'intent', intentAction + ' → ' + keys.join(',') + ' (receivers=' + receivers + ')'); return { success: true, method: 'intent_broadcast', verified: receivers > 0, receivers: receivers, action: intentAction, }; }); } catch (e) { return { success: false, error: String(e), action: intentAction }; } } // ============================================================ // § 11 Hook 初始化 // ============================================================ Java.perform(function () { log('info', 'init', 'wechat_hook_v3.1 正在初始化(110 actions / 24 modules / 版本自适应)...'); // 获取微信版本 + 初始化版本兼容 try { var ctx = getCtx(); var pm = ctx.getPackageManager(); var info = pm.getPackageInfo(jStr('com.tencent.mm'), 0); _wechatVersion = safeStr(info.versionName.value); log('info', 'init', '微信版本: ' + _wechatVersion); _resolveVersionCompat(_wechatVersion); } catch (_) { _versionCompat = _DEFAULT_COMPAT; log('warn', 'init', '无法获取微信版本,使用 default 全候选'); } // 查找数据库路径 _dbPath = _findDbPath(CONFIG.DB_NAME); _snsDbPath = _findDbPath(CONFIG.SNS_DB_NAME); log('info', 'init', 'EnMicroMsg.db: ' + (_dbPath || '未找到')); log('info', 'init', 'SnsMicroMsg.db: ' + (_snsDbPath || '未找到')); // ---- Hook 1: SQLite insert 拦截新消息 (H15) ---- if (CONFIG.HOOK_SQLITE) { try { var SQLiteDatabase = Java.use('android.database.sqlite.SQLiteDatabase'); SQLiteDatabase.insert.overload( 'java.lang.String', 'java.lang.String', 'android.content.ContentValues' ).implementation = function (table, nullColumnHack, values) { var result = this.insert(table, nullColumnHack, values); try { var tbl = safeStr(table); if (tbl === 'message') { var talker = safeStr(values.getAsString(jStr('talker'))); var content = safeStr(values.getAsString(jStr('content'))); var msgType = safeStr(values.getAsString(jStr('type'))); var msgSvrId = safeStr(values.getAsString(jStr('msgSvrId'))); var isSend = safeStr(values.getAsString(jStr('isSend'))); if (talker && content) { var msg = { from_id: talker, content: content, msg_type: msgType, msg_svr_id: msgSvrId, is_send: isSend === '1', timestamp: new Date().toISOString(), }; _messageBuffer.push(msg); if (_messageBuffer.length > CONFIG.MESSAGE_BUFFER_MAX) { _messageBuffer = _messageBuffer.slice(-Math.floor(CONFIG.MESSAGE_BUFFER_MAX * 0.6)); } emitEvent('message_received', msg); } // H18: 好友请求 type=10002 if (msgType === '10002') { var reqInfo = _parseFriendRequestXml(content); var req = { msg_id: msgSvrId, from_id: reqInfo.fromusername || talker, from_nickname: reqInfo.fromnickname || '', encrypt_username: reqInfo.encryptusername || '', ticket: reqInfo.ticket || '', content: reqInfo.content || '', timestamp: new Date().toISOString(), }; _friendRequestBuffer.push(req); if (_friendRequestBuffer.length > CONFIG.FRIEND_REQUEST_BUFFER_MAX) { _friendRequestBuffer = _friendRequestBuffer.slice(-60); } emitEvent('friend_request_received', req); log('info', 'hook', '检测到好友请求: ' + req.from_id); } } } catch (_) {} return result; }; _hooked.sqlite_insert = true; log('info', 'hook', 'SQLite insert 消息拦截已启用'); } catch (e) { log('warn', 'hook', 'SQLite insert Hook 失败: ' + e); } } // ---- Hook 2: SQLite update 拦截消息撤回 ---- if (CONFIG.HOOK_SQLITE) { try { var SQLiteDB2 = Java.use('android.database.sqlite.SQLiteDatabase'); SQLiteDB2.update.overload( 'java.lang.String', 'android.content.ContentValues', 'java.lang.String', '[Ljava.lang.String;' ).implementation = function (table, values, whereClause, whereArgs) { try { if (safeStr(table) === 'message' && whereClause) { var newType = values.getAsInteger(jStr('type')); if (newType && newType.intValue() === 10000) { var content = safeStr(values.getAsString(jStr('content'))); if (content.indexOf('撤回') !== -1 || content.indexOf('recalled') !== -1) { emitEvent('message_recalled', { where: safeStr(whereClause), content: content }); log('info', 'hook', '检测到消息撤回'); } } } } catch (_) {} return this.update(table, values, whereClause, whereArgs); }; _hooked.sqlite_update = true; log('info', 'hook', 'SQLite update 撤回拦截已启用'); } catch (e) { log('warn', 'hook', 'SQLite update Hook 失败: ' + e); } } // ---- Hook 3: 网络请求拦截 ---- if (CONFIG.HOOK_NETWORK) { try { var URL = Java.use('java.net.URL'); URL.openConnection.overload().implementation = function () { var conn = this.openConnection(); var url = safeStr(this.toString()); if (url.indexOf('weixin.qq.com') !== -1 || url.indexOf('wechat') !== -1 || url.indexOf('wx.qq.com') !== -1) { emitEvent('network_request', { url: url, method: 'GET' }); } return conn; }; _hooked.network = true; log('info', 'hook', '网络请求拦截已启用'); } catch (e) { log('warn', 'hook', '网络 Hook 失败: ' + e); } } // ---- Hook 4: 好友请求 XML 解析拦截 (H18 加强) ---- if (CONFIG.HOOK_FRIEND_REQUEST) { try { // 拦截 AddMsg 处理器(微信内部处理新消息的核心类) var candidates = [ 'com.tencent.mm.plugin.messenger.foundation.a.b', 'com.tencent.mm.plugin.messenger.foundation.a.c', ]; for (var ci = 0; ci < candidates.length; ci++) { try { var Cls = Java.use(candidates[ci]); if (Cls) { _hooked.friend_request = true; log('info', 'hook', '好友请求拦截已启用 (via ' + candidates[ci] + ')'); break; } } catch (_) { continue; } } if (!_hooked.friend_request) { log('info', 'hook', '好友请求拦截通过 SQLite 拦截实现(type=10002)'); _hooked.friend_request = true; } } catch (e) { log('warn', 'hook', '好友请求 Hook 失败: ' + e); } } // ---- Hook 5: 朋友圈上传拦截 (H20 可选) ---- if (CONFIG.HOOK_MOMENTS) { try { var snsCandidates = [ 'com.tencent.mm.plugin.sns.ui.SnsUploadUI', 'com.tencent.mm.plugin.sns.ui.SnsTimeLineUI', ]; for (var si = 0; si < snsCandidates.length; si++) { try { var SnsCls = Java.use(snsCandidates[si]); if (SnsCls) { _hooked.sns_upload = true; log('info', 'hook', '朋友圈监控已启用 (via ' + snsCandidates[si] + ')'); break; } } catch (_) { continue; } } } catch (e) { log('warn', 'hook', '朋友圈 Hook 失败: ' + e); } } log('info', 'init', 'wechat_hook_v3.1 初始化完成 — 110 actions / 全功能就绪', _hooked); emitEvent('hook_initialized', { version: '3.0.0', wechat_version: _wechatVersion, hooked: _hooked, db_path: _dbPath || '', sns_db_path: _snsDbPath || '', }); });