feat: publish workphone SDK deployment and API docs
This commit is contained in:
270
sdk/agent/hook/wechat_class_finder.js
Normal file
270
sdk/agent/hook/wechat_class_finder.js
Normal file
@@ -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' });
|
||||
});
|
||||
Reference in New Issue
Block a user