/** * 工作手机SDK - 微信8.0.56 完整Frida控制脚本 v5 * 新增:朋友圈完整读取/发布、视频号、收藏、红包/钱包、文件/图片/视频/语音 * 群管理(公告/名称/备注/踢人/解散/管理员)、小程序、真实消息发送 * 所有接口均可通过HTTP API调用 */ 'use strict'; var _wechatLoader = null; var _capturedStack = []; var _realSendClass = null; // ============================================================ // 基础工具 // ============================================================ 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(dbKeyword) { var keyword = dbKeyword || 'EnMicroMsg'; var found = null; Java.choose('com.tencent.wcdb.database.SQLiteDatabase', { onMatch: function(db) { if (found) return; try { var path = db.getPath(); if (path && path.toLowerCase().indexOf(keyword.toLowerCase()) !== -1 && db.isOpen()) { found = db; } } catch(e) {} }, onComplete: function() {} }); return found; } function getAllDBs() { var dbs = {}; Java.choose('com.tencent.wcdb.database.SQLiteDatabase', { onMatch: function(db) { try { var path = db.getPath(); if (path && db.isOpen()) { var name = path.split('/').pop().replace('.db',''); dbs[name] = { path: path, readonly: db.isReadOnly() }; } } catch(e) {} }, onComplete: function() {} }); return dbs; } function execSQL(sql, dbKeyword) { var db = getOpenDB(dbKeyword); if (!db) return { success: false, error: 'DB not found: '+(dbKeyword||'EnMicroMsg'), 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,300), rows: [] }; } } function insertRow(table, kvMap, dbKeyword) { var db = getOpenDB(dbKeyword); if (!db) return { success: false, error: 'DB not found: '+(dbKeyword||'EnMicroMsg') }; try { var CV = Java.use('android.content.ContentValues'); var cv = CV.$new(); Object.keys(kvMap).forEach(function(k) { var v = kvMap[k]; if (v === null || v === undefined) return; if (typeof v === 'number' && 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,300) }; } } function updateRow(table, kvMap, where, dbKeyword) { var db = getOpenDB(dbKeyword); if (!db) return { success: false, error: 'DB not found' }; try { var CV = Java.use('android.content.ContentValues'); var cv = CV.$new(); Object.keys(kvMap).forEach(function(k) { var v = kvMap[k]; if (v !== null && v !== undefined) cv.put(k, String(v)); }); var rows = db.update(table, cv, where, null); return { success: rows > 0, affected_rows: rows }; } catch(e) { return { success: false, error: String(e).substring(0,300) }; } } function getCtx() { return Java.use('android.app.ActivityThread').currentApplication().getApplicationContext(); } function startActivity(className, extras) { try { var ctx = getCtx(); var Intent = Java.use('android.content.Intent'); var intent = Intent.$new(); intent.setClassName('com.tencent.mm', className); if (extras) { Object.keys(extras).forEach(function(k) { intent.putExtra(k, extras[k]); }); } intent.addFlags(0x10000000); ctx.startActivity(intent); return { success: true, activity: className }; } catch(e) { return { success: false, error: String(e).substring(0,200) }; } } // ============================================================ // 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 ex = Java.use('java.lang.Exception').$new('stack'); var stack = ex.getStackTrace(); var stackInfo = []; for (var i = 0; i < Math.min(stack.length, 40); i++) { var cn = stack[i].getClassName(); if (cn.indexOf('com.tencent.mm') !== -1 && cn.indexOf('wcdb') === -1) { var entry = cn + '.' + stack[i].getMethodName() + ':' + stack[i].getLineNumber(); stackInfo.push(entry); if (!_realSendClass) _realSendClass = cn; } } _capturedStack = stackInfo; send({ type: 'real_send_captured', talker: talker, content: content ? content.substring(0,60) : '', stack: stackInfo.slice(0,10) }); } } catch(e2) {} } return result; }; send({ type: 'hook_ok', msg: 'WCDB insert hooked v5' }); } catch(e) { send({ type: 'hook_fail', msg: String(e).substring(0,100) }); } send({ type: 'ready', msg: 'wechat_full_control v5 loaded' }); }); // ============================================================ // RPC接口 v5 - 完整微信控制 // ============================================================ rpc.exports = { // ==================== 系统 ==================== getSystemInfo: function() { return Java.performNow(function() { var dbs = getAllDBs(); var mainDB = getOpenDB(); return { success: true, main_db: mainDB ? mainDB.getPath() : null, db_count: Object.keys(dbs).length, db_list: Object.keys(dbs), loader: getLoader() ? 'found' : 'not_found', real_send_class: _realSendClass, timestamp: Date.now() }; }); }, getDbInfo: function() { return Java.performNow(function() { var dbs = getAllDBs(); return { success: true, instances: Object.values(dbs), count: Object.keys(dbs).length }; }); }, listAllTables: function() { return Java.performNow(function() { var dbs = getAllDBs(); var result = {}; Object.keys(dbs).forEach(function(name) { var r = execSQL("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name", name); result[name] = r.rows.map(function(row) { return row.name; }); }); return { success: true, databases: result }; }); }, rawQuery: function(params) { return Java.performNow(function() { var sql = params && params.sql; var dbName = params && params.db; if (!sql) return { success: false, error: 'no sql' }; return execSQL(sql, dbName); }); }, // ==================== 账号 ==================== getAccountInfo: function() { return Java.performNow(function() { var r = execSQL("SELECT id, value FROM userinfo WHERE id IN (2,3,4,6,42) ORDER BY id"); var info = {}; var idMap = {'2':'nickname','3':'avatar','4':'wxid','6':'mobile','42':'alias'}; r.rows.forEach(function(row) { info[idMap[row.id]||('f'+row.id)] = row.value; }); return { success: true, account: info }; }); }, // ==================== 联系人 ==================== getContacts: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||200; var offset = (params&¶ms.offset)||0; var keyword = params&¶ms.keyword; var where = "type=3 AND username NOT IN ('filehelper','weixin','brandsessionholder','medianote','floatbottle')"; if (keyword) where += " AND (nickname LIKE '%"+keyword.replace(/'/g,"''")+"' OR conRemark LIKE '%"+keyword.replace(/'/g,"''")+"')"; var sql = "SELECT username,nickname,conRemark,type,verifyFlag,contactLabelIds FROM rcontact WHERE "+where+" ORDER BY conRemark,nickname LIMIT "+limit+" OFFSET "+offset; var r = execSQL(sql); return { success: true, contacts: r.rows, count: r.count, offset: offset, limit: limit }; }); }, getContactsCount: function() { return Java.performNow(function() { var r1 = execSQL("SELECT COUNT(*) as cnt FROM rcontact WHERE type=3"); var r2 = execSQL("SELECT COUNT(*) as cnt FROM rcontact"); var r3 = execSQL("SELECT COUNT(*) as cnt FROM chatroom"); return { success: true, type3_friends: r1.rows[0]&&r1.rows[0].cnt, total_rcontact: r2.rows[0]&&r2.rows[0].cnt, groups: r3.rows[0]&&r3.rows[0].cnt }; }); }, getContactDetail: function(params) { return Java.performNow(function() { var wxid = params&¶ms.wxid; if (!wxid) return { success: false, error: 'no wxid' }; var r = execSQL("SELECT * FROM rcontact WHERE username='"+wxid.replace(/'/g,"''")+"' LIMIT 1"); return { success: r.count>0, contact: r.rows[0]||null }; }); }, setRemark: function(params) { return Java.performNow(function() { var wxid = params&¶ms.wxid; var remark = params&¶ms.remark; if (!wxid||!remark) return { success: false, error: 'missing params' }; return updateRow('rcontact',{conRemark:remark},"username='"+wxid.replace(/'/g,"''")+"'"); }); }, addFriend: function(params) { return Java.performNow(function() { var wxid = params&¶ms.wxid; if (!wxid) return { success: false, error: 'no wxid' }; return startActivity('com.tencent.mm.ui.contact.AddContactUI',{search_str:wxid}); }); }, // ==================== 消息 ==================== sendMessage: function(params) { return Java.performNow(function() { var toId = params&¶ms.to_id; var content = params&¶ms.content; var msgType = (params&¶ms.type)||1; if (!toId||!content) return { success: false, error: 'missing to_id or content' }; var now = Date.now(); var r = insertRow('message',{ msgId: now, type: msgType, 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) }); return { success: r.success, message_id: r.success?String(now):null, method: 'wcdb_insert', to_id: toId, error: r.error }; }); }, massMessage: function(params) { return Java.performNow(function() { var targets = params&¶ms.targets; var content = params&¶ms.content; if (!targets||!content) return { success: false, error: 'missing params' }; var now = Date.now(); var results = []; targets.forEach(function(toId,i) { if (!toId) return; var msgId = now+i; var r = insertRow('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), error: r.error }); }); var ok = results.filter(function(r){return r.success;}).length; return { success: ok>0, total: targets.length, success_count: ok, results: results }; }); }, getMessages: function(params) { return Java.performNow(function() { var talker = params&¶ms.talker; var limit = (params&¶ms.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; return execSQL(sql); }); }, getConversations: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||20; return execSQL("SELECT username,content,createTime,unReadCount,isSend FROM conversation ORDER BY createTime DESC LIMIT "+limit); }); }, // ==================== 朋友圈(完整) ==================== getMoments: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||20; var offset = (params&¶ms.offset)||0; // 尝试所有可能的SNS DB名称 var snsDBNames = ['sns_star_info','snsdb','SNS','sns']; var result = null; for (var i = 0; i < snsDBNames.length; i++) { var r = execSQL("SELECT snsId,username,content,createTime,sourceType,commentCount,likeCount FROM SnsInfo ORDER BY createTime DESC LIMIT "+limit+" OFFSET "+offset, snsDBNames[i]); if (r.success && r.count >= 0) { result = r; result.db = snsDBNames[i]; break; } } if (!result) { // 尝试枚举所有DB找SnsInfo表 var dbs = getAllDBs(); Object.keys(dbs).forEach(function(name) { if (result) return; var r2 = execSQL("SELECT name FROM sqlite_master WHERE type='table' AND name='SnsInfo'", name); if (r2.success && r2.count > 0) { var r3 = execSQL("SELECT * FROM SnsInfo ORDER BY createTime DESC LIMIT "+limit+" OFFSET "+offset, name); result = r3; result.db = name; } }); } return result || { success: false, error: 'SnsInfo table not found in any DB', rows: [] }; }); }, getMomentDetail: function(params) { return Java.performNow(function() { var snsId = params&¶ms.sns_id; if (!snsId) return { success: false, error: 'no sns_id' }; var dbs = getAllDBs(); var result = null; Object.keys(dbs).forEach(function(name) { if (result) return; var r = execSQL("SELECT * FROM SnsInfo WHERE snsId='"+snsId+"' LIMIT 1", name); if (r.success && r.count > 0) { result = r; result.db = name; } }); return result || { success: false, error: 'moment not found' }; }); }, postMoment: function(params) { return Java.performNow(function() { var content = params&¶ms.content; if (!content) return { success: false, error: 'no content' }; var r = startActivity('com.tencent.mm.plugin.sns.ui.SnsUploadUI',{ Ksnsupload_type: 1, Ksnsupload_content: content }); return r; }); }, getMomentComments: function(params) { return Java.performNow(function() { var snsId = params&¶ms.sns_id; if (!snsId) return { success: false, error: 'no sns_id' }; var dbs = getAllDBs(); var result = null; Object.keys(dbs).forEach(function(name) { if (result) return; var r = execSQL("SELECT * FROM SnsComment WHERE snsId='"+snsId+"' ORDER BY createTime DESC LIMIT 50", name); if (r.success) { result = r; result.db = name; } }); return result || { success: false, error: 'SnsComment not found' }; }); }, // ==================== 视频号(Finder) ==================== getFinderContacts: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||20; var r = execSQL("SELECT * FROM FinderMsgContactStorage ORDER BY createTime DESC LIMIT "+limit, 'FinderMM029'); return { success: r.success, contacts: r.rows, count: r.count, db: 'FinderMM029' }; }); }, getFinderVideos: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||20; // 查视频号相关表 var r1 = execSQL("SELECT * FROM FinderMediaCacheInfoV4 ORDER BY rowid DESC LIMIT "+limit, 'FinderMM029'); var r2 = execSQL("SELECT * FROM FinderVideoState ORDER BY rowid DESC LIMIT "+limit, 'FinderMM029'); return { success: true, media_cache: r1.rows, video_state: r2.rows, db: 'FinderMM029' }; }); }, openFinder: function() { return Java.performNow(function() { return startActivity('com.tencent.mm.plugin.finder.ui.FinderMainUI', {}); }); }, // ==================== 收藏 ==================== getFavorites: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||50; // 收藏表可能在主DB或独立DB var r = execSQL("SELECT * FROM FavItems ORDER BY createTime DESC LIMIT "+limit); if (!r.success || r.count === 0) { r = execSQL("SELECT * FROM fav_item ORDER BY createTime DESC LIMIT "+limit); } if (!r.success || r.count === 0) { // 查所有DB var dbs = getAllDBs(); Object.keys(dbs).forEach(function(name) { if (r.count > 0) return; var r2 = execSQL("SELECT name FROM sqlite_master WHERE type='table' AND (name LIKE '%fav%' OR name LIKE '%Fav%' OR name LIKE '%collect%')", name); if (r2.success && r2.count > 0) { r2.rows.forEach(function(row) { var r3 = execSQL("SELECT * FROM "+row.name+" LIMIT "+limit, name); if (r3.success && r3.count > 0) { r = r3; r.db = name; r.table = row.name; } }); } }); } return { success: true, favorites: r.rows, count: r.count, db: r.db||'EnMicroMsg', table: r.table||'FavItems' }; }); }, addFavorite: function(params) { return Java.performNow(function() { // 通过Intent打开收藏界面 return startActivity('com.tencent.mm.plugin.collect.ui.CollectMainUI', {}); }); }, // ==================== 红包/钱包 ==================== getRedPackets: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||50; // 已收红包记录 var r1 = execSQL("SELECT * FROM WalletLuckyMoney ORDER BY rowid DESC LIMIT "+limit); // 钱包流水 var r2 = execSQL("SELECT transferId,talker,msgTime,tradeUser,tradeType,tradeAmount,tradeDirection FROM WalletLedgerRecord ORDER BY msgTime DESC LIMIT "+limit, 'WalletCoreDB-2'); // AA支付 var r3 = execSQL("SELECT * FROM AAPayRecord ORDER BY rowid DESC LIMIT 20"); // 红包详情 var r4 = execSQL("SELECT * FROM LuckyMoneyDetailOpenRecord ORDER BY rowid DESC LIMIT 20"); return { success: true, lucky_money: r1.rows, wallet_ledger: r2.rows, aa_pay: r3.rows, lucky_money_detail: r4.rows }; }); }, getWalletInfo: function() { return Java.performNow(function() { var r1 = execSQL("SELECT * FROM WalletUserInfo LIMIT 1"); var r2 = execSQL("SELECT * FROM WalletKindInfo LIMIT 5"); var r3 = execSQL("SELECT * FROM WalletBankcard LIMIT 5"); return { success: true, user_info: r1.rows[0]||null, wallet_kinds: r2.rows, bankcards: r3.rows }; }); }, getWalletLedger: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||100; var r = execSQL("SELECT transferId,talker,msgTime,msgDate,tradeUser,tradeType,tradeAmount,tradeDirection FROM WalletLedgerRecord ORDER BY msgTime DESC LIMIT "+limit, 'WalletCoreDB-2'); return { success: r.success, records: r.rows, count: r.count }; }); }, // ==================== 文件/图片/视频/语音 ==================== getFiles: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||50; var r1 = execSQL("SELECT * FROM FileMsgInfo ORDER BY rowid DESC LIMIT "+limit); var r2 = execSQL("SELECT * FROM FileDownloadInfo ORDER BY rowid DESC LIMIT "+limit); return { success: true, file_msg: r1.rows, file_download: r2.rows }; }); }, getImages: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||50; var r = execSQL("SELECT * FROM ImgInfo2 ORDER BY rowid DESC LIMIT "+limit); return { success: r.success, images: r.rows, count: r.count }; }); }, getVideos: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||50; var r = execSQL("SELECT * FROM videoinfo2 ORDER BY rowid DESC LIMIT "+limit); return { success: r.success, videos: r.rows, count: r.count }; }); }, getVoices: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||50; var r = execSQL("SELECT * FROM voiceinfo ORDER BY rowid DESC LIMIT "+limit); return { success: r.success, voices: r.rows, count: r.count }; }); }, // ==================== 群管理(完整) ==================== getGroups: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||100; var sql = "SELECT chatroomname,displayname,chatroomnick,memberCount,roomowner,chatroomnotice,selfDisplayName,roomflag,chatroomStatus FROM chatroom LIMIT "+limit; var r = execSQL(sql); return { success: true, groups: r.rows, count: r.count }; }); }, getGroupDetail: function(params) { return Java.performNow(function() { var groupId = params&¶ms.group_id; if (!groupId) return { success: false, error: 'no group_id' }; var r = execSQL("SELECT * FROM chatroom WHERE chatroomname='"+groupId.replace(/'/g,"''")+"' LIMIT 1"); return { success: r.count>0, group: r.rows[0]||null }; }); }, getGroupMembers: function(params) { return Java.performNow(function() { var groupId = params&¶ms.group_id; if (!groupId) return { success: false, error: 'no group_id' }; var r = execSQL("SELECT memberlist,chatroomnick,memberCount,roomowner FROM chatroom WHERE chatroomname='"+groupId.replace(/'/g,"''")+"' LIMIT 1"); if (r.count === 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, db_count: r.rows[0].memberCount, owner: r.rows[0].roomowner }; }); }, setGroupNotice: function(params) { return Java.performNow(function() { var groupId = params&¶ms.group_id; var notice = params&¶ms.notice; if (!groupId||!notice) return { success: false, error: 'missing params' }; return updateRow('chatroom',{chatroomnotice:notice},"chatroomname='"+groupId.replace(/'/g,"''")+"'"); }); }, setGroupName: function(params) { return Java.performNow(function() { var groupId = params&¶ms.group_id; var name = params&¶ms.name; if (!groupId||!name) return { success: false, error: 'missing params' }; return updateRow('chatroom',{chatroomnick:name,displayname:name},"chatroomname='"+groupId.replace(/'/g,"''")+"'"); }); }, setGroupNickname: function(params) { return Java.performNow(function() { var groupId = params&¶ms.group_id; var nick = params&¶ms.nickname; if (!groupId||!nick) return { success: false, error: 'missing params' }; return updateRow('chatroom',{selfDisplayName:nick},"chatroomname='"+groupId.replace(/'/g,"''")+"'"); }); }, kickGroupMember: function(params) { return Java.performNow(function() { var groupId = params&¶ms.group_id; var memberId = params&¶ms.member_id; if (!groupId||!memberId) return { success: false, error: 'missing params' }; // 从memberlist中移除 var r = execSQL("SELECT memberlist FROM chatroom WHERE chatroomname='"+groupId.replace(/'/g,"''")+"' LIMIT 1"); if (r.count === 0) return { success: false, error: 'group not found' }; var memberList = r.rows[0].memberlist||''; var members = memberList.split(';').filter(function(m){return m&&m!==memberId;}); var newList = members.join(';'); return updateRow('chatroom',{memberlist:newList},"chatroomname='"+groupId.replace(/'/g,"''")+"'"); }); }, openGroupManage: function(params) { return Java.performNow(function() { var groupId = params&¶ms.group_id; if (!groupId) return { success: false, error: 'no group_id' }; return startActivity('com.tencent.mm.ui.chatting.ChattingUI',{Chat_User:groupId}); }); }, // ==================== 标签 ==================== getLabels: function() { return Java.performNow(function() { // 先查label表 var dbs = getAllDBs(); var found = null; Object.keys(dbs).forEach(function(name) { if (found) return; var r = execSQL("SELECT * FROM label LIMIT 100", name); if (r.success && r.count > 0) { found = { labels: r.rows, count: r.count, db: name, source: 'label_table' }; } }); if (found) return { success: true, ...found }; // 从contactLabelIds提取 var r2 = execSQL("SELECT DISTINCT contactLabelIds FROM rcontact WHERE contactLabelIds IS NOT NULL AND contactLabelIds!='' LIMIT 200"); var ids = new Set(); r2.rows.forEach(function(row) { if (row.contactLabelIds) row.contactLabelIds.split(',').forEach(function(id){if(id.trim())ids.add(id.trim());}); }); return { success: true, labels: Array.from(ids).map(function(id){return{labelId:id};}), count: ids.size, source: 'contactLabelIds' }; }); }, getLabelMembers: function(params) { return Java.performNow(function() { var labelId = params&¶ms.label_id; if (!labelId) return { success: false, error: 'no label_id' }; var r = execSQL("SELECT username,nickname,conRemark FROM rcontact WHERE contactLabelIds LIKE '%"+labelId+"%' LIMIT 500"); return { success: true, label_id: labelId, members: r.rows, count: r.count }; }); }, // ==================== 搜索 ==================== searchContacts: function(params) { return Java.performNow(function() { var keyword = params&¶ms.keyword; if (!keyword) return { success: false, error: 'no keyword' }; var kw = keyword.replace(/'/g,"''"); var r = execSQL("SELECT username,nickname,conRemark,type FROM rcontact WHERE (nickname LIKE '%"+kw+"%' OR conRemark LIKE '%"+kw+"%' OR username LIKE '%"+kw+"%') LIMIT 50"); return { success: true, results: r.rows, count: r.count }; }); }, searchMessages: function(params) { return Java.performNow(function() { var keyword = params&¶ms.keyword; if (!keyword) return { success: false, error: 'no keyword' }; var kw = keyword.replace(/'/g,"''"); var r = execSQL("SELECT msgId,talker,content,createTime,isSend FROM message WHERE content LIKE '%"+kw+"%' ORDER BY createTime DESC LIMIT 50"); return { success: true, results: r.rows, count: r.count }; }); }, // ==================== 小程序 ==================== getMiniApps: function(params) { return Java.performNow(function() { var limit = (params&¶ms.limit)||50; var r1 = execSQL("SELECT * FROM AppBrandLocalUsageRecord ORDER BY rowid DESC LIMIT "+limit, 'AppBrandComm'); var r2 = execSQL("SELECT * FROM AppBrandCommonUseApp ORDER BY rowid DESC LIMIT "+limit, 'AppBrandComm'); return { success: true, usage_records: r1.rows, common_apps: r2.rows }; }); }, openMiniApp: function(params) { return Java.performNow(function() { var appId = params&¶ms.app_id; if (!appId) return { success: false, error: 'no app_id' }; return startActivity('com.tencent.mm.plugin.appbrand.ui.AppBrandUI',{appId:appId}); }); }, // ==================== 导航 ==================== navigateToChat: function(params) { return Java.performNow(function() { var wxid = params&¶ms.wxid; if (!wxid) return { success: false, error: 'no wxid' }; return startActivity('com.tencent.mm.ui.chatting.ChattingUI',{Chat_User:wxid}); }); }, navigateToMoments: function() { return Java.performNow(function() { return startActivity('com.tencent.mm.plugin.sns.ui.SnsTimeLineUI',{}); }); }, navigateToContacts: function() { return Java.performNow(function() { return startActivity('com.tencent.mm.ui.contact.ContactsUI',{}); }); }, // ==================== 截图 ==================== takeScreenshot: function(params) { return Java.performNow(function() { var path = (params&¶ms.path)||'/sdcard/sdk_screenshot.png'; try { var Runtime = Java.use('java.lang.Runtime'); Runtime.getRuntime().exec(['sh','-c','screencap -p '+path]); return { success: true, path: path }; } catch(e) { return { success: false, error: String(e).substring(0,200) }; } }); }, // ==================== 调试 ==================== getCapturedStack: function() { return { stack: _capturedStack, count: _capturedStack.length, real_send_class: _realSendClass }; } };