Files
workphone-sdk/sdk/agent/hook/wechat_api_v7.js

494 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 微信工作手机 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.');