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

271 lines
12 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.

/**
* 微信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' });
});