紀錄一下攔截某銀APP的封包流量透過中間人攻擊

紀錄一下脫機的過程,因為一些因素所以,沒有追完全全部,等最近有空再說 反正主要目的就是假設一個APP 我要提取目標其中功能,我是否可以透過 HOOK的方式 將所有APP 流程轉成 API 也就是我要攔截 APP的 HTTPS 的流量 得到 請求的 request 然後在透過 攔截 app 的 簽章加密的 function 試圖找出簽這個 request 的 方法,這樣的步驟都做到的話基本上,你就可以偽造 這個 https 的請求了,當然有些情況可能變得更加棘手,有空再研究 image 目前整個流程就是hook call stack 的 function 看他從請求的 request 到觸發經過哪些 function,試圖找到 簽 request 的 function 再重新構造 request 進行呼叫 這邊要透過 frida-interception-and-unpinning-main.zip

code
frida -U \
    -l ./config.js \
    -l ./native-connect-hook.js \
    -l ./native-tls-hook.js \
    -l ./android/android-proxy-override.js \
    -l ./android/android-system-certificate-injection.js \
    -l ./android/android-certificate-unpinning.js \
    -l ./android/android-certificate-unpinning-fallback.js \
    -f $PACKAGE_ID

按照之前的下法找個安全的地方 進行 hook就可以繞過去了 image 重新啟動就可以透過 hook 去撈取封包

code
Java.perform(function () {
    const log = (tag, message) => console.log(`[${tag}] ${message}`);
    const printStackTrace = () => {
        const Thread = Java.use("java.lang.Thread");
        const stackTrace = Thread.currentThread().getStackTrace();
        log("STACK", "Current Call Stack:");
        stackTrace.forEach(trace => console.log("\t" + trace.toString()));
    };

    // Hook Runtime.exec
    const hookRuntimeExec = () => {
        const Runtime = Java.use("java.lang.Runtime");
        Runtime.exec.overload("java.lang.String").implementation = function (cmd) {
            log("HOOK", `Blocked Command: ${cmd}`);
            if (cmd.includes("su")) {
                return null; // Block 'su' commands
            }
            return this.exec(cmd);
        };
        log("HOOK", "Runtime.exec hooked");
    };

    // Hook File.exists
    const hookFileExists = () => {
        const File = Java.use("java.io.File");
        File.exists.implementation = function () {
            const path = this.getPath();
            if (path.includes("/system/app/Superuser.apk") || path.includes("/su")) {
                log("HOOK", `Fake File.exists for path: ${path}`);
                return false;
            }
            return this.exists();
        };
        log("HOOK", "File.exists hooked");
    };

    // Hook URL.openConnection
    const hookUrlOpenConnection = () => {
        const URL = Java.use("java.net.URL");
        URL.openConnection.overload().implementation = function () {
            const connection = this.openConnection();
            log("HOOK", `Opening connection to URL: ${this.toString()}`);
            printStackTrace(); // Print call stack
            return connection;
        };
        log("HOOK", "URL.openConnection hooked");
    };

    // Hook SSLPinningManager
    const hookSSLPinningManager = () => {
        const SSLPinningManager = Java.use("com.alipay.imobile.network.sslpinning.SSLPinningManager");
        SSLPinningManager.c.implementation = function (arg1, arg2) {
            log("HOOK", "Bypassed SSLPinningManager.c");
            printStackTrace();
            return; // Skip original logic
        };
        log("HOOK", "SSLPinningManager.c hooked");
    };

    // Hook TrustManagerImpl.verifyChain
    const hookTrustManagerImpl = () => {
        const TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
        TrustManagerImpl.verifyChain.implementation = function (chain, authType, session, hostname) {
            log("HOOK", `Bypassed TrustManagerImpl.verifyChain for hostname: ${hostname}`);
            printStackTrace();
            return chain; // Force return valid chain
        };
        log("HOOK", "TrustManagerImpl.verifyChain hooked");
    };

    // Hook AbstractHttpTransport.signRequest
    const hookAbstractHttpTransport = () => {
        const AbstractHttpTransport = Java.use("com.alipay.imobile.network.quake.transport.http.AbstractHttpTransport");
        AbstractHttpTransport.signRequest.overload(
            "com.alipay.imobile.network.quake.Request",
            "com.alipay.imobile.network.quake.util.SignUtil$SignInfo"
        ).implementation = function (request, signInfo) {
            log("HOOK", "AbstractHttpTransport.signRequest called");
            log("INFO", `Request: ${request.toString()}`);
            log("INFO", `SignInfo: ${signInfo}`);
            printStackTrace();
            const signedString = this.signRequest(request, signInfo);
            log("INFO", `Signed String: ${signedString}`);
            return signedString;
        };
        log("HOOK", "AbstractHttpTransport.signRequest hooked");
    };

    // Hook SignUtil.a
    const hookSignUtil = () => {
        const SignUtil = Java.use("com.alipay.imobile.network.quake.util.SignUtil");
        SignUtil.a.overload(
            "android.content.Context",
            "com.alipay.imobile.network.quake.util.SignUtil$SignInfo",
            "java.lang.String",
            "java.lang.String"
        ).implementation = function (context, signInfo, param1, param2) {
            log("HOOK", "SignUtil.a hooked");
            log("INFO", `Context: ${context}`);
            log("INFO", `SignInfo: ${signInfo}`);
            log("INFO", `Param1: ${param1}`);
            log("INFO", `Param2: ${param2}`);
            printStackTrace();
            const result = this.a(context, signInfo, param1, param2);
            log("INFO", `Result (Signature): ${result}`);
            return result;
        };
        log("HOOK", "SignUtil.a(Context, SignInfo, String, String) hooked");
    };

    // Hook SignedJsonProtocolV1.serialize
    const hookSignedJsonProtocol = () => {
        const SignedJsonProtocolV1 = Java.use("com.alipay.imobile.network.quake.protocol.json.SignedJsonProtocolV1");
        SignedJsonProtocolV1.serialize.implementation = function (request) {
            log("HOOK", "SignedJsonProtocolV1.serialize hooked");
            log("INFO", `Request: ${request.toString()}`);
            printStackTrace();
            const result = this.serialize(request);
            log("INFO", `Serialized Data: ${Java.use("java.lang.String").$new(result)}`);
               return result;
        };
        log("HOOK", "SignedJsonProtocolV1.serialize hooked");
    };

    // Initialize all hooks
    const initializeHooks = () => {
        hookRuntimeExec();
        hookFileExists();
        hookUrlOpenConnection();
        hookSSLPinningManager();
        hookTrustManagerImpl();
        hookAbstractHttpTransport();
        hookSignUtil();
        hookSignedJsonProtocol();
        log("INIT", "All hooks initialized successfully");
    };

    initializeHooks();
});

Java.perform(function () {
    var SignUtil = Java.use('com.alipay.imobile.network.quake.util.SignUtil');
    var SecurityGuardParamContext = Java.use('com.alibaba.wireless.security.open.SecurityGuardParamContext');

    // Hook 私有静态方法
    var method = SignUtil.a.overload(
        'com.alibaba.wireless.security.open.SecurityGuardParamContext',
        'java.lang.String',
        'boolean',
        'android.content.Context'
    );

    method.implementation = function (securityGuardParamContext, stringParam, booleanParam, context) {
        console.log('Hooked SignUtil.a (private static method)');
        console.log('String Parameter: ' + stringParam);
        console.log('Boolean Parameter: ' + booleanParam);
        console.log('Context: ' + context);

        // 获取 SecurityGuardParamContext 的 appKey
        try {
            var appKey = securityGuardParamContext.appKey.value;
            console.log('SecurityGuardParamContext.appKey: ' + appKey);
        } catch (e) {
            console.log('Failed to access appKey: ' + e);
        }

        // 调用原始方法
        var result = method.call(this, securityGuardParamContext, stringParam, booleanParam, context);
        console.log('Result: ' + result);
        return result;
    };
});

Java.perform(function () {
    var SignUtil = Java.use('com.alipay.imobile.network.quake.util.SignUtil');
    console.log('Methods in com.alipay.imobile.network.quake.util.SignUtil:');

    var methods = SignUtil.class.getDeclaredMethods();
    for (var i = 0; i < methods.length; i++) {
        console.log(methods[i].toString());
    }
});

Java.perform(function () {
    var AbstractHttpTransport = Java.use('com.alipay.imobile.network.quake.transport.http.AbstractHttpTransport');

    // Hook executeRequest 方法
    AbstractHttpTransport.executeRequest.overload('com.alipay.imobile.network.quake.Request').implementation = function (request) {
        console.log('[HOOwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwK] executeRequest called');

        // 打印传入的 Request 对象内容
        console.log('Request: ' + request.toString());

        // 打印堆栈信息
        console.log('[STACK] Current Call Stack:');
        var stack = Java.use('java.lang.Thread').currentThread().getStackTrace();
        for (var i = 0; i < stack.length; i++) {
            console.log(stack[i].toString());
        }

        // 调用原始方法并获取结果
        var result = this.executeRequest(request);

        // 打印返回结果
        console.log('NetworkResponse: ' + result.toString());

        return result;
    };
});

Java.perform(function () {
    var AbstractHttpTransport = Java.use('com.alipay.imobile.network.quake.transport.http.AbstractHttpTransport');

    AbstractHttpTransport.addRequestHeaders.overload('java.util.Map', 'com.alipay.imobile.network.quake.Request').implementation = function (headers, request) {
        console.log('[HOOwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwK] addRequestHeaders called');

        try {
            // 確認 headers 是否為 Java 對象
            if (headers && headers.getClass) {
                console.log('Headers Class: ' + headers.getClass().getName());

                // 嘗試遍歷 headers 的鍵值對
                if (headers.entrySet) {
                    console.log('Headers Content:');
                    var iterator = headers.entrySet().iterator();
                    while (iterator.hasNext()) {
                        var entry = iterator.next();
                        console.log('  Key: ' + entry.getKey() + ', Value: ' + entry.getValue());
                    }
                } else {
                    console.log('[WARNING] headers does not have an entrySet method, cannot iterate over it.');
                }
            } else {
                console.log('[WARNING] headers is not a valid Java object.');
            }

            // 確認 request 是否為 Java 對象
            if (request && request.getClass) {
                console.log('Request Class: ' + request.getClass().getName());
            } else {
                console.log('[WARNING] request is not a valid Java object.');
            }
        } catch (e) {
            console.log('[ERROR] Exception while processing addRequestHeaders: ' + e.message);
        }

        // 調用原始方法
        this.addRequestHeaders(headers, request);
    };
});

Java.perform(function () {
    var RpcRequest = Java.use('com.alipay.imobile.network.quake.rpc.RpcRequest');

    // Hook RpcRequest 構造方法
    RpcRequest.$init.overload('java.lang.String', 'java.lang.reflect.Type', 'com.alipay.imobile.network.quake.Request$Listener', 'com.alipay.imobile.network.quake.Request$ErrorListener').implementation = function (arg1, arg2, arg3, arg4) {
        console.log('[HOOK] RpcRequest constructor called');

        // 安全檢查並列印參數
        console.log('  arg1 (String): ' + (arg1 !== null ? arg1 : 'null'));
        console.log('  arg2 (Type): ' + (arg2 !== null ? arg2.toString() : 'null'));
        console.log('  arg3 (Listener): ' + (arg3 !== null ? arg3.toString() : 'null'));
        console.log('  arg4 (ErrorListener): ' + (arg4 !== null ? arg4.toString() : 'null'));

        // 調用原始構造方法
        var instance = this.$init(arg1, arg2, arg3, arg4);
        return instance;
    };
});