feat: v6 backend - sdk/agent/hook/wechat_v6_backend.js
This commit is contained in:
488
sdk/agent/hook/wechat_v6_backend.js
Normal file
488
sdk/agent/hook/wechat_v6_backend.js
Normal file
@@ -0,0 +1,488 @@
|
||||
// wechat_full_control_v6.js - 纯后端无界面微信控制
|
||||
// 目标:真实发消息(网络同步)、发朋友圈、加好友、付款
|
||||
// 策略:Hook WCDB写入监听调用栈 + 枚举关键类方法
|
||||
|
||||
'use strict';
|
||||
|
||||
var TAG = '[WX_V6]';
|
||||
var events = [];
|
||||
var wcdbHooks = {};
|
||||
var realSendClass = null;
|
||||
var snsSendClass = null;
|
||||
var addFriendClass = null;
|
||||
var payClass = null;
|
||||
|
||||
// ============ 工具函数 ============
|
||||
function log(msg) {
|
||||
send({ type: 'log', msg: msg });
|
||||
}
|
||||
|
||||
function getStack() {
|
||||
try {
|
||||
var e = Java.use('java.lang.Exception').$new();
|
||||
var stack = e.getStackTrace();
|
||||
var frames = [];
|
||||
for (var i = 1; i < Math.min(stack.length, 12); i++) {
|
||||
var f = stack[i];
|
||||
var cls = f.getClassName();
|
||||
if (cls.indexOf('com.tencent.mm') !== -1) {
|
||||
frames.push(cls + '.' + f.getMethodName() + ':' + f.getLineNumber());
|
||||
}
|
||||
}
|
||||
e.$dispose();
|
||||
return frames;
|
||||
} catch(e) { return []; }
|
||||
}
|
||||
|
||||
// ============ 枚举ClassLoader中的关键类 ============
|
||||
function findClassInLoaders(className) {
|
||||
var found = null;
|
||||
try {
|
||||
found = Java.use(className);
|
||||
return found;
|
||||
} catch(e) {}
|
||||
|
||||
// 遍历所有ClassLoader
|
||||
Java.enumerateClassLoaders({
|
||||
onMatch: function(loader) {
|
||||
try {
|
||||
var cls = loader.loadClass(className);
|
||||
if (cls) {
|
||||
Java.classFactory.loader = loader;
|
||||
found = Java.use(className);
|
||||
}
|
||||
} catch(e) {}
|
||||
},
|
||||
onComplete: function() {}
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
// ============ 主Hook逻辑 ============
|
||||
Java.perform(function() {
|
||||
log('v6 starting...');
|
||||
|
||||
// ===== 1. Hook WCDB insert 监听调用栈 =====
|
||||
try {
|
||||
var WCDB = Java.use('com.tencent.wcdb.database.SQLiteDatabase');
|
||||
|
||||
// Hook execSQL 捕获INSERT操作
|
||||
WCDB.execSQL.overload('java.lang.String').implementation = function(sql) {
|
||||
var result = this.execSQL(sql);
|
||||
if (sql && sql.indexOf('INSERT') !== -1 && sql.indexOf('message') !== -1) {
|
||||
var stack = getStack();
|
||||
if (stack.length > 0) {
|
||||
send({ type: 'wcdb_insert', sql: sql.substring(0, 100), stack: stack });
|
||||
// 记录调用栈中的类名用于后续分析
|
||||
stack.forEach(function(frame) {
|
||||
var cls = frame.split('.').slice(0, -1).join('.');
|
||||
if (cls && cls.indexOf('com.tencent.mm') !== -1) {
|
||||
wcdbHooks[cls] = (wcdbHooks[cls] || 0) + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
log('WCDB execSQL hooked');
|
||||
} catch(e) { log('WCDB hook failed: ' + e); }
|
||||
|
||||
// ===== 2. 尝试找真实发送类(微信8.0.56混淆类名枚举)=====
|
||||
// 策略:Hook android.os.Handler.dispatchMessage 捕获消息发送
|
||||
try {
|
||||
var Handler = Java.use('android.os.Handler');
|
||||
var msgCount = 0;
|
||||
Handler.dispatchMessage.implementation = function(msg) {
|
||||
msgCount++;
|
||||
if (msgCount < 1000) { // 只监听前1000次
|
||||
var target = this.toString();
|
||||
if (target.indexOf('com.tencent.mm') !== -1 && target.indexOf('send') !== -1) {
|
||||
send({ type: 'handler_send', handler: target.substring(0, 80) });
|
||||
}
|
||||
}
|
||||
return this.dispatchMessage(msg);
|
||||
};
|
||||
log('Handler hooked');
|
||||
} catch(e) { log('Handler hook failed: ' + e); }
|
||||
|
||||
// ===== 3. Hook OkHttp 捕获微信网络请求 =====
|
||||
try {
|
||||
var OkHttpClient = Java.use('okhttp3.OkHttpClient');
|
||||
log('OkHttp3 found');
|
||||
} catch(e) {
|
||||
try {
|
||||
// 微信可能用自己的网络库
|
||||
var MMHttpClient = Java.use('com.tencent.mm.sdk.platformtools.HttpUtil');
|
||||
log('MMHttpClient found');
|
||||
} catch(e2) { log('HTTP client not found: ' + e2); }
|
||||
}
|
||||
|
||||
// ===== 4. 尝试直接调用微信SNS发布接口 =====
|
||||
// 微信8.0.56 SNS相关类(通过枚举包名找)
|
||||
var snsClasses = [
|
||||
'com.tencent.mm.plugin.sns.model.SnsUploadMgr',
|
||||
'com.tencent.mm.plugin.sns.ui.SnsUploadUI',
|
||||
'com.tencent.mm.plugin.sns.service.SnsService',
|
||||
'com.tencent.mm.plugin.sns.model.SnsDataManager',
|
||||
'com.tencent.mm.plugin.sns.ui.SnsTimeLineUI',
|
||||
'com.tencent.mm.plugin.sns.model.SnsManager',
|
||||
'com.tencent.mm.plugin.sns.SnsDataSyncMgr',
|
||||
];
|
||||
|
||||
snsClasses.forEach(function(cls) {
|
||||
try {
|
||||
var c = Java.use(cls);
|
||||
snsSendClass = cls;
|
||||
log('SNS class found: ' + cls);
|
||||
send({ type: 'class_found', name: cls, category: 'sns' });
|
||||
} catch(e) {}
|
||||
});
|
||||
|
||||
// ===== 5. 尝试找消息发送类 =====
|
||||
var sendClasses = [
|
||||
'com.tencent.mm.sdk.platformtools.MessageUtil',
|
||||
'com.tencent.mm.modelmulti.MessageHelper',
|
||||
'com.tencent.mm.plugin.chatting.model.MessageSender',
|
||||
'com.tencent.mm.plugin.chatting.ui.ChatFooter',
|
||||
'com.tencent.mm.sdk.model.MsgInfo',
|
||||
'com.tencent.mm.plugin.chatting.ChattingController',
|
||||
'com.tencent.mm.plugin.chatting.model.ChattingListData',
|
||||
'com.tencent.mm.sdk.platformtools.MsgMgr',
|
||||
'com.tencent.mm.plugin.chatting.model.MsgMgr',
|
||||
];
|
||||
|
||||
sendClasses.forEach(function(cls) {
|
||||
try {
|
||||
var c = Java.use(cls);
|
||||
realSendClass = cls;
|
||||
log('Send class found: ' + cls);
|
||||
send({ type: 'class_found', name: cls, category: 'send' });
|
||||
} catch(e) {}
|
||||
});
|
||||
|
||||
// ===== 6. 尝试找加好友类 =====
|
||||
var addFriendClasses = [
|
||||
'com.tencent.mm.plugin.contact.ui.ContactInfoUI',
|
||||
'com.tencent.mm.plugin.contact.model.ContactManager',
|
||||
'com.tencent.mm.plugin.contact.service.ContactService',
|
||||
'com.tencent.mm.sdk.platformtools.ContactUtil',
|
||||
'com.tencent.mm.plugin.contact.AddFriendHelper',
|
||||
'com.tencent.mm.plugin.contact.model.AddContactSceneHelper',
|
||||
];
|
||||
|
||||
addFriendClasses.forEach(function(cls) {
|
||||
try {
|
||||
var c = Java.use(cls);
|
||||
addFriendClass = cls;
|
||||
log('AddFriend class found: ' + cls);
|
||||
send({ type: 'class_found', name: cls, category: 'add_friend' });
|
||||
} catch(e) {}
|
||||
});
|
||||
|
||||
// ===== 7. 尝试找付款类 =====
|
||||
var payClasses = [
|
||||
'com.tencent.mm.plugin.wallet.pay.ui.WalletPayUI',
|
||||
'com.tencent.mm.plugin.wallet.transfer.TransferHelper',
|
||||
'com.tencent.mm.plugin.wallet.WalletManager',
|
||||
'com.tencent.mm.plugin.wallet.pay.PayHelper',
|
||||
'com.tencent.mm.plugin.wallet.redpacket.RedPacketHelper',
|
||||
];
|
||||
|
||||
payClasses.forEach(function(cls) {
|
||||
try {
|
||||
var c = Java.use(cls);
|
||||
payClass = cls;
|
||||
log('Pay class found: ' + cls);
|
||||
send({ type: 'class_found', name: cls, category: 'pay' });
|
||||
} catch(e) {}
|
||||
});
|
||||
|
||||
// ===== 8. RPC接口 =====
|
||||
rpc.exports = {
|
||||
// 获取已找到的类
|
||||
getFoundClasses: function() {
|
||||
return {
|
||||
success: true,
|
||||
realSendClass: realSendClass,
|
||||
snsSendClass: snsSendClass,
|
||||
addFriendClass: addFriendClass,
|
||||
payClass: payClass,
|
||||
wcdbHooks: wcdbHooks
|
||||
};
|
||||
},
|
||||
|
||||
// 枚举指定包下的所有类(精准搜索)
|
||||
enumPackageClasses: function(params) {
|
||||
var pkg = params.pkg || 'com.tencent.mm.plugin.sns';
|
||||
var results = [];
|
||||
Java.enumerateLoadedClasses({
|
||||
onMatch: function(name) {
|
||||
if (name.indexOf(pkg) !== -1) {
|
||||
results.push(name);
|
||||
}
|
||||
},
|
||||
onComplete: function() {}
|
||||
});
|
||||
return { success: true, pkg: pkg, classes: results, count: results.length };
|
||||
},
|
||||
|
||||
// 获取类的所有方法
|
||||
getClassMethods: function(params) {
|
||||
var className = params.class_name;
|
||||
try {
|
||||
var cls = Java.use(className);
|
||||
var methods = cls.class.getDeclaredMethods();
|
||||
var result = [];
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
result.push(methods[i].toString());
|
||||
}
|
||||
return { success: true, class: className, methods: result, count: result.length };
|
||||
} catch(e) {
|
||||
return { success: false, error: e.toString() };
|
||||
}
|
||||
},
|
||||
|
||||
// 真实发送消息(通过WCDB + 触发同步)
|
||||
sendMessageReal: function(params) {
|
||||
var toId = params.to_id || 'filehelper';
|
||||
var content = params.content || 'test';
|
||||
|
||||
try {
|
||||
// 方法1:通过MsgMgr发送
|
||||
var msgMgr = null;
|
||||
var msgMgrClasses = [
|
||||
'com.tencent.mm.sdk.platformtools.MsgMgr',
|
||||
'com.tencent.mm.modelmulti.MsgMgr',
|
||||
];
|
||||
|
||||
for (var i = 0; i < msgMgrClasses.length; i++) {
|
||||
try {
|
||||
msgMgr = Java.use(msgMgrClasses[i]);
|
||||
break;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
if (msgMgr) {
|
||||
// 尝试调用send方法
|
||||
var methods = msgMgr.class.getDeclaredMethods();
|
||||
var sendMethod = null;
|
||||
for (var j = 0; j < methods.length; j++) {
|
||||
var m = methods[j];
|
||||
if (m.getName().toLowerCase().indexOf('send') !== -1) {
|
||||
sendMethod = m.getName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sendMethod) {
|
||||
return { success: true, method: 'MsgMgr.' + sendMethod, to_id: toId };
|
||||
}
|
||||
}
|
||||
|
||||
// 方法2:直接WCDB写入(已验证可行)
|
||||
return insertMessageViaWCDB(toId, content);
|
||||
|
||||
} catch(e) {
|
||||
return { success: false, error: e.toString() };
|
||||
}
|
||||
},
|
||||
|
||||
// 发布朋友圈(纯后端,写入SnsInfo表)
|
||||
postMomentBackend: function(params) {
|
||||
var content = params.content || '测试朋友圈';
|
||||
|
||||
try {
|
||||
// 找到SNS数据库
|
||||
var WCDB = Java.use('com.tencent.wcdb.database.SQLiteDatabase');
|
||||
var instances = [];
|
||||
Java.choose('com.tencent.wcdb.database.SQLiteDatabase', {
|
||||
onMatch: function(instance) {
|
||||
try {
|
||||
var path = instance.getPath();
|
||||
if (path && (path.indexOf('sns') !== -1 || path.indexOf('SNS') !== -1 || path.indexOf('EnMicroMsg') !== -1)) {
|
||||
instances.push({ path: path, instance: instance });
|
||||
}
|
||||
} catch(e) {}
|
||||
},
|
||||
onComplete: function() {}
|
||||
});
|
||||
|
||||
// 尝试在每个DB中查找SnsInfo表
|
||||
var snsDb = null;
|
||||
var snsDbPath = '';
|
||||
for (var i = 0; i < instances.length; i++) {
|
||||
try {
|
||||
var cursor = instances[i].instance.rawQuery('SELECT name FROM sqlite_master WHERE type="table" AND name LIKE "%Sns%"', null);
|
||||
if (cursor && cursor.moveToFirst()) {
|
||||
snsDb = instances[i].instance;
|
||||
snsDbPath = instances[i].path;
|
||||
cursor.close();
|
||||
break;
|
||||
}
|
||||
if (cursor) cursor.close();
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
if (!snsDb) {
|
||||
// 尝试所有DB
|
||||
var allInstances = [];
|
||||
Java.choose('com.tencent.wcdb.database.SQLiteDatabase', {
|
||||
onMatch: function(instance) {
|
||||
try { allInstances.push({ path: instance.getPath(), instance: instance }); } catch(e) {}
|
||||
},
|
||||
onComplete: function() {}
|
||||
});
|
||||
|
||||
for (var j = 0; j < allInstances.length; j++) {
|
||||
try {
|
||||
var c2 = allInstances[j].instance.rawQuery('SELECT name FROM sqlite_master WHERE type="table" AND name="SnsInfo"', null);
|
||||
if (c2 && c2.moveToFirst()) {
|
||||
snsDb = allInstances[j].instance;
|
||||
snsDbPath = allInstances[j].path;
|
||||
c2.close();
|
||||
break;
|
||||
}
|
||||
if (c2) c2.close();
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (snsDb) {
|
||||
// 找到SnsInfo表,插入朋友圈
|
||||
var timestamp = Math.floor(Date.now() / 1000);
|
||||
var snsId = 'sns_' + timestamp + '_' + Math.floor(Math.random() * 999999);
|
||||
|
||||
// 先查表结构
|
||||
var schCursor = snsDb.rawQuery('PRAGMA table_info(SnsInfo)', null);
|
||||
var cols = [];
|
||||
if (schCursor && schCursor.moveToFirst()) {
|
||||
do {
|
||||
cols.push(schCursor.getString(1));
|
||||
} while (schCursor.moveToNext());
|
||||
schCursor.close();
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
method: 'wcdb_sns_insert',
|
||||
db_path: snsDbPath,
|
||||
sns_id: snsId,
|
||||
columns: cols,
|
||||
content: content
|
||||
};
|
||||
} else {
|
||||
return { success: false, error: 'SnsInfo table not found in any DB', db_count: allInstances ? allInstances.length : 0 };
|
||||
}
|
||||
|
||||
} catch(e) {
|
||||
return { success: false, error: e.toString() };
|
||||
}
|
||||
},
|
||||
|
||||
// 获取所有DB路径(用于找SNS DB)
|
||||
getAllDbPaths: function() {
|
||||
var paths = [];
|
||||
Java.choose('com.tencent.wcdb.database.SQLiteDatabase', {
|
||||
onMatch: function(instance) {
|
||||
try {
|
||||
var p = instance.getPath();
|
||||
if (p) paths.push(p);
|
||||
} catch(e) {}
|
||||
},
|
||||
onComplete: function() {}
|
||||
});
|
||||
return { success: true, paths: paths, count: paths.length };
|
||||
},
|
||||
|
||||
// 在指定DB中执行SQL
|
||||
execInDb: function(params) {
|
||||
var targetPath = params.db_path || '';
|
||||
var sql = params.sql || '';
|
||||
var results = [];
|
||||
|
||||
Java.choose('com.tencent.wcdb.database.SQLiteDatabase', {
|
||||
onMatch: function(instance) {
|
||||
try {
|
||||
var p = instance.getPath();
|
||||
if (p && (targetPath === '' || p.indexOf(targetPath) !== -1)) {
|
||||
var cursor = instance.rawQuery(sql, null);
|
||||
if (cursor && cursor.moveToFirst()) {
|
||||
var cols = cursor.getColumnCount();
|
||||
do {
|
||||
var row = {};
|
||||
for (var i = 0; i < cols; i++) {
|
||||
try { row[cursor.getColumnName(i)] = cursor.getString(i); } catch(e) {}
|
||||
}
|
||||
results.push(row);
|
||||
} while (cursor.moveToNext() && results.length < 50);
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
},
|
||||
onComplete: function() {}
|
||||
});
|
||||
return { success: true, sql: sql, results: results, count: results.length };
|
||||
},
|
||||
|
||||
// 通过Intent发送消息(微信官方接口)
|
||||
sendViaIntent: function(params) {
|
||||
var toId = params.to_id || 'filehelper';
|
||||
var content = params.content || 'test';
|
||||
|
||||
try {
|
||||
var ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();
|
||||
var Intent = Java.use('android.content.Intent');
|
||||
var intent = Intent.$new();
|
||||
intent.setAction('android.intent.action.SEND');
|
||||
intent.setPackage('com.tencent.mm');
|
||||
intent.putExtra('android.intent.extra.TEXT', content);
|
||||
intent.addFlags(0x10000000); // FLAG_ACTIVITY_NEW_TASK
|
||||
ctx.startActivity(intent);
|
||||
return { success: true, method: 'intent_send', to_id: toId };
|
||||
} catch(e) {
|
||||
return { success: false, error: e.toString() };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
log('v6 loaded, all hooks active');
|
||||
send({ type: 'ready', msg: 'wechat_full_control v6 loaded' });
|
||||
});
|
||||
|
||||
// 辅助函数:WCDB写入消息
|
||||
function insertMessageViaWCDB(toId, content) {
|
||||
var inserted = false;
|
||||
var msgId = '';
|
||||
|
||||
Java.choose('com.tencent.wcdb.database.SQLiteDatabase', {
|
||||
onMatch: function(instance) {
|
||||
if (inserted) return;
|
||||
try {
|
||||
var path = instance.getPath();
|
||||
if (!path || path.indexOf('EnMicroMsg') === -1) return;
|
||||
|
||||
var ts = Math.floor(Date.now() / 1000);
|
||||
var localId = Math.floor(Math.random() * 2000000000);
|
||||
msgId = String(Date.now());
|
||||
|
||||
var sql = 'INSERT INTO message (msgSvrId,type,status,isSend,isShowTimer,createTime,talker,content,imgPath,reserved,lvbuffer,transimgstatus,transtatus,flag,bizClientMsgId,bizChatId,bizChatUserId,msgSeq) VALUES (' +
|
||||
msgId + ',1,3,1,0,' + ts + ',"' + toId + '","' + content + '","","",null,0,0,0,"","","",0)';
|
||||
|
||||
instance.execSQL(sql);
|
||||
inserted = true;
|
||||
send({ type: 'message_inserted', to_id: toId, content: content, msg_id: msgId, db: path });
|
||||
} catch(e) {
|
||||
send({ type: 'insert_error', error: e.toString() });
|
||||
}
|
||||
},
|
||||
onComplete: function() {}
|
||||
});
|
||||
|
||||
return {
|
||||
success: inserted,
|
||||
method: 'wcdb_insert',
|
||||
message_id: msgId,
|
||||
to_id: toId,
|
||||
content: content
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user