| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- 'use strict';
- var _state = {
- ready: false,
- B: null,
- A: null,
- bInst: null,
- aInst: null,
- RequestBuilder: null,
- ResponseBuilder: null,
- ResponseBody: null,
- MediaType: null,
- Protocol: null,
- ChainEnc: null,
- ChainDec: null,
- capturedUrl: '',
- jniProbeReady: false,
- jniAttached: {}
- };
- var _JNI_NAME_RE = /(enc|encrypt|cipher|aes|codec|raw|sign|decode|decrypt)/i;
- function _tryReadUtf8(p) {
- if (!p || p.isNull()) return '';
- try { return Memory.readUtf8String(p); } catch (_) {}
- return '';
- }
- function _attachNativeProbe(fnPtr, tag) {
- if (!fnPtr || fnPtr.isNull()) return false;
- var key = fnPtr.toString();
- if (_state.jniAttached[key]) return false;
- _state.jniAttached[key] = tag || 'native';
- Interceptor.attach(fnPtr, {
- onEnter: function (args) {
- var input = _tryReadUtf8(args[0]);
- if (!input) input = _tryReadUtf8(args[2]);
- console.log("input:", input);
- console.log("key_ptr:", args[1]);
- try {
- console.log("key:", Memory.readUtf8String(args[1]));
- } catch(e){}
- }
- });
- console.log('[jni-probe] attached:', _state.jniAttached[key], '@', key);
- return true;
- }
- function _hookRegisterNatives(regPtr, label) {
- if (!regPtr || regPtr.isNull()) return;
- Interceptor.attach(regPtr, {
- onEnter: function (args) {
- try {
- var methods = args[2];
- var nMethods = args[3].toInt32();
- if (!methods || methods.isNull()) return;
- if (nMethods <= 0 || nMethods > 1024) return;
- var step = Process.pointerSize * 3;
- for (var i = 0; i < nMethods; i++) {
- var item = methods.add(i * step);
- var namePtr = item.readPointer();
- var sigPtr = item.add(Process.pointerSize).readPointer();
- var fnPtr = item.add(Process.pointerSize * 2).readPointer();
- var name = _tryReadUtf8(namePtr);
- var sig = _tryReadUtf8(sigPtr);
- if (!_JNI_NAME_RE.test(name) && !_JNI_NAME_RE.test(sig)) continue;
- _attachNativeProbe(fnPtr, name + sig);
- }
- } catch (_) {}
- }
- });
- console.log('[jni-probe] hooked RegisterNatives:', label);
- }
- function ensureJniProbe() {
- if (_state.jniProbeReady) return;
- _state.jniProbeReady = true;
- try {
- var resolver = new ApiResolver('module');
- var regs = resolver.enumerateMatchesSync('exports:*!RegisterNatives*');
- for (var i = 0; i < regs.length; i++) {
- _hookRegisterNatives(regs[i].address, regs[i].name);
- }
- } catch (e) {
- console.log('[jni-probe] register hook failed:', e);
- }
- try {
- var mods = Process.enumerateModulesSync();
- for (var m = 0; m < mods.length; m++) {
- var mod = mods[m];
- if (mod.name.indexOf('.so') === -1) continue;
- var exps = [];
- try { exps = Module.enumerateExportsSync(mod.name); } catch (_) { continue; }
- for (var j = 0; j < exps.length; j++) {
- var exp = exps[j];
- if (exp.type !== 'function') continue;
- if (exp.name.indexOf('Java_') !== 0) continue;
- if (!_JNI_NAME_RE.test(exp.name)) continue;
- _attachNativeProbe(exp.address, exp.name + '@' + mod.name);
- }
- }
- } catch (e2) {
- console.log('[jni-probe] export scan failed:', e2);
- }
- }
- function initJava() {
- if (_state.ready) return;
- _state.B = Java.use('gc.b');
- _state.A = Java.use('gc.a');
- _state.RequestBuilder = Java.use('okhttp3.Request$Builder');
- _state.ResponseBuilder = Java.use('okhttp3.Response$Builder');
- _state.ResponseBody = Java.use('okhttp3.ResponseBody');
- _state.MediaType = Java.use('okhttp3.MediaType');
- _state.Protocol = Java.use('okhttp3.Protocol');
- _state.ChainEnc = Java.registerClass({
- name: 'com.jhs.RawCodecEncChain',
- implements: [Java.use('okhttp3.Interceptor$Chain')],
- methods: {
- request: function () { return this.req.value; },
- proceed: function (req) {
- _state.capturedUrl = req.url().toString();
- var body = _state.ResponseBody.create.overload('okhttp3.MediaType', 'java.lang.String')
- .call(_state.ResponseBody, _state.MediaType.parse('application/json; charset=utf-8'), '{}');
- return _state.ResponseBuilder.$new()
- .request(req)
- .protocol(_state.Protocol.valueOf('HTTP_1_1'))
- .code(200)
- .message('OK')
- .body(body)
- .build();
- },
- connection: function () { return null; },
- call: function () { return null; },
- connectTimeoutMillis: function () { return 15000; },
- readTimeoutMillis: function () { return 15000; },
- writeTimeoutMillis: function () { return 15000; },
- withConnectTimeout: function (_t, _u) { return this; },
- withReadTimeout: function (_t, _u) { return this; },
- withWriteTimeout: function (_t, _u) { return this; }
- },
- fields: { req: 'okhttp3.Request' }
- });
- _state.ChainDec = Java.registerClass({
- name: 'com.jhs.RawCodecDecChain',
- implements: [Java.use('okhttp3.Interceptor$Chain')],
- methods: {
- request: function () { return this.req.value; },
- proceed: function (req) {
- var json = '{"raw_data":"' + this.cipher.value + '"}';
- var body = _state.ResponseBody.create.overload('okhttp3.MediaType', 'java.lang.String')
- .call(_state.ResponseBody, _state.MediaType.parse('application/json; charset=utf-8'), json);
- return _state.ResponseBuilder.$new()
- .request(req)
- .protocol(_state.Protocol.valueOf('HTTP_1_1'))
- .code(200)
- .message('OK')
- .body(body)
- .build();
- },
- connection: function () { return null; },
- call: function () { return null; },
- connectTimeoutMillis: function () { return 15000; },
- readTimeoutMillis: function () { return 15000; },
- writeTimeoutMillis: function () { return 15000; },
- withConnectTimeout: function (_t, _u) { return this; },
- withReadTimeout: function (_t, _u) { return this; },
- withWriteTimeout: function (_t, _u) { return this; }
- },
- fields: {
- req: 'okhttp3.Request',
- cipher: 'java.lang.String'
- }
- });
- _state.ready = true;
- }
- function getCachedInstance(cacheKey, C, className) {
- if (_state[cacheKey] !== null) return _state[cacheKey];
- // Fast path: directly construct once and reuse.
- try {
- _state[cacheKey] = C.$new();
- return _state[cacheKey];
- } catch (_) {}
- // Slow fallback: heap-scan only once when constructor is unavailable.
- var out = null;
- Java.choose(className, {
- onMatch: function (o) {
- if (out === null) out = Java.retain(o);
- },
- onComplete: function () {}
- });
- if (out !== null) {
- _state[cacheKey] = out;
- return out;
- }
- throw new Error('unable to resolve instance for ' + className);
- }
- function extractRawData(url) {
- var m = /[?&]raw_data=([^&#]+)/.exec(url);
- if (!m) return '';
- var v = m[1];
- try { v = decodeURIComponent(v); } catch (_) {}
- return v;
- }
- function encryptInner(url) {
- ensureJniProbe();
- initJava();
- _state.capturedUrl = '';
- var req = _state.RequestBuilder.$new().url(url).get().build();
- var ch = _state.ChainEnc.$new();
- ch.req.value = req;
- var b = getCachedInstance('bInst', _state.B, 'gc.b');
- b.b(req, ch);
- var outUrl = _state.capturedUrl;
- return {
- ok: true,
- input_url: url,
- output_url: outUrl,
- raw_data: extractRawData(outUrl)
- };
- }
- function decryptInner(requestUrlWithRawData, responseRawData) {
- initJava();
- var req = _state.RequestBuilder.$new().url(requestUrlWithRawData).get().build();
- var ch = _state.ChainDec.$new();
- ch.req.value = req;
- ch.cipher.value = responseRawData;
- var a = getCachedInstance('aInst', _state.A, 'gc.a');
- var resp = a.intercept(ch);
- var body = resp.body();
- var text = body ? body.string() : '';
- var parsed = null;
- var rawDataPlain = null;
- try {
- parsed = JSON.parse(text);
- if (parsed && parsed.raw_data !== undefined) {
- rawDataPlain = (typeof parsed.raw_data === 'string') ? parsed.raw_data : JSON.stringify(parsed.raw_data);
- }
- } catch (_) {}
- return {
- ok: true,
- request_url: requestUrlWithRawData,
- response_raw_data: responseRawData,
- response_body: text,
- raw_data_plain: rawDataPlain
- };
- }
- rpc.exports = {
- ping: function () {
- return 'ok';
- },
- call: function (params) {
- return new Promise(function (resolve, reject) {
- Java.perform(function () {
- try {
- if (!params || typeof params !== 'object') {
- throw new Error('params must be an object');
- }
- var op = ('' + (params.op || '')).toLowerCase().trim();
- if (op === 'enc') {
- if (!params.url) throw new Error("enc requires params.url");
- resolve(encryptInner('' + params.url));
- return;
- }
- if (op === 'dec') {
- if (!params.request_url) throw new Error("dec requires params.request_url");
- if (!params.response_raw_data) throw new Error("dec requires params.response_raw_data");
- resolve(decryptInner('' + params.request_url, '' + params.response_raw_data));
- return;
- }
- throw new Error("params.op must be 'enc' or 'dec'");
- } catch (e) {
- reject('call failed: ' + e);
- }
- });
- });
- },
- encrypt: function (url) {
- return new Promise(function (resolve, reject) {
- Java.perform(function () {
- try {
- resolve(encryptInner(url));
- } catch (e) {
- reject('encrypt failed: ' + e);
- }
- });
- });
- },
- decrypt: function (requestUrlWithRawData, responseRawData) {
- return new Promise(function (resolve, reject) {
- Java.perform(function () {
- try {
- resolve(decryptInner(requestUrlWithRawData, responseRawData));
- } catch (e) {
- reject('decrypt failed: ' + e);
- }
- });
- });
- }
- };
|