diff --git a/sdk/agent/hook/wechat_class_finder.js b/sdk/agent/hook/wechat_class_finder.js new file mode 100644 index 0000000000..134ec83194 --- /dev/null +++ b/sdk/agent/hook/wechat_class_finder.js @@ -0,0 +1,270 @@ +/** + * 微信8.0.56 类名发现脚本 + * 通过ClassLoader枚举找到真实类名 + */ +'use strict'; + +function getWechatLoader() { + var wechatLoader = null; + Java.enumerateClassLoaders({ + onMatch: function(loader) { + if (wechatLoader) return; + try { + loader.loadClass('com.tencent.mm.app.Application'); + wechatLoader = loader; + } catch(e) {} + }, + onComplete: function() {} + }); + return wechatLoader; +} + +rpc.exports = { + // 获取ChattingUIFragment所有方法 + getChattingMethods: function() { + return Java.performNow(function() { + var loader = getWechatLoader(); + if (!loader) return { error: 'no loader' }; + try { + var cls = loader.loadClass('com.tencent.mm.ui.chatting.ChattingUIFragment'); + var methods = cls.getDeclaredMethods(); + var result = []; + for (var i = 0; i < methods.length; i++) { + var m = methods[i]; + var params = m.getParameterTypes().map(function(p) { return p.getName(); }); + result.push({ + name: m.getName(), + params: params, + ret: m.getReturnType().getName() + }); + } + return { success: true, count: result.length, methods: result }; + } catch(e) { + return { success: false, error: String(e) }; + } + }); + }, + + // 搜索双字母类名 + findDoubleLetterClasses: function() { + return Java.performNow(function() { + var loader = getWechatLoader(); + if (!loader) return { error: 'no loader' }; + var letters = 'abcdefghijklmnopqrstuvwxyz'; + var found = []; + var prefixes = [ + 'com.tencent.mm.modelmulti.', + 'com.tencent.mm.plugin.messenger.foundation.a.', + 'com.tencent.mm.plugin.messenger.foundation.b.', + 'com.tencent.mm.plugin.messenger.foundation.c.', + ]; + for (var p = 0; p < prefixes.length; p++) { + for (var i = 0; i < letters.length; i++) { + for (var j = 0; j < letters.length; j++) { + var clsName = prefixes[p] + letters[i] + letters[j]; + try { + var cls = loader.loadClass(clsName); + var methods = cls.getDeclaredMethods(); + var mNames = []; + for (var k = 0; k < methods.length; k++) { + var params = methods[k].getParameterTypes().map(function(t) { return t.getName(); }); + mNames.push(methods[k].getName() + '(' + params.join(',') + ')'); + } + found.push({ cls: clsName, methods: mNames }); + send({ type: 'found', cls: clsName, methods: mNames }); + } catch(e) {} + } + } + } + return { success: true, count: found.length, found: found }; + }); + }, + + // Hook SQLite insert监听消息写入 + hookMessageInsert: function() { + try { + var WCDB = Java.use('com.tencent.wcdb.database.SQLiteDatabase'); + WCDB.insert.overload( + 'java.lang.String', + 'java.lang.String', + 'android.content.ContentValues' + ).implementation = function(table, nullCol, values) { + var result = this.insert(table, nullCol, values); + if (table === 'message') { + try { + var isSend = values.getAsInteger('isSend'); + if (isSend && parseInt(isSend.toString()) === 1) { + var content = values.getAsString('content'); + var talker = values.getAsString('talker'); + // 捕获调用栈 + var Exception = Java.use('java.lang.Exception'); + var ex = Exception.$new('stack capture'); + var stack = ex.getStackTrace(); + var stackInfo = []; + for (var i = 0; i < Math.min(stack.length, 30); i++) { + var frame = stack[i]; + var cn = frame.getClassName(); + if (cn.indexOf('com.tencent.mm') !== -1) { + stackInfo.push(cn + '.' + frame.getMethodName() + ':' + frame.getLineNumber()); + } + } + send({ + type: 'message_sent_captured', + talker: talker, + content: content ? content.substring(0, 80) : '', + stack: stackInfo + }); + } + } catch(e2) {} + } + return result; + }; + return { success: true, msg: 'WCDB insert hooked' }; + } catch(e) { + return { success: false, error: String(e) }; + } + }, + + // 通过反射找到DB实例并获取密钥 + findDBInstance: function() { + return Java.performNow(function() { + var loader = getWechatLoader(); + if (!loader) return { error: 'no loader' }; + + // 尝试找到MicroMsgDBHelper + var helperCandidates = [ + 'com.tencent.mm.storage.MicroMsgDBHelper', + 'com.tencent.mm.storage.DatabaseHelper', + 'com.tencent.mm.storage.MicroMsgDB', + 'com.tencent.mm.storage.db.MicroMsgDB', + ]; + var found = []; + for (var i = 0; i < helperCandidates.length; i++) { + try { + var cls = loader.loadClass(helperCandidates[i]); + var methods = cls.getDeclaredMethods(); + var mNames = []; + for (var j = 0; j < methods.length; j++) { + mNames.push(methods[j].getName()); + } + found.push({ cls: helperCandidates[i], methods: mNames.slice(0, 10) }); + } catch(e) {} + } + return { success: true, found: found }; + }); + }, + + // 通过WCDB直接操作DB(用已打开的DB连接) + queryViaWCDB: function(sql) { + return Java.performNow(function() { + try { + // 获取已打开的DB实例(通过Application的静态字段) + var WCDB = Java.use('com.tencent.wcdb.database.SQLiteDatabase'); + // 找到所有WCDB实例 + var instances = []; + Java.choose('com.tencent.wcdb.database.SQLiteDatabase', { + onMatch: function(instance) { + try { + var path = instance.getPath(); + instances.push({ path: path, open: instance.isOpen() }); + } catch(e) {} + }, + onComplete: function() {} + }); + return { success: true, instances: instances, count: instances.length }; + } catch(e) { + return { success: false, error: String(e) }; + } + }); + }, + + // 通过Java.choose找到已打开的DB并执行查询 + executeOnOpenDB: function(sql) { + return Java.performNow(function() { + try { + var results = []; + var executed = false; + Java.choose('com.tencent.wcdb.database.SQLiteDatabase', { + onMatch: function(db) { + if (executed) return; + try { + var path = db.getPath(); + if (path && path.indexOf('EnMicroMsg') !== -1 && db.isOpen()) { + // 执行查询 + var cursor = db.rawQuery(sql, null); + if (cursor) { + var cols = cursor.getColumnNames(); + while (cursor.moveToNext()) { + var row = {}; + for (var i = 0; i < cols.length; i++) { + try { row[cols[i]] = cursor.getString(i); } catch(e) { row[cols[i]] = null; } + } + results.push(row); + } + cursor.close(); + executed = true; + send({ type: 'db_query_ok', path: path, rows: results.length }); + } + } + } catch(e) { + send({ type: 'db_query_err', error: String(e).substring(0, 100) }); + } + }, + onComplete: function() {} + }); + return { success: executed, results: results, count: results.length }; + } catch(e) { + return { success: false, error: String(e) }; + } + }); + }, + + // 通过Java.choose找到DB并INSERT消息 + insertViaOpenDB: function(params) { + return Java.performNow(function() { + var toId = params.to_id; + var content = params.content; + try { + var inserted = false; + var dbPath = ''; + Java.choose('com.tencent.wcdb.database.SQLiteDatabase', { + onMatch: function(db) { + if (inserted) return; + try { + var path = db.getPath(); + if (path && path.indexOf('EnMicroMsg') !== -1 && db.isOpen()) { + dbPath = path; + var ContentValues = Java.use('android.content.ContentValues'); + var cv = ContentValues.$new(); + var now = Date.now(); + cv.put('msgId', Java.use('java.lang.Long').$new(now)); + cv.put('type', Java.use('java.lang.Integer').$new(1)); + cv.put('isSend', Java.use('java.lang.Integer').$new(1)); + cv.put('talker', toId); + cv.put('content', content); + cv.put('createTime', Java.use('java.lang.Long').$new(now)); + cv.put('status', Java.use('java.lang.Integer').$new(2)); + cv.put('imgPath', ''); + var rowId = db.insert('message', null, cv); + if (rowId > 0) { + inserted = true; + send({ type: 'message_inserted', row_id: rowId.toString(), path: path }); + } + } + } catch(e) { + send({ type: 'insert_err', error: String(e).substring(0, 150) }); + } + }, + onComplete: function() {} + }); + return { success: inserted, db_path: dbPath, method: 'wcdb_direct_insert' }; + } catch(e) { + return { success: false, error: String(e) }; + } + }); + } +}; + +Java.perform(function() { + send({ type: 'ready', msg: 'wechat_class_finder loaded' }); +});