TngRootBypassHook 增强 captcha 诊断、TigerTally/JNIC 分层与 HWUI 策略;新增逆向脚本、Frida 工具与 UI dump;同步 MariBank SG hook 与 tng_exit_guard 更新。
635 lines
20 KiB
JavaScript
635 lines
20 KiB
JavaScript
'use strict';
|
|
/**
|
|
* MariBank SG 3.2.2 — Java + native attestation / encrypt trace
|
|
* Package: sg.com.maribankmobile.digitalbank
|
|
*
|
|
* SG 差异: 无 utils.d / com.shopee.shpssdk.*,仅 shpssdkbank + uvwuvwuv
|
|
*/
|
|
const TAG = '[MB-NATIVE]';
|
|
const MAX_STR = 4000;
|
|
const MAX_BYTES_LOG = 8192;
|
|
const HOOKED_NATIVE_PTRS = {};
|
|
|
|
const JAVA_TARGETS = [
|
|
'com.shopee.shpssdkbank.wvvvuwwu',
|
|
'com.shopee.shpssdkbank.uwuvuvvww.vvuuuuvvv',
|
|
'com.shopee.shpssdkbank.uwuvuvvww.uvwuuuuuw.vvvvuwwvu',
|
|
'com.shopee.shpssdkbank.uwuvuvvww.wvvuuwvwu',
|
|
'com.shopee.shpssdkbank.uvuwwuvwv.uvwwuuvvw',
|
|
'com.shopee.bke.lib.jni.utils.uvwuvwuv',
|
|
'com.shopee.bke.lib.jni.utils.uvwwwwuv',
|
|
'com.shopee.shpssdkbank.SHPSSDK',
|
|
];
|
|
|
|
const SO_WATCH = [
|
|
'libshpssdk_bank.so',
|
|
'libshpssdk.so',
|
|
'libsdkutils.so',
|
|
'libbkutils.so',
|
|
];
|
|
|
|
function log(msg) {
|
|
send(TAG + ' ' + msg);
|
|
}
|
|
|
|
function jniFn(envPtr, index, ret, args) {
|
|
const funcs = envPtr.readPointer();
|
|
const addr = funcs.add(index * Process.pointerSize).readPointer();
|
|
if (!addr || addr.isNull()) return null;
|
|
return new NativeFunction(addr, ret, args);
|
|
}
|
|
|
|
function jniReadByteArray(envPtr, jarrayPtr) {
|
|
if (!jarrayPtr || jarrayPtr.isNull()) return null;
|
|
try {
|
|
const GetArrayLength = jniFn(envPtr, 171, 'int', ['pointer', 'pointer']);
|
|
const GetByteArrayElements = jniFn(envPtr, 184, 'pointer', ['pointer', 'pointer', 'pointer']);
|
|
const ReleaseByteArrayElements = jniFn(envPtr, 187, 'void', ['pointer', 'pointer', 'pointer', 'int']);
|
|
if (!GetArrayLength || !GetByteArrayElements || !ReleaseByteArrayElements) {
|
|
return jniReadByteArrayArt(envPtr, jarrayPtr);
|
|
}
|
|
const len = GetArrayLength(envPtr, jarrayPtr);
|
|
if (len <= 0 || len > MAX_BYTES_LOG) return { len: len, hex: '', text: '' };
|
|
const elems = GetByteArrayElements(envPtr, jarrayPtr, ptr(0));
|
|
if (!elems || elems.isNull()) return { len: len, hex: '', text: '' };
|
|
const raw = elems.readByteArray(Math.min(len, MAX_BYTES_LOG));
|
|
ReleaseByteArrayElements(envPtr, jarrayPtr, elems, 0);
|
|
return bytesToPreview(raw, len);
|
|
} catch (e) {
|
|
return { len: -1, hex: 'err:' + e, text: '' };
|
|
}
|
|
}
|
|
|
|
function bytesToPreview(raw, len) {
|
|
const arr = new Uint8Array(raw);
|
|
let text = '';
|
|
try {
|
|
text = String.fromCharCode.apply(null, arr);
|
|
if (text.indexOf('\u0000') >= 0 || !/^[\x20-\x7e\r\n\t\u4e00-\u9fff\u0100-\u024f]+$/.test(text.substring(0, Math.min(text.length, 200)))) {
|
|
text = '';
|
|
}
|
|
} catch (e) {
|
|
text = '';
|
|
}
|
|
if (text.length > MAX_STR) text = text.substring(0, MAX_STR) + '...';
|
|
return { len: len, hex: hexPreview(arr, 64), text: text, arr: arr };
|
|
}
|
|
|
|
function jniReadByteArrayArt(envPtr, jarrayPtr) {
|
|
const art = moduleByName('libart.so');
|
|
if (!art) return { len: -1, hex: 'err:no-art', text: '' };
|
|
let sym = null;
|
|
art.enumerateSymbols().forEach(function (s) {
|
|
if (sym) return;
|
|
if (s.name.indexOf('GetByteArrayRegion') >= 0 && s.name.indexOf('JNI') >= 0) {
|
|
sym = s.address;
|
|
}
|
|
});
|
|
if (!sym) return { len: -1, hex: 'err:no-GetByteArrayRegion', text: '' };
|
|
const GetLen = jniFn(envPtr, 171, 'int', ['pointer', 'pointer']);
|
|
const len = GetLen ? GetLen(envPtr, jarrayPtr) : 0;
|
|
if (len <= 0 || len > MAX_BYTES_LOG) return { len: len, hex: '', text: '' };
|
|
const buf = Memory.alloc(len);
|
|
const GetRegion = new NativeFunction(sym, 'void', ['pointer', 'pointer', 'int', 'int', 'pointer']);
|
|
GetRegion(envPtr, jarrayPtr, 0, len, buf);
|
|
return bytesToPreview(buf.readByteArray(len), len);
|
|
}
|
|
|
|
function jniReadJstring(envPtr, jstrPtr) {
|
|
if (!jstrPtr || jstrPtr.isNull()) return '';
|
|
try {
|
|
const GetStringUTFChars = jniFn(envPtr, 169, 'pointer', ['pointer', 'pointer', 'pointer']);
|
|
const ReleaseStringUTFChars = jniFn(envPtr, 170, 'void', ['pointer', 'pointer', 'pointer']);
|
|
if (!GetStringUTFChars || !ReleaseStringUTFChars) return '';
|
|
const chars = GetStringUTFChars(envPtr, jstrPtr, ptr(0));
|
|
if (!chars || chars.isNull()) return '';
|
|
const s = chars.readCString();
|
|
ReleaseStringUTFChars(envPtr, jstrPtr, chars);
|
|
return s || '';
|
|
} catch (e) {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function dumpNativeArgs(methodName, sig, envPtr, args) {
|
|
if (methodName === 'vuwuuwvw' && sig.indexOf('[B[B') >= 0) {
|
|
const a0 = jniReadByteArray(envPtr, args[2]);
|
|
const a1 = jniReadByteArray(envPtr, args[3]);
|
|
if (a0) log(' nat in0 len=' + a0.len + ' hex=' + a0.hex);
|
|
if (a1) log(' nat in1 len=' + a1.len + ' hex=' + a1.hex);
|
|
return;
|
|
}
|
|
if (methodName === 'uvwuuww') {
|
|
const plain = jniReadByteArray(envPtr, args[2]);
|
|
const key = jniReadJstring(envPtr, args[3]);
|
|
const flag = args[4] ? args[4].toInt32() : 0;
|
|
if (plain) {
|
|
log(' nat plain len=' + plain.len + ' hex=' + plain.hex);
|
|
if (plain.text) log(' nat plain utf8=' + plain.text);
|
|
}
|
|
if (key) log(' nat key=' + key + ' flag=' + flag);
|
|
return;
|
|
}
|
|
if (methodName === 'vuwuuuwv' && sig.indexOf('[B[B') >= 0) {
|
|
const a0 = jniReadByteArray(envPtr, args[2]);
|
|
const a1 = jniReadByteArray(envPtr, args[3]);
|
|
if (a0) log(' nat defense in0 len=' + a0.len + ' hex=' + a0.hex);
|
|
if (a1) log(' nat defense in1 len=' + a1.len + ' hex=' + a1.hex);
|
|
}
|
|
}
|
|
|
|
function hexPreview(arr, limit) {
|
|
if (!arr) return '';
|
|
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 += '...(' + arr.length + ')';
|
|
return hex;
|
|
}
|
|
|
|
function dumpBytes(label, jobj) {
|
|
if (jobj === null || jobj === undefined) {
|
|
log(label + ' = null');
|
|
return;
|
|
}
|
|
try {
|
|
const arr = Java.cast(jobj, Java.use('[B'));
|
|
let text = '';
|
|
try {
|
|
text = Java.use('java.lang.String').$new(arr, 'UTF-8').toString();
|
|
} catch (e) {
|
|
text = '';
|
|
}
|
|
const printable = text.length > 0 && text.indexOf('\u0000') < 0;
|
|
if (printable && (text.indexOf('rdVerifyInfo') >= 0 || text.indexOf('REGISTRATION') >= 0
|
|
|| text.indexOf('deviceFingerprint') >= 0 || text.length < MAX_STR)) {
|
|
const show = text.length > MAX_STR ? text.substring(0, MAX_STR) + '...' : text;
|
|
log(label + ' byte[' + arr.length + '] utf8=' + show);
|
|
} else {
|
|
log(label + ' byte[' + arr.length + '] hex=' + hexPreview(arr, 48));
|
|
}
|
|
} catch (e) {
|
|
log(label + ' dump err=' + e);
|
|
}
|
|
}
|
|
|
|
function dumpJava(label, obj) {
|
|
if (obj === null || obj === undefined) {
|
|
log(label + ' = null');
|
|
return;
|
|
}
|
|
try {
|
|
const cls = obj.getClass().getName();
|
|
if (cls === '[B') {
|
|
dumpBytes(label, obj);
|
|
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(label + ' String(' + s.length + ') ' + show);
|
|
return;
|
|
}
|
|
if (cls === '[Ljava.lang.String;') {
|
|
const arr = Java.cast(obj, Java.use('[Ljava.lang.String;'));
|
|
log(label + ' String[' + arr.length + ']');
|
|
for (let i = 0; i < arr.length; i++) dumpJava(label + '[' + i + ']', arr[i]);
|
|
return;
|
|
}
|
|
if (cls === '[[B') {
|
|
const outer = Java.cast(obj, Java.use('[[B'));
|
|
log(label + ' byte[][] len=' + outer.length);
|
|
for (let i = 0; i < outer.length; i++) dumpBytes(label + '[' + i + ']', outer[i]);
|
|
return;
|
|
}
|
|
log(label + ' ' + cls + ' = ' + obj.toString());
|
|
} catch (e) {
|
|
log(label + ' err=' + e);
|
|
}
|
|
}
|
|
|
|
function shouldLogRegisterText(s) {
|
|
if (!s) return false;
|
|
const low = s.toLowerCase();
|
|
return low.indexOf('register') >= 0 || low.indexOf('rdverifyinfo') >= 0
|
|
|| low.indexOf('datakey') >= 0 || low.indexOf('fingerprint') >= 0
|
|
|| low.indexOf('3100012') >= 0 || s.indexOf('|') >= 0;
|
|
}
|
|
|
|
/* ---------- native: dlopen + RegisterNatives ---------- */
|
|
|
|
function moduleExport(moduleName, symbol) {
|
|
if (typeof Module.getExportByName === 'function') {
|
|
try {
|
|
return Module.getExportByName(moduleName, symbol);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
if (typeof Module.findExportByName === 'function') {
|
|
return Module.findExportByName(moduleName, symbol);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function moduleByName(name) {
|
|
if (typeof Process.getModuleByName === 'function') {
|
|
try {
|
|
return Process.getModuleByName(name);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
if (typeof Process.findModuleByName === 'function') {
|
|
return Process.findModuleByName(name);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function moduleByAddress(addr) {
|
|
if (typeof Process.getModuleByAddress === 'function') {
|
|
try {
|
|
return Process.getModuleByAddress(addr);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
if (typeof Process.findModuleByAddress === 'function') {
|
|
return Process.findModuleByAddress(addr);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function hookDlopen() {
|
|
const names = ['android_dlopen_ext', '__loader_android_dlopen_ext', 'dlopen'];
|
|
names.forEach(function (sym) {
|
|
const addr = moduleExport(null, sym);
|
|
if (!addr) return;
|
|
Interceptor.attach(addr, {
|
|
onEnter(args) {
|
|
try {
|
|
this.path = args[0].readCString();
|
|
} catch (e) {
|
|
this.path = '';
|
|
}
|
|
},
|
|
onLeave() {
|
|
if (!this.path) return;
|
|
SO_WATCH.forEach(function (so) {
|
|
if (this.path.indexOf(so) >= 0) log('dlopen ' + this.path);
|
|
}, this);
|
|
},
|
|
});
|
|
log('hooked ' + sym);
|
|
});
|
|
}
|
|
|
|
function findRegisterNatives() {
|
|
const art = moduleByName('libart.so');
|
|
if (!art) return null;
|
|
let found = null;
|
|
art.enumerateSymbols().forEach(function (sym) {
|
|
if (found) return;
|
|
const n = sym.name;
|
|
if (n.indexOf('RegisterNatives') >= 0
|
|
&& n.indexOf('CheckJNI') < 0
|
|
&& n.indexOf('art') >= 0) {
|
|
found = sym.address;
|
|
}
|
|
});
|
|
return found;
|
|
}
|
|
|
|
function hookNativePtr(className, methodName, sig, fnPtr) {
|
|
const key = fnPtr.toString();
|
|
if (HOOKED_NATIVE_PTRS[key]) return;
|
|
HOOKED_NATIVE_PTRS[key] = true;
|
|
const mod = moduleByAddress(fnPtr);
|
|
const modName = mod ? mod.name : '?';
|
|
const off = mod ? fnPtr.sub(mod.base) : fnPtr;
|
|
log('RegisterNatives HOOK ' + className + '.' + methodName + sig
|
|
+ ' @ ' + modName + '+0x' + off.toString(16));
|
|
|
|
try {
|
|
Interceptor.attach(fnPtr, {
|
|
onEnter(args) {
|
|
this.mname = methodName;
|
|
this.msig = sig;
|
|
this.env = args[0];
|
|
log('native>> ' + className + '.' + methodName + sig);
|
|
dumpNativeArgs(methodName, sig, this.env, args);
|
|
},
|
|
onLeave(retval) {
|
|
log('native<< ' + className + '.' + methodName + ' ret=' + retval);
|
|
},
|
|
});
|
|
} catch (e) {
|
|
log('Interceptor.attach fail ' + methodName + ': ' + e);
|
|
}
|
|
}
|
|
|
|
function resolveJClassName(jclassPtr) {
|
|
if (typeof Java === 'undefined' || !Java.available) {
|
|
return '';
|
|
}
|
|
let className = '';
|
|
const run = (typeof Java.performNow === 'function') ? Java.performNow : Java.perform;
|
|
try {
|
|
run(function () {
|
|
className = Java.cast(jclassPtr, Java.use('java.lang.Class')).getName();
|
|
});
|
|
} catch (e) {
|
|
className = '';
|
|
}
|
|
return className;
|
|
}
|
|
|
|
function isInterestingSo(modName) {
|
|
if (!modName) return false;
|
|
return modName.indexOf('shpssdk') >= 0
|
|
|| modName.indexOf('sdkutils') >= 0
|
|
|| modName.indexOf('bkutils') >= 0;
|
|
}
|
|
|
|
function hookRegisterNatives() {
|
|
const addr = findRegisterNatives();
|
|
if (!addr) {
|
|
log('RegisterNatives symbol not found');
|
|
return;
|
|
}
|
|
Interceptor.attach(addr, {
|
|
onEnter(args) {
|
|
const count = args[3].toInt32();
|
|
const methods = args[2];
|
|
const clazz = args[1];
|
|
const className = resolveJClassName(clazz) || '<unknown>';
|
|
const classHit = className.indexOf('shpssdk') >= 0
|
|
|| className.indexOf('jni.utils') >= 0
|
|
|| className.indexOf('bke.lib.jni') >= 0;
|
|
|
|
const ptrSize = Process.pointerSize;
|
|
let loggedClass = false;
|
|
for (let i = 0; i < count; i++) {
|
|
const base = methods.add(i * ptrSize * 3);
|
|
const name = base.readPointer().readCString();
|
|
const sig = base.add(ptrSize).readPointer().readCString();
|
|
const fnPtr = base.add(ptrSize * 2).readPointer();
|
|
const mod = moduleByAddress(fnPtr);
|
|
const modName = mod ? mod.name : '';
|
|
if (!classHit && !isInterestingSo(modName)) continue;
|
|
if (!loggedClass) {
|
|
log('RegisterNatives class=' + className + ' count=' + count);
|
|
loggedClass = true;
|
|
}
|
|
log(' JNI ' + name + sig + ' -> ' + fnPtr + ' (' + modName + ')');
|
|
hookNativePtr(className, name, sig, fnPtr);
|
|
}
|
|
},
|
|
});
|
|
log('hooked RegisterNatives @ ' + addr);
|
|
}
|
|
|
|
/* ---------- Java: hook static native + key methods ---------- */
|
|
|
|
function hookJavaMethod(className, clazz, methodName, isNative, retName, isStatic) {
|
|
try {
|
|
const overloads = clazz[methodName].overloads;
|
|
overloads.forEach(function (ovl) {
|
|
ovl.implementation = function () {
|
|
const args = [].slice.call(arguments);
|
|
log('Java>> ' + className + '.' + methodName
|
|
+ (isStatic ? ' static' : '')
|
|
+ (isNative ? ' native' : '') + ' args=' + args.length);
|
|
args.forEach(function (a, idx) { dumpJava(' in' + idx, a); });
|
|
|
|
const ret = ovl.apply(this, args);
|
|
|
|
if (retName === 'void') {
|
|
log('Java<< ' + methodName + ' void');
|
|
} else if (retName === '[B') {
|
|
dumpBytes(' out', ret);
|
|
} else if (retName === 'java.lang.String') {
|
|
dumpJava(' out', ret);
|
|
} else if (retName === '[[B') {
|
|
dumpJava(' out', ret);
|
|
} else if (retName === 'boolean' || retName === 'int' || retName === 'long') {
|
|
log(' out=' + ret);
|
|
} else {
|
|
dumpJava(' out', ret);
|
|
}
|
|
return ret;
|
|
};
|
|
});
|
|
log('hooked ' + className + '.' + methodName + ' overloads=' + overloads.length
|
|
+ (isNative ? ' native' : '') + (isStatic ? ' static' : ''));
|
|
return 1;
|
|
} catch (e) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function hookClassMethods(className, staticOnly, instanceOnly) {
|
|
let clazz;
|
|
try {
|
|
clazz = Java.use(className);
|
|
} catch (e) {
|
|
log('skip Java class ' + className + ': ' + e);
|
|
return 0;
|
|
}
|
|
|
|
const Modifier = Java.use('java.lang.reflect.Modifier');
|
|
const declared = clazz.class.getDeclaredMethods();
|
|
let hooked = 0;
|
|
|
|
for (let i = 0; i < declared.length; i++) {
|
|
const m = declared[i];
|
|
const isStatic = Modifier.isStatic(m.getModifiers());
|
|
if (staticOnly && !isStatic) continue;
|
|
if (instanceOnly && isStatic) continue;
|
|
|
|
const methodName = m.getName();
|
|
const isNative = Modifier.isNative(m.getModifiers());
|
|
const retName = m.getReturnType().getName();
|
|
hooked += hookJavaMethod(className, clazz, methodName, isNative, retName, isStatic);
|
|
}
|
|
return hooked;
|
|
}
|
|
|
|
function hookShpsSdkFacade() {
|
|
try {
|
|
const SHPSSDK = Java.use('com.shopee.shpssdkbank.SHPSSDK');
|
|
['getRiskToken', 'getRiskSync', 'requestDefense', 'assessRisk'].forEach(function (name) {
|
|
if (!SHPSSDK[name]) return;
|
|
SHPSSDK[name].overloads.forEach(function (ovl) {
|
|
ovl.implementation = function () {
|
|
const args = [].slice.call(arguments);
|
|
log('Java>> SHPSSDK.' + name);
|
|
args.forEach(function (a, idx) { dumpJava(' in' + idx, a); });
|
|
const ret = ovl.apply(this, args);
|
|
dumpJava(' out', ret);
|
|
return ret;
|
|
};
|
|
});
|
|
log('hooked SHPSSDK.' + name);
|
|
});
|
|
} catch (e) {
|
|
log('SHPSSDK facade skip: ' + e);
|
|
}
|
|
}
|
|
|
|
function hookOkHttp() {
|
|
try {
|
|
const RealCall = Java.use('okhttp3.RealCall');
|
|
RealCall.execute.implementation = function () {
|
|
const req = this.request();
|
|
const url = req.url().toString();
|
|
if (url.indexOf('register') >= 0 || url.indexOf('dfp') >= 0 || url.indexOf('uapi') >= 0) {
|
|
log('HTTP>> ' + req.method() + ' ' + url);
|
|
}
|
|
const resp = this.execute.call(this);
|
|
if (url.indexOf('register') >= 0) {
|
|
try {
|
|
const peek = resp.peekBody(Java.use('java.lang.Long').parseLong('1048576'));
|
|
log('HTTP<< register ' + peek.string());
|
|
} catch (e) {
|
|
log('HTTP<< register peek err=' + e);
|
|
}
|
|
}
|
|
return resp;
|
|
};
|
|
log('hooked OkHttp RealCall.execute');
|
|
} catch (e) {
|
|
log('OkHttp skip: ' + e);
|
|
}
|
|
}
|
|
|
|
function hookGsonRegister() {
|
|
try {
|
|
const Gson = Java.use('com.google.gson.Gson');
|
|
Gson.toJson.overload('java.lang.Object').implementation = function (obj) {
|
|
const ret = this.toJson(obj);
|
|
if (shouldLogRegisterText(ret)) {
|
|
const show = ret.length > MAX_STR ? ret.substring(0, MAX_STR) + '...' : ret;
|
|
log('Gson.toJson REGISTRATION len=' + ret.length + ' ' + show);
|
|
}
|
|
return ret;
|
|
};
|
|
log('hooked Gson.toJson');
|
|
} catch (e) {
|
|
log('Gson skip: ' + e);
|
|
}
|
|
}
|
|
|
|
function hookRiskTokenEntry() {
|
|
try {
|
|
const V = Java.use('com.shopee.shpssdkbank.uwuvuvvww.vvuuuuvvv');
|
|
hookJavaMethod(
|
|
'com.shopee.shpssdkbank.uwuvuvvww.vvuuuuvvv',
|
|
V, 'wwvuwuwvu', false, 'java.lang.String', true);
|
|
} catch (e) {
|
|
log('vvuuuuvvv skip: ' + e);
|
|
}
|
|
}
|
|
|
|
function hookEncryptHelper() {
|
|
hookClassMethods('com.shopee.bke.lib.jni.utils.uvwwwwuv', false, true);
|
|
}
|
|
|
|
function isAdbSettingKey(key) {
|
|
if (!key) return false;
|
|
const lower = key.toLowerCase();
|
|
return lower.indexOf('adb') >= 0
|
|
|| lower === 'development_settings_enabled'
|
|
|| lower.indexOf('wireless_debug') >= 0;
|
|
}
|
|
|
|
function hookAdbBypassJava() {
|
|
try {
|
|
const fakeInt = function (key) {
|
|
if (isAdbSettingKey(key)) {
|
|
log('fake Settings int ' + key + ' -> 0');
|
|
return 0;
|
|
}
|
|
return null;
|
|
};
|
|
const fakeStr = function (key) {
|
|
if (isAdbSettingKey(key)) {
|
|
log('fake Settings str ' + key + ' -> 0');
|
|
return '0';
|
|
}
|
|
return null;
|
|
};
|
|
|
|
['Global', 'Secure', 'System'].forEach(function (bucket) {
|
|
const Cls = Java.use('android.provider.Settings$' + bucket);
|
|
Cls.getInt.overloads.forEach(function (ovl) {
|
|
ovl.implementation = function () {
|
|
const key = arguments[1];
|
|
const f = fakeInt(String(key));
|
|
if (f !== null) return f;
|
|
return ovl.apply(this, arguments);
|
|
};
|
|
});
|
|
if (Cls.getString) {
|
|
Cls.getString.overloads.forEach(function (ovl) {
|
|
ovl.implementation = function () {
|
|
const key = arguments[1];
|
|
const f = fakeStr(String(key));
|
|
if (f !== null) return f;
|
|
return ovl.apply(this, arguments);
|
|
};
|
|
});
|
|
}
|
|
});
|
|
|
|
const SysProp = Java.use('android.os.SystemProperties');
|
|
SysProp.get.overload('java.lang.String').implementation = function (key) {
|
|
if (key === 'init.svc.adbd' || key === 'init.svc.adb_wifi') {
|
|
log('fake SystemProperties ' + key + ' -> stopped');
|
|
return 'stopped';
|
|
}
|
|
return this.get(key);
|
|
};
|
|
log('hooked ADB Settings/SystemProperties bypass');
|
|
} catch (e) {
|
|
log('ADB Java bypass skip: ' + e);
|
|
}
|
|
}
|
|
|
|
function installJavaHooks() {
|
|
hookAdbBypassJava();
|
|
let total = 0;
|
|
JAVA_TARGETS.forEach(function (cn) {
|
|
total += hookClassMethods(cn, true, false);
|
|
});
|
|
hookRiskTokenEntry();
|
|
hookEncryptHelper();
|
|
hookShpsSdkFacade();
|
|
hookOkHttp();
|
|
hookGsonRegister();
|
|
log('Java hooks installed methods=' + total + ' pid=' + Process.id);
|
|
log('READY SG — Sign up -> +65 -> Next (watch native>> / Gson / HTTP)');
|
|
}
|
|
|
|
function waitForJava(n) {
|
|
n = n || 0;
|
|
if (typeof Java === 'undefined' || !Java.available) {
|
|
if (n % 10 === 0) log('waiting Java.available attempt=' + n);
|
|
setTimeout(function () { waitForJava(n + 1); }, 500);
|
|
return;
|
|
}
|
|
Java.perform(function () {
|
|
installJavaHooks();
|
|
});
|
|
}
|
|
|
|
setImmediate(function () {
|
|
log('SG native trace loaded pid=' + Process.id);
|
|
hookDlopen();
|
|
hookRegisterNatives();
|
|
waitForJava(0);
|
|
});
|