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

2247 lines
93 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.

/**
* 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;
}
function _execSQL(sql, dbPathOverride) {
var results = [];
try {
var path = dbPathOverride || _getDbPath();
if (!path) return results;
var db = _findOpenWcdb(path);
if (!db) {
log('warn', 'db', '未找到已打开的 WCDB 实例: ' + path);
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();
} catch (e) {
log('warn', 'db', 'SQL 执行失败: ' + e);
}
return results;
}
function _findOpenWcdb(path) {
var normalizedPath = path.replace('/data/data/', '/data/user/0/');
var found = null;
Java.choose('com.tencent.wcdb.database.SQLiteDatabase', {
onMatch: function(instance) {
if (found) return;
try {
var p = instance.getPath();
if ((p === path || p === normalizedPath) && instance.isOpen()) {
found = instance;
}
} catch(_) {}
},
onComplete: function() {}
});
return found;
}
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;
},
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) || '';
try {
return Java.performNow(function () {
return _getMessagesFromDB(conversationId, limit);
});
} catch (e) {
return { success: false, error: String(e), messages: [] };
}
},
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: '朋友圈内容不能为空' };
try {
return Java.performNow(function () {
return _postMomentsInternal(content, imageUrls);
});
} 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, {});
},
// ==================== 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) 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';
if (!msgSvrId) return { success: false, error: '缺少 msg_svr_id' };
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 { success: false, error: '缺少 wxid' };
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) || '';
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) || '';
var voicePath = (params && params.voice_path) || '';
var duration = (params && params.duration) || 0;
if (!toId || !voicePath) return { success: false, error: '缺少 to_id 或 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) || '';
if (!wxid) return { success: false, error: '缺少 wxid' };
return _intentAction('com.workphone.SWITCH_ACCOUNT', { wxid: wxid }, 'account_switched', { wxid: wxid });
},
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) || '';
var emojiMd5 = (params && params.emoji_md5) || '';
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 };
},
// ==================== 系统控制 (纯Frida无线) ====================
takeScreenshot: function (params) {
try {
return Java.performNow(function () {
var savePath = (params && params.path) || '/sdcard/frida_sc_' + Date.now() + '.png';
var Runtime = Java.use('java.lang.Runtime');
var process = Runtime.getRuntime().exec(['sh', '-c', 'screencap -p ' + savePath]);
process.waitFor();
var File = Java.use('java.io.File');
var file = File.$new(savePath);
if (!file.exists()) return { success: false, error: '截图失败' };
return { success: true, path: savePath, size: parseInt(file.length()) };
});
} catch (e) { return { success: false, error: String(e) }; }
},
getScreenshotBase64: function (params) {
try {
return Java.performNow(function () {
var filePath = (params && params.path) || '/sdcard/frida_sc.png';
var File = Java.use('java.io.File');
var file = File.$new(filePath);
if (!file.exists()) return { success: false, error: '文件不存在' };
var fileSize = parseInt(file.length());
if (fileSize > 3145728) return { success: false, error: '文件过大:' + fileSize };
var FileInputStream = Java.use('java.io.FileInputStream');
var fis = FileInputStream.$new(file);
var bytes = Java.array('byte', new Array(fileSize).fill(0));
fis.read(bytes);
fis.close();
var Base64 = Java.use('android.util.Base64');
var b64 = Base64.encodeToString(bytes, 2);
return { success: true, base64: b64, size: fileSize };
});
} catch (e) { return { success: false, error: String(e) }; }
},
getCurrentActivity: function () {
try {
return Java.performNow(function () {
var activities = [];
Java.choose('android.app.Activity', {
onMatch: function (act) {
if (!act.isFinishing() && !act.isDestroyed()) {
activities.push({ cls: act.getClass().getName(), title: act.getTitle() ? act.getTitle().toString() : '', focus: act.hasWindowFocus() });
}
},
onComplete: function () {},
});
return { success: true, activities: activities, foreground: activities.length > 0 ? activities[activities.length - 1].cls : 'none' };
});
} catch (e) { return { success: false, error: String(e) }; }
},
navigateToChat: function (params) {
try {
return Java.performNow(function () {
var wxid = (params && params.wxid) || 'filehelper';
var ctx = getCtx();
var Intent = Java.use('android.content.Intent');
var intent = Intent.$new();
intent.setClassName(jStr('com.tencent.mm'), jStr('com.tencent.mm.ui.chatting.ChattingUI'));
intent.putExtra(jStr('Chat_User'), jStr(wxid));
intent.addFlags(0x10000000);
ctx.startActivity(intent);
return { success: true, target: 'ChattingUI', wxid: wxid };
});
} catch (e) { return { success: false, error: String(e) }; }
},
navigateToMain: function () {
try {
return Java.performNow(function () {
var ctx = getCtx();
var Intent = Java.use('android.content.Intent');
var intent = Intent.$new();
intent.setClassName(jStr('com.tencent.mm'), jStr('com.tencent.mm.ui.LauncherUI'));
intent.addFlags(0x10000000);
ctx.startActivity(intent);
return { success: true, target: 'LauncherUI' };
});
} catch (e) { return { success: false, error: String(e) }; }
},
navigateToMoments: function () {
try {
return Java.performNow(function () {
var ctx = getCtx();
var Intent = Java.use('android.content.Intent');
var intent = Intent.$new();
intent.setClassName(jStr('com.tencent.mm'), jStr('com.tencent.mm.plugin.sns.ui.SnsTimeLineUI'));
intent.addFlags(0x10000000);
ctx.startActivity(intent);
return { success: true, target: 'SnsTimeLineUI' };
});
} catch (e) { return { success: false, error: String(e) }; }
},
simulateTap: function (params) {
try {
return Java.performNow(function () {
var x = (params && params.x) || 0;
var y = (params && params.y) || 0;
var Runtime = Java.use('java.lang.Runtime');
Runtime.getRuntime().exec(['sh', '-c', 'input tap ' + x + ' ' + y]).waitFor();
return { success: true, x: x, y: y };
});
} catch (e) { return { success: false, error: String(e) }; }
},
simulateBack: function () {
try {
return Java.performNow(function () {
var Runtime = Java.use('java.lang.Runtime');
Runtime.getRuntime().exec(['sh', '-c', 'input keyevent 4']).waitFor();
return { success: true, action: 'back' };
});
} catch (e) { return { success: false, error: String(e) }; }
},
simulateInput: function (params) {
try {
return Java.performNow(function () {
var text = (params && params.text) || '';
var Runtime = Java.use('java.lang.Runtime');
Runtime.getRuntime().exec(['sh', '-c', 'input text "' + text.replace(/"/g, '\\"') + '"']).waitFor();
return { success: true, text: text };
});
} catch (e) { return { success: false, error: String(e) }; }
},
getConnectionStatus: function () {
return { success: true, mode: 'frida_wireless_tcp', pid: Process.id, arch: Process.arch, platform: Process.platform, timestamp: Date.now() };
},
};
// ============================================================
// § 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);
}
}
return {
success: sent,
message_id: sent ? generateId('msg') : null,
method: sent ? 'hook' : 'failed',
to_id: toId,
};
}
// ============================================================
// § 6 内部实现 — 数据库读取 (H15/H16)
// ============================================================
function _getMessagesFromDB(conversationId, limit) {
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) {
var sql = "SELECT username, nickname, conRemark, type FROM rcontact"
+ " WHERE type NOT IN (0, 33) AND verifyFlag=0"
+ " AND username NOT LIKE '%@chatroom'"
+ " AND username NOT LIKE 'gh_%'"
+ " ORDER BY nickname LIMIT " + limit;
var rows = _execSQL(sql);
var contacts = rows.map(function (r) {
return { wxid: r.username, nickname: r.nickname, remark: r.conRemark, type: r.type };
});
_contactCache = contacts;
return { success: true, contacts: contacts, count: contacts.length };
}
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)
// ============================================================
function _postMomentsInternal(content, imageUrls) {
try {
var Intent = Java.use('android.content.Intent');
var ctx = getCtx();
var intent = Intent.$new('com.workphone.POST_MOMENTS');
intent.putExtra('content', content);
if (imageUrls.length > 0) intent.putExtra('image_urls', imageUrls.join(','));
ctx.sendBroadcast(intent);
log('info', 'sns', '发布朋友圈: content_len=' + content.length + ' images=' + imageUrls.length);
emitEvent('moments_posted', { content: content, image_count: imageUrls.length });
return { success: true, method: 'intent_broadcast', sns_id: generateId('sns') };
} catch (e) {
return { success: false, error: '发朋友圈失败: ' + e };
}
}
function _getMomentsFromDB(wxid, limit) {
var snsPath = _getSnsDbPath();
if (!snsPath) {
return { success: false, error: 'SnsMicroMsg.db 未找到', moments: [] };
}
var where = wxid
? " WHERE userName='" + wxid.replace(/'/g, "''") + "'"
: '';
var sql = "SELECT snsId, userName, createTime, content, mediaCount, likeCount, commentCount FROM SnsInfo"
+ where + " ORDER BY createTime DESC LIMIT " + limit;
var rows = _execSnsSQL(sql);
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,
};
}
function _parseSnsContent(raw) {
if (!raw) return { text: '' };
try {
var idx = raw.indexOf('<contentDesc>');
var endIdx = raw.indexOf('</contentDesc>');
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]]));
}
ctx.sendBroadcast(intent);
if (eventType) {
emitEvent(eventType, eventPayload || {});
}
log('info', 'intent', intentAction + ' → ' + keys.join(','));
return { success: true, method: 'intent_broadcast', 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 || '',
});
});