From 89c96e9ad48adf4d8829ce849bdf136a1341671c Mon Sep 17 00:00:00 2001 From: fnvtk Date: Mon, 18 May 2026 21:38:05 +0800 Subject: [PATCH] feat: v7 full API - wechat_api_v7.js --- sdk/agent/hook/wechat_api_v7.js | 493 ++++++++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 sdk/agent/hook/wechat_api_v7.js diff --git a/sdk/agent/hook/wechat_api_v7.js b/sdk/agent/hook/wechat_api_v7.js new file mode 100644 index 0000000000..ef84015be1 --- /dev/null +++ b/sdk/agent/hook/wechat_api_v7.js @@ -0,0 +1,493 @@ +// 微信工作手机 SDK v7 - 全量Frida RPC接口 +// 基于微信8.0.56真实DB结构开发 +// 账号:游条姐-游戏教学辅助看我朋友圈 (wxid_5g37snchpv8e22) + +'use strict'; + +var _db = null; +var _snsDb = null; +var _walletDb = null; +var _finderDb = null; +var _favoriteDb = null; +var _dbPath = null; + +// ==================== DB初始化 ==================== +function initDB() { + if (_db) return _db; + try { + Java.choose('com.tencent.wcdb.database.SQLiteDatabase', { + onMatch: function(db) { + try { + var path = db.getPath(); + if (path && path.indexOf('EnMicroMsg.db') > -1 && path.indexOf('wal') === -1) { + _db = db; + _dbPath = path; + } + if (path && path.indexOf('sns_star_info') > -1) _snsDb = db; + if (path && path.indexOf('WalletCoreDB') > -1) _walletDb = db; + if (path && path.indexOf('Finder') > -1 && path.indexOf('MM') > -1) _finderDb = db; + if (path && path.indexOf('FTS5Index') > -1) _favoriteDb = db; + } catch(e) {} + }, + onComplete: function() {} + }); + } catch(e) {} + return _db; +} + +function execSQL(db, sql, args) { + try { + if (!db) return {error: 'DB not initialized'}; + var cursor; + if (args && args.length > 0) { + var strArgs = args.map(function(a) { return a ? a.toString() : null; }); + var jArgs = Java.array('java.lang.String', strArgs); + cursor = db.rawQuery(sql, jArgs); + } else { + cursor = db.rawQuery(sql, null); + } + var cols = []; + var colCount = cursor.getColumnCount(); + for (var i = 0; i < colCount; i++) cols.push(cursor.getColumnName(i)); + var rows = []; + while (cursor.moveToNext()) { + var row = {}; + for (var j = 0; j < colCount; j++) { + try { row[cols[j]] = cursor.getString(j); } catch(e) { row[cols[j]] = null; } + } + rows.push(row); + } + cursor.close(); + return {columns: cols, rows: rows, count: rows.length}; + } catch(e) { + return {error: e.toString()}; + } +} + +function writeDB(db, sql) { + try { + if (!db) return {error: 'DB not initialized'}; + db.execSQL(sql); + return {success: true}; + } catch(e) { + return {error: e.toString()}; + } +} + +// ==================== RPC接口定义 ==================== +rpc.exports = { + + // ===== 系统 ===== + ping: function() { + return {status: 'ok', version: 'v7', timestamp: Date.now()}; + }, + + getSystemInfo: function() { + initDB(); + return { + db_path: _dbPath, + has_main_db: !!_db, + has_sns_db: !!_snsDb, + has_wallet_db: !!_walletDb, + has_finder_db: !!_finderDb, + has_favorite_db: !!_favoriteDb, + frida_version: Frida.version, + timestamp: Date.now() + }; + }, + + // ===== 账号 ===== + getAccountInfo: function() { + initDB(); + var r = execSQL(_db, "SELECT value FROM userinfo WHERE id IN (2,4,6,42,43,44,62,66,67) ORDER BY id"); + var info = execSQL(_db, "SELECT value FROM userinfo WHERE id=2"); + var nick = execSQL(_db, "SELECT value FROM userinfo WHERE id=6"); + var avatar = execSQL(_db, "SELECT value FROM userinfo WHERE id=4"); + var phone = execSQL(_db, "SELECT value FROM userinfo WHERE id=42"); + return { + wxid: info.rows && info.rows[0] ? info.rows[0].value : null, + nickname: nick.rows && nick.rows[0] ? nick.rows[0].value : null, + avatar: avatar.rows && avatar.rows[0] ? avatar.rows[0].value : null, + phone: phone.rows && phone.rows[0] ? phone.rows[0].value : null, + raw: r + }; + }, + + // ===== 联系人 ===== + getContacts: function(limit, offset) { + initDB(); + limit = limit || 100; + offset = offset || 0; + return execSQL(_db, + "SELECT username, alias, conRemark, nickName, type, contactLabelIds, imgFlag, verifyFlag " + + "FROM rcontact WHERE type=3 AND verifyFlag=0 " + + "ORDER BY rowid LIMIT " + limit + " OFFSET " + offset + ); + }, + + getContactCount: function() { + initDB(); + return execSQL(_db, "SELECT COUNT(*) as total FROM rcontact WHERE type=3 AND verifyFlag=0"); + }, + + searchContact: function(keyword) { + initDB(); + return execSQL(_db, + "SELECT username, alias, conRemark, nickName, type, contactLabelIds " + + "FROM rcontact WHERE (nickName LIKE '%" + keyword + "%' OR conRemark LIKE '%" + keyword + "%' OR alias LIKE '%" + keyword + "%') " + + "AND type NOT IN (0,4) LIMIT 50" + ); + }, + + getContactDetail: function(wxid) { + initDB(); + return execSQL(_db, + "SELECT * FROM rcontact WHERE username='" + wxid + "' LIMIT 1" + ); + }, + + // ===== 消息 ===== + getMessages: function(talker, limit) { + initDB(); + limit = limit || 50; + return execSQL(_db, + "SELECT msgId, type, isSend, createTime, talker, content, imgPath " + + "FROM message WHERE talker='" + talker + "' " + + "ORDER BY createTime DESC LIMIT " + limit + ); + }, + + getRecentMessages: function(hours) { + initDB(); + hours = hours || 24; + var since = Date.now() - hours * 3600 * 1000; + return execSQL(_db, + "SELECT msgId, type, isSend, createTime, talker, content " + + "FROM message WHERE createTime > " + since + + " ORDER BY createTime DESC LIMIT 200" + ); + }, + + sendMessage: function(toUser, content) { + initDB(); + if (!_db) return {error: 'DB not available'}; + var msgId = Date.now(); + var createTime = Date.now(); + var sql = "INSERT INTO message (msgId, msgSvrId, type, status, isSend, isShowTimer, createTime, talker, content, imgPath, reserved, lvbuffer, transContent, transBrandWording, talkerId, bizClientMsgId, bizChatId, bizChatUserId, msgSeq, flag) VALUES (" + + msgId + ", 0, 1, 2, 1, 0, " + createTime + ", '" + toUser + "', '" + content.replace(/'/g, "''") + "', '', '', '', '', '', 0, '', '', '', 0, 0)"; + var result = writeDB(_db, sql); + if (result.success) { + // 验证写入 + var verify = execSQL(_db, "SELECT msgId, content, talker, createTime FROM message WHERE msgId=" + msgId); + return {success: true, msgId: msgId, toUser: toUser, content: content, verify: verify}; + } + return result; + }, + + // ===== 会话 ===== + getConversations: function(limit) { + initDB(); + limit = limit || 30; + return execSQL(_db, + "SELECT username, unReadCount, lastMsg, lastMsgCreateTime, isSend, msgStatus " + + "FROM conversation ORDER BY lastMsgCreateTime DESC LIMIT " + limit + ); + }, + + // ===== 群组 ===== + getGroups: function(limit) { + initDB(); + limit = limit || 50; + return execSQL(_db, + "SELECT chatroomname, memberlist, roomowner, announcement, selfDisplayName, memberCount " + + "FROM chatroom ORDER BY rowid DESC LIMIT " + limit + ); + }, + + getGroupCount: function() { + initDB(); + return execSQL(_db, "SELECT COUNT(*) as total FROM chatroom"); + }, + + getGroupDetail: function(chatroomId) { + initDB(); + return execSQL(_db, "SELECT * FROM chatroom WHERE chatroomname='" + chatroomId + "' LIMIT 1"); + }, + + getGroupMembers: function(chatroomId) { + initDB(); + var group = execSQL(_db, "SELECT memberlist FROM chatroom WHERE chatroomname='" + chatroomId + "'"); + if (group.rows && group.rows[0]) { + var memberList = group.rows[0].memberlist || ''; + var members = memberList.split(';').filter(function(m) { return m.trim(); }); + return {chatroomId: chatroomId, members: members, count: members.length}; + } + return {error: 'Group not found'}; + }, + + getGroupAnnouncement: function(chatroomId) { + initDB(); + return execSQL(_db, "SELECT chatroomname, announcement, selfDisplayName FROM chatroom WHERE chatroomname='" + chatroomId + "'"); + }, + + setGroupRemark: function(chatroomId, remark) { + initDB(); + var sql = "UPDATE chatroom SET selfDisplayName='" + remark.replace(/'/g, "''") + "' WHERE chatroomname='" + chatroomId + "'"; + return writeDB(_db, sql); + }, + + // ===== 标签 ===== + getLabels: function() { + initDB(); + // 从rcontact的contactLabelIds字段提取所有标签ID + var r = execSQL(_db, "SELECT DISTINCT contactLabelIds FROM rcontact WHERE contactLabelIds IS NOT NULL AND contactLabelIds != '' LIMIT 500"); + var labelSet = {}; + if (r.rows) { + r.rows.forEach(function(row) { + var ids = (row.contactLabelIds || '').split(','); + ids.forEach(function(id) { if (id.trim()) labelSet[id.trim()] = true; }); + }); + } + return {label_ids: Object.keys(labelSet), count: Object.keys(labelSet).length}; + }, + + getContactsByLabel: function(labelId) { + initDB(); + return execSQL(_db, + "SELECT username, nickName, conRemark, contactLabelIds FROM rcontact " + + "WHERE contactLabelIds LIKE '%" + labelId + "%' AND type=3 LIMIT 100" + ); + }, + + // ===== 朋友圈 ===== + getMoments: function(limit) { + if (!_snsDb) initDB(); + limit = limit || 20; + if (!_snsDb) return {error: 'SNS DB not available'}; + return execSQL(_snsDb, + "SELECT snsId, userName, createTime, content, type, localFlag, likeFlag " + + "FROM StarSnsInfo ORDER BY createTime DESC LIMIT " + limit + ); + }, + + getMomentCount: function() { + if (!_snsDb) initDB(); + if (!_snsDb) return {error: 'SNS DB not available'}; + return execSQL(_snsDb, "SELECT COUNT(*) as total FROM StarSnsInfo"); + }, + + postMoment: function(content) { + if (!_snsDb) initDB(); + if (!_snsDb) return {error: 'SNS DB not available'}; + var snsId = Date.now(); + var createTime = Math.floor(Date.now() / 1000); + var sql = "INSERT INTO StarSnsInfo (snsId, userName, localFlag, createTime, content, type, localPrivate, likeFlag, pravited, stringSeq, withTa, withTaHasOther, sourceType, subType, serverExtFlag) VALUES (" + + snsId + ", 'wxid_5g37snchpv8e22', 1, " + createTime + ", '" + content.replace(/'/g, "''") + "', 1, 0, 0, 0, '', 0, 0, 0, 0, 0)"; + var result = writeDB(_snsDb, sql); + if (result.success) { + var verify = execSQL(_snsDb, "SELECT snsId, content, createTime FROM StarSnsInfo WHERE snsId=" + snsId); + return {success: true, snsId: snsId, content: content, verify: verify}; + } + return result; + }, + + // ===== 视频号 ===== + getFinderInfo: function() { + if (!_finderDb) initDB(); + if (!_finderDb) return {error: 'Finder DB not available'}; + var tables = execSQL(_finderDb, "SELECT name FROM sqlite_master WHERE type='table'"); + return {tables: tables, has_finder_db: !!_finderDb}; + }, + + getFinderContacts: function() { + if (!_finderDb) initDB(); + if (!_finderDb) return {error: 'Finder DB not available'}; + return execSQL(_finderDb, "SELECT * FROM FinderMsgContactStorage LIMIT 20"); + }, + + getFinderMedia: function() { + if (!_finderDb) initDB(); + if (!_finderDb) return {error: 'Finder DB not available'}; + return execSQL(_finderDb, "SELECT * FROM FinderMediaCacheInfoV4 LIMIT 20"); + }, + + // ===== 收藏 ===== + getFavorites: function(limit) { + if (!_favoriteDb) initDB(); + limit = limit || 20; + if (!_favoriteDb) { + // 尝试从主DB查 + return execSQL(_db, "SELECT * FROM FavItems LIMIT " + limit); + } + return execSQL(_favoriteDb, "SELECT * FROM FTS5MetaFavorite LIMIT " + limit); + }, + + // ===== 钱包/红包 ===== + getWalletInfo: function() { + if (!_walletDb) initDB(); + if (!_walletDb) return {error: 'Wallet DB not available'}; + var tables = execSQL(_walletDb, "SELECT name FROM sqlite_master WHERE type='table'"); + return {tables: tables, has_wallet_db: !!_walletDb}; + }, + + getWalletLedger: function(limit) { + if (!_walletDb) initDB(); + limit = limit || 20; + if (!_walletDb) return {error: 'Wallet DB not available'}; + return execSQL(_walletDb, + "SELECT transferId, talker, msgTime, msgDate, tradeUser, tradeType, tradeAmount " + + "FROM WalletLedgerRecord ORDER BY msgTime DESC LIMIT " + limit + ); + }, + + getLuckyMoney: function(limit) { + initDB(); + limit = limit || 20; + return execSQL(_db, + "SELECT * FROM WalletLuckyMoney ORDER BY rowid DESC LIMIT " + limit + ); + }, + + getWalletBalance: function() { + if (!_walletDb) initDB(); + if (!_walletDb) return {error: 'Wallet DB not available'}; + // 查询钱包余额相关表 + var tables = execSQL(_walletDb, "SELECT name FROM sqlite_master WHERE type='table'"); + var results = {}; + if (tables.rows) { + tables.rows.forEach(function(t) { + try { + var r = execSQL(_walletDb, "SELECT * FROM " + t.name + " LIMIT 5"); + results[t.name] = r; + } catch(e) {} + }); + } + return results; + }, + + // ===== 图片/文件 ===== + getImageList: function(limit) { + initDB(); + limit = limit || 20; + return execSQL(_db, "SELECT * FROM ImgInfo2 ORDER BY rowid DESC LIMIT " + limit); + }, + + getImageCount: function() { + initDB(); + return execSQL(_db, "SELECT COUNT(*) as total FROM ImgInfo2"); + }, + + getVideoList: function(limit) { + initDB(); + limit = limit || 20; + return execSQL(_db, "SELECT * FROM videoinfo2 ORDER BY rowid DESC LIMIT " + limit); + }, + + getVideoCount: function() { + initDB(); + return execSQL(_db, "SELECT COUNT(*) as total FROM videoinfo2"); + }, + + getFileList: function(limit) { + initDB(); + limit = limit || 20; + return execSQL(_db, "SELECT * FROM FileMsgInfo ORDER BY rowid DESC LIMIT " + limit); + }, + + // ===== 小程序 ===== + getAppBrandList: function() { + initDB(); + return execSQL(_db, "SELECT * FROM AppBrandInfo LIMIT 20"); + }, + + // ===== 搜索 ===== + globalSearch: function(keyword) { + initDB(); + var contacts = execSQL(_db, + "SELECT username, nickName, conRemark FROM rcontact WHERE nickName LIKE '%" + keyword + "%' OR conRemark LIKE '%" + keyword + "%' LIMIT 10" + ); + var messages = execSQL(_db, + "SELECT talker, content, createTime FROM message WHERE content LIKE '%" + keyword + "%' ORDER BY createTime DESC LIMIT 10" + ); + var groups = execSQL(_db, + "SELECT chatroomname FROM chatroom WHERE chatroomname LIKE '%" + keyword + "%' LIMIT 10" + ); + return {contacts: contacts, messages: messages, groups: groups}; + }, + + // ===== 群发消息 ===== + massMessage: function(targets, content) { + initDB(); + if (!_db) return {error: 'DB not available'}; + if (!targets || !targets.length) return {error: 'No targets'}; + var results = []; + targets.forEach(function(toUser) { + var msgId = Date.now() + Math.floor(Math.random() * 1000); + var createTime = Date.now(); + var sql = "INSERT INTO message (msgId, msgSvrId, type, status, isSend, isShowTimer, createTime, talker, content, imgPath, reserved, lvbuffer, transContent, transBrandWording, talkerId, bizClientMsgId, bizChatId, bizChatUserId, msgSeq, flag) VALUES (" + + msgId + ", 0, 1, 2, 1, 0, " + createTime + ", '" + toUser + "', '" + content.replace(/'/g, "''") + "', '', '', '', '', '', 0, '', '', '', 0, 0)"; + var r = writeDB(_db, sql); + results.push({toUser: toUser, msgId: msgId, success: r.success || false, error: r.error}); + }); + return {results: results, total: targets.length, success: results.filter(function(r) { return r.success; }).length}; + }, + + // ===== 原始SQL(调试用)===== + rawQuery: function(dbName, sql) { + initDB(); + var db; + if (dbName === 'main') db = _db; + else if (dbName === 'sns') db = _snsDb; + else if (dbName === 'wallet') db = _walletDb; + else if (dbName === 'finder') db = _finderDb; + else if (dbName === 'favorite') db = _favoriteDb; + else db = _db; + return execSQL(db, sql); + }, + + // ===== 截图 ===== + takeScreenshot: function(savePath) { + try { + var Runtime = Java.use('java.lang.Runtime'); + var path = savePath || '/sdcard/wpsdk_screenshot.png'; + Runtime.getRuntime().exec(['sh', '-c', 'screencap -p ' + path]); + Java.use('java.lang.Thread').sleep(500); + return {success: true, path: path}; + } catch(e) { + return {error: e.toString()}; + } + }, + + // ===== DB信息 ===== + getDbInfo: function() { + initDB(); + return { + main: _dbPath, + sns: _snsDb ? _snsDb.getPath() : null, + wallet: _walletDb ? _walletDb.getPath() : null, + finder: _finderDb ? _finderDb.getPath() : null, + favorite: _favoriteDb ? _favoriteDb.getPath() : null + }; + }, + + // ===== 联系人完整统计 ===== + getContactStats: function() { + initDB(); + var total = execSQL(_db, "SELECT COUNT(*) as total FROM rcontact"); + var friends = execSQL(_db, "SELECT COUNT(*) as total FROM rcontact WHERE type=3 AND verifyFlag=0"); + var groups = execSQL(_db, "SELECT COUNT(*) as total FROM chatroom"); + var msgs = execSQL(_db, "SELECT COUNT(*) as total FROM message"); + var imgs = execSQL(_db, "SELECT COUNT(*) as total FROM ImgInfo2"); + var videos = execSQL(_db, "SELECT COUNT(*) as total FROM videoinfo2"); + return { + total_contacts: total.rows[0] ? total.rows[0].total : 0, + friends: friends.rows[0] ? friends.rows[0].total : 0, + groups: groups.rows[0] ? groups.rows[0].total : 0, + messages: msgs.rows[0] ? msgs.rows[0].total : 0, + images: imgs.rows[0] ? imgs.rows[0].total : 0, + videos: videos.rows[0] ? videos.rows[0].total : 0 + }; + } +}; + +console.log('[*] WeChat SDK v7 loaded. All interfaces ready.');