新增 MariBank/SeaBank PH Root 与 SHPSSDK bypass、riskToken 净化及 Up/Suncorp/ubank 消息 Hook;整理 reverse/ 脚本与 Frida 工具链,并补充当日工作说明文档。
183 lines
5.5 KiB
JavaScript
183 lines
5.5 KiB
JavaScript
'use strict';
|
||
/**
|
||
* MariBank 注册 trace — attach 模式优先,聚焦 Java 层(OkHttp / Gson / 加密包装)
|
||
*/
|
||
const TAG = '[MB-TRACE]';
|
||
const MAX_STR = 2000;
|
||
|
||
function log(msg) {
|
||
console.log(TAG + ' ' + msg);
|
||
}
|
||
|
||
function shouldLogUrl(url) {
|
||
if (!url) return false;
|
||
const u = String(url).toLowerCase();
|
||
return u.indexOf('register') >= 0 || u.indexOf('dfp') >= 0
|
||
|| u.indexOf('risk') >= 0 || u.indexOf('uapi') >= 0;
|
||
}
|
||
|
||
function hexPreview(arr, limit) {
|
||
const n = Math.min(arr.length, limit || 64);
|
||
let hex = '';
|
||
for (let i = 0; i < n; i++) {
|
||
const b = (arr[i] & 0xff).toString(16);
|
||
hex += (b.length === 1 ? '0' : '') + b;
|
||
}
|
||
if (arr.length > n) hex += '...';
|
||
return hex;
|
||
}
|
||
|
||
function dumpJava(tag, obj) {
|
||
if (obj === null || obj === undefined) {
|
||
log(tag + ' = null');
|
||
return;
|
||
}
|
||
try {
|
||
const cls = obj.getClass().getName();
|
||
if (cls === '[B') {
|
||
const arr = Java.cast(obj, Java.use('[B'));
|
||
let text = '';
|
||
try {
|
||
text = Java.use('java.lang.String').$new(arr, 'UTF-8').toString();
|
||
} catch (e) {
|
||
text = '<bin>';
|
||
}
|
||
const show = text.length > MAX_STR ? text.substring(0, MAX_STR) + '...' : text;
|
||
log(tag + ' byte[' + arr.length + '] hex=' + hexPreview(arr, 48) + ' text=' + show);
|
||
return;
|
||
}
|
||
if (cls === 'java.lang.String') {
|
||
const s = Java.cast(obj, Java.use('java.lang.String')).toString();
|
||
const show = s.length > MAX_STR ? s.substring(0, MAX_STR) + '...' : s;
|
||
log(tag + ' String(' + s.length + ') ' + show);
|
||
return;
|
||
}
|
||
log(tag + ' ' + cls + ' = ' + obj.toString());
|
||
} catch (e) {
|
||
log(tag + ' err=' + e);
|
||
}
|
||
}
|
||
|
||
function hookOkHttp() {
|
||
const RealCall = Java.use('okhttp3.RealCall');
|
||
const orig = RealCall.execute;
|
||
RealCall.execute.implementation = function () {
|
||
const req = this.request();
|
||
const url = req.url().toString();
|
||
const method = req.method();
|
||
if (shouldLogUrl(url)) {
|
||
log('HTTP >> ' + method + ' ' + url);
|
||
try {
|
||
const body = req.body();
|
||
if (body) {
|
||
const Buffer = Java.use('okio.Buffer');
|
||
const buf = Buffer.$new();
|
||
body.writeTo(buf);
|
||
const bytes = buf.readByteArray();
|
||
if (bytes) dumpJava(' reqBody', Java.array('byte', bytes));
|
||
}
|
||
} catch (e) {
|
||
log(' reqBody err: ' + e);
|
||
}
|
||
}
|
||
const resp = orig.call(this);
|
||
if (shouldLogUrl(url)) {
|
||
try {
|
||
const peek = resp.peekBody(Java.use('java.lang.Long').parseLong('1048576'));
|
||
const s = peek.string();
|
||
const show = s.length > MAX_STR ? s.substring(0, MAX_STR) + '...' : s;
|
||
log('HTTP << ' + resp.code() + ' ' + show);
|
||
} catch (e) {
|
||
log('HTTP resp err: ' + e);
|
||
}
|
||
}
|
||
return resp;
|
||
};
|
||
log('hooked RealCall.execute');
|
||
}
|
||
|
||
function hookGson() {
|
||
const Gson = Java.use('com.google.gson.Gson');
|
||
Gson.toJson.overload('java.lang.Object').implementation = function (obj) {
|
||
const ret = this.toJson(obj);
|
||
if (ret) {
|
||
const low = ret.toLowerCase();
|
||
if (low.indexOf('mobile') >= 0 || low.indexOf('phone') >= 0
|
||
|| low.indexOf('risktoken') >= 0 || low.indexOf('register') >= 0
|
||
|| low.indexOf('4067') >= 0) {
|
||
const show = ret.length > MAX_STR ? ret.substring(0, MAX_STR) + '...' : ret;
|
||
log('Gson.toJson ' + show);
|
||
}
|
||
}
|
||
return ret;
|
||
};
|
||
log('hooked Gson.toJson');
|
||
}
|
||
|
||
function hookRisk() {
|
||
const vv = Java.use('com.shopee.shpssdkbank.uwuvuvvww.vvuuuuvvv');
|
||
vv.wwvuwuwvu.overload('android.content.Context').implementation = function (ctx) {
|
||
const ret = this.wwvuwuwvu(ctx);
|
||
dumpJava('riskToken', ret);
|
||
return ret;
|
||
};
|
||
log('hooked vvuuuuvvv.wwvuwuwvu');
|
||
}
|
||
|
||
function hookEncryptWrapper() {
|
||
const D = Java.use('com.shopee.bke.lib.jni.utils.d');
|
||
const methods = D.class.getDeclaredMethods();
|
||
for (let i = 0; i < methods.length; i++) {
|
||
const m = methods[i];
|
||
const name = m.getName();
|
||
if (m.getModifiers() & 0x0100) continue;
|
||
try {
|
||
D[name].overloads.forEach(function (ovl) {
|
||
ovl.implementation = function () {
|
||
const args = [].slice.call(arguments);
|
||
log('>> EncryptWrapper.' + name);
|
||
args.forEach(function (a, idx) { dumpJava(' in' + idx, a); });
|
||
const ret = ovl.apply(this, args);
|
||
if (ret && ret.getClass) {
|
||
const cn = ret.getClass().getName();
|
||
if (cn === '[Ljava.lang.String;') {
|
||
const arr = Java.cast(ret, Java.use('[Ljava.lang.String;'));
|
||
for (let j = 0; j < arr.length; j++) dumpJava(' out' + j, arr[j]);
|
||
} else {
|
||
dumpJava(' ret', ret);
|
||
}
|
||
}
|
||
return ret;
|
||
};
|
||
});
|
||
} catch (e) {}
|
||
}
|
||
log('hooked NativeEncryptUtilsWrapper (utils.d)');
|
||
}
|
||
|
||
function installAll() {
|
||
Java.perform(function () {
|
||
log('Java.perform OK pid=' + Process.id);
|
||
try { hookOkHttp(); } catch (e) { log('okhttp fail: ' + e); }
|
||
try { hookGson(); } catch (e) { log('gson fail: ' + e); }
|
||
try { hookRisk(); } catch (e) { log('risk fail: ' + e); }
|
||
try { hookEncryptWrapper(); } catch (e) { log('encrypt fail: ' + e); }
|
||
log('READY — 请在 App 输入号码点 Next');
|
||
});
|
||
}
|
||
|
||
function waitForJava(n) {
|
||
n = n || 0;
|
||
if (typeof Java === 'undefined' || !Java.available) {
|
||
if (n % 5 === 0) log('waiting Java.available attempt=' + n);
|
||
setTimeout(function () { waitForJava(n + 1); }, 500);
|
||
return;
|
||
}
|
||
installAll();
|
||
}
|
||
|
||
setImmediate(function () {
|
||
log('script loaded pid=' + Process.id);
|
||
waitForJava(0);
|
||
});
|