• 针对Android App隐私信息检测


    针对Android App隐私信息检测

    尝试采用Frida进行处理。

    目前的一个思路就是trace app中所有调用系统函数的功能,这个方法不够细致,无法判断是app自身调用的还是app调用的sdk调用的。

    https://github.com/zhengjim/camille

    昨天尝试了下,发现并没有多好使,还需要继续研究下,自己写个demo试试。

    只需要hook相应的隐私API就算是触犯了隐私信息的采集了。

    //获取手机通信录
    
    function getPhoneAddressBook() {
    
        var contacts_uri = Java.use("android.provider.ContactsContract$Contacts").CONTENT_URI.value.toString();
    
    
    
        var contentResolver = Java.use("android.content.ContentResolver");
    
        contentResolver.query.overload('android.net.Uri', '[Ljava.lang.String;', 'android.os.Bundle', 'android.os.CancellationSignal').implementation = function (uri, str, bundle, sig) {
    
            if (uri == contacts_uri) {
    
                alertSend("获取手机通信录", "获取uri为:" + uri)
    
            }
    
            return this.query(uri, str, bundle, sig);
    
        }
    
    }
    

    这样就可以了,无非就是看隐私API都有哪些,有了就行了

    还有就是如果是这种写死的方式的话,可能不太方便不适合维护,所以,需要一个单独的接口传入类、类中的函数名,就能够批次hook即可。

    可以参考这个:https://github.com/r0ysue/r0tracer

    import { alertSend2 } from "./alertSend";
    
    var isLite = false;
    
    
    
    // trace单个类的所有静态和实例方法包括构造方法 trace a specific Java Method
    
    function traceMethod(targetClassMethod: any) {
    
        var delim = targetClassMethod.lastIndexOf(".");
    
        if (delim === -1) return;
    
        var targetClass = targetClassMethod.slice(0, delim)
    
        var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length)
    
        var hook = Java.use(targetClass);
    
        var overloadCount = hook[targetMethod].overloads.length;
    
        for (var i = 0; i < overloadCount; i++) {
    
            hook[targetMethod].overloads[i].implementation = function () {
    
                //初始化输出
    
                var output = "";
    
                var args = "";
    
                var retvalContent = "";
    
                var stackTrace = "";
    
                //进入函数
    
                output = output.concat("\n*** entered " + targetClassMethod);
    
                output = output.concat("\r\n")
    
                //参数
    
                var retval = this[targetMethod].apply(this, arguments);
    
                if (!isLite) {
    
                    // 入参
    
                    for (var j = 0; j < arguments.length; j++) {
    
                        args = args.concat("arg[" + j + "]: " + arguments[j] + " => " + JSON.stringify(arguments[j]));
    
                        args = args.concat("\r\n")
    
                    }
    
                    //调用栈
    
                    stackTrace = stackTrace.concat(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
    
                    //返回值
    
                    retvalContent = retvalContent.concat("\nretval: " + retval + " => " + JSON.stringify(retval));
    
    
    
                }
    
                alertSend2("", targetClassMethod, args, retvalContent, stackTrace);
    
                return retval;
    
            }
    
        }
    
    }
    
    
    
    export function traceClassMethod(params: Map<any, any[]>) {
    
        Java.perform(() => {
    
            params.forEach((methodNames, targetClass) => {
    
                //Java.use是新建一个对象哈,大家还记得么?
    
                var hook = Java.use(targetClass);
    
                //利用反射的方式,拿到当前类的所有方法
    
                var methods = hook.class.getDeclaredMethods();
    
                //建完对象之后记得将对象释放掉哈
    
                hook.$dispose;
    
                //将方法名保存到数组中
    
                var parsedMethods = new Set();
    
                methods.forEach(function (method: any) {
    
                    methodNames.forEach(function (methodName) {
    
                        if (method.toString().indexOf(methodName) != -1) {
    
                            parsedMethods.add(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
    
                        }
    
                    })
    
                });
    
                //对数组中所有的方法进行hook,
    
                parsedMethods.forEach(function (targetMethod: any) {
    
                    traceMethod(targetClass + "." + targetMethod);
    
                });
    
            })
    
        })
    
    }
    
    
    import { traceClassMethod} from "./trace";
    
    
    
    Java.perform(function() {
    
        console.log("隐私合规检测敏感接口开始监控...");
    
        send({"type": "isHook"});
    
    
    
        let map = new Map<any, any[]>();
    
        // class methods
    
        map.set("android.app.ApplicationPackageManager", ["getInstalledPackages", "getInstalledApplications", "queryIntentActivities", "getApplicationInfo"]);
    
        map.set("android.media.AudioRecord", ["startRecording"]);
    
        traceClassMethod(map);
    
    });
    
    export function alertSend2(action: string, className: string, input: string, output: string, stackTrace: string) {
    
        let myDate = new Date();
    
        let _time = myDate.getFullYear() + "-" + myDate.getMonth() + "-" + myDate.getDate() + " " + myDate.getHours() + ":" + myDate.getMinutes() + ":" + myDate.getSeconds();
    
        send({"type": "notice", "time": _time, "action": action, "className": className, "input": input, "output": output, "stacks": stackTrace});
    
    }
    import sys
    
    import time
    
    import frida
    
    import signal
    
    import os
    
    
    
    #from screen import *
    
    
    
    def frida_hook(app_name, wait_time=0):
    
        #isHook = True
    
    
    
        def my_message_handler(message, payload):
    
            if message["type"] == "error":
    
                print(message)
    
                os.kill(os.getpid(), signal.SIGTERM)
    
                return
    
            if message['type'] == 'send':
    
                data = message["payload"]
    
                if data['type'] == "notice":
    
                    #take_screenshot(None, app_name + "/")
    
                    alert_time = data['time']
    
                    action = data['action']
    
                    className = data['className']
    
                    input1 = data['input']
    
                    output1 = data['output']
    
                    stacks = data['stacks']
    
    
    
                    # todo,以后将这些数据发送给后端,最好就是保存这些数据,并将图片的位置跟这些数据存储在一起。
    
                    print("------------------------------start---------------------------------")
    
                    print("[*] 行为时间:\n{0},\nAPP行为:\n{1},\n类名:\n{2},\n入参:\n{3},\n出参:{4}\n".format(alert_time, action, className, input1, output1))
    
                    print("[*] 调用堆栈:")
    
                    print(stacks)
    
                    print("-------------------------------end----------------------------------")
    
    
    
                if data['type'] == "app_name":
    
                    get_app_name = data['data']
    
                    my_data = False if get_app_name == app_name else True
    
                    script.post({"my_data": my_data})
    
    
    
                if data['type'] == "isHook":
    
                    global isHook
    
                    isHook = True
    
    
    
        #session = None
    
        try:
    
            device = frida.get_usb_device()
    
            pid = device.spawn([app_name])
    
        except Exception as e:
    
            print("[*] hook error")
    
            print(e)
    
            exit()
    
    
    
        time.sleep(1)
    
        session = device.attach(pid)
    
        time.sleep(1)
    
        with open("../frida-agent-example/_agent.js", encoding="utf-8") as f:
    
            script_read = f.read()
    
    
    
        script = session.create_script(script_read)
    
        script.on("message", my_message_handler)
    
        script.load()
    
        time.sleep(1)
    
        try:
    
            device.resume(pid)
    
        except Exception as e:
    
            print("[*] hook error")
    
            print(e)
    
            exit()
    
    
    
        # 等待frida-server发送消息过来,确保是否已经完成attach
    
        wait_time += 1
    
        time.sleep(wait_time)
    
        if isHook:
    
            def stop(signum, frame):
    
                print('[*] You have stoped hook.')
    
                session.detach()
    
                exit()
    
    
    
            signal.signal(signal.SIGINT, stop)
    
            signal.signal(signal.SIGTERM, stop)
    
            sys.stdin.read()
    
        else:
    
            print("[*] hook fail, try delaying hook, adjusting delay time")
    
    
    
    if __name__ == '__main__':
    
        print("start frida hook")
    
    
    
        # 全局变量
    
        isHook = False
    
        frida_hook("com.mepride.freepods")
    
        print("end")
    

    针对这种spawn不上的

    attach也不行的

    Bypass :

    // 
    
    export var ByPassTracerPid = () => {
    
        let fgetsPtr = Module.findExportByName("libc.so", "fgets");
    
        if (null != fgetsPtr) {
    
            let fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
    
            Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) {
    
                var retval = fgets(buffer, size, fp);
    
                var bufstr = readMemory(buffer);
    
                //var bufstr = buffer?.toString();
    
                if (null != bufstr) {
    
                    if (bufstr.indexOf("TracerPid:") > -1) {
    
                        writeMemory(buffer, "TracerPid:\t0");
    
                        console.log("tracerpid replaced: " + readMemory(buffer));
    
                    }
    
                }
    
                return retval;
    
            }, 'pointer', ['pointer', 'int', 'pointer']));
    
        }
    
    }
    
    
    
    
    
    function writeMemory(addr: NativePointer, str: string) {
    
        Memory.protect(addr, str.length, 'rwx');
    
        addr.writeUtf8String(str);
    
    }
    
    
    
    function readMemory(addr: NativePointer) {
    
        return addr.readUtf8String();
    
    }
    

    https://github.com/chame1eon/jnitrace-engine

    https://github.com/deathmemory/FridaContainer

    https://frida.re/docs/javascript-api/

    1. 稳定的frida环境 => 8 + 12.8.0 \ 10 + 14.2.18
    2. Trace jni https://github.com/chame1eon/jnitrace-engine
    3. Bypass anti frida
    4. Bypass ssl panning https://github.com/r0ysue/r0capture

    Bypass anti frida

    export function antiAntiFrida() {
    
        var strstr = Module.findExportByName(null, "strstr");
    
        if (strstr !== null) {
    
            Interceptor.attach(strstr, {
    
                onEnter: function (args) {
    
                    this.frida = Boolean(0);
    
    
    
                    this.haystack = args[0];
    
                    this.needle = args[1];
    
    
    
                    if (this.haystack.readCString() !== null && this.needle.readCString() !== null) {
    
                        if (this.haystack.readCString().indexOf("frida") !== -1 || this.needle.readCString().indexOf("frida") !== -1 ||
    
                            this.haystack.readCString().indexOf("gum-js-loop") !== -1 || this.needle.readCString().indexOf("gum-js-loop") !== -1 ||
    
                            this.haystack.readCString().indexOf("gmain") !== -1 || this.needle.readCString().indexOf("gmain") !== -1 ||
    
                            this.haystack.readCString().indexOf("linjector") !== -1 || this.needle.readCString().indexOf("linjector") !== -1) {
    
                            this.frida = Boolean(1);
    
                        }
    
                    }
    
                },
    
                onLeave: function (retval) {
    
                    if (this.frida) {
    
                        retval.replace(ptr("0x0"));
    
                    }
    
    
    
                }
    
            })
    
            console.log("anti anti-frida");
    
        }
    
    }
    

    Bypass ssl pinning:https://github.com/sensepost/objection/blob/master/agent/src/android/pinning.ts

    import { wrapJavaPerform } from "./libjava";
    
    import {
    
        ArrayList, CertificatePinner, PinningTrustManager, SSLCertificateChecker,
    
        SSLContext, TrustManagerImpl, X509TrustManager,
    
    } from "./types";
    
    
    
    export function byPassSSLPinning() {
    
        const sslContextEmptyTrustManager = (): any => {
    
            // -- Sample Java
    
            //
    
            // "Generic" TrustManager Example
    
            //
    
            // TrustManager[] trustAllCerts = new TrustManager[] {
    
            //     new X509TrustManager() {
    
            //         public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    
            //             return null;
    
            //         }
    
            //         public void checkClientTrusted(X509Certificate[] certs, String authType) {  }
    
            //         public void checkServerTrusted(X509Certificate[] certs, String authType) {  }
    
            //     }
    
            // };
    
            // SSLContext sslcontect = SSLContext.getInstance("TLS");
    
            // sslcontect.init(null, trustAllCerts, null);
    
            return wrapJavaPerform(() => {
    
                const x509TrusManager: X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
    
                const sSLContext: SSLContext = Java.use("javax.net.ssl.SSLContext");
    
    
    
                // Some 'anti-frida' detections will scan /proc/<pid>/maps.
    
                // Rename the tempFileNaming prefix as this could end up in maps.
    
                // https://github.com/frida/frida-java-bridge/blob/8b3790f7489ff5be7b19ddaccf5149d4e7738460/lib/class-factory.js#L94
    
                if (Java.classFactory.tempFileNaming.prefix == 'frida') {
    
                    Java.classFactory.tempFileNaming.prefix = 'onetwothree';
    
                }
    
    
    
                // Implement a new TrustManager
    
                // ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8
    
                const TrustManager: X509TrustManager = Java.registerClass({
    
                    implements: [x509TrusManager],
    
                    methods: {
    
                        // tslint:disable-next-line:no-empty
    
                        checkClientTrusted(chain, authType) { },
    
                        // tslint:disable-next-line:no-empty
    
                        checkServerTrusted(chain, authType) { },
    
                        getAcceptedIssuers() {
    
                            return [];
    
                        },
    
                    },
    
                    // todo 这个是干嘛的
    
                    name: "com.sensepost.test.TrustManager",
    
                });
    
    
    
                // Prepare the TrustManagers array to pass to SSLContext.init()
    
                const TrustManagers: X509TrustManager[] = [TrustManager.$new()];
    
    
    
                // Get a handle on the init() on the SSLContext class
    
                const SSLContextInit = sSLContext.init.overload(
    
                    "[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom");
    
                // Override the init method, specifying our new TrustManager
    
                SSLContextInit.implementation = function (keyManager: any, trustManager: any, secureRandom: any) {
    
    
    
                    SSLContextInit.call(this, keyManager, TrustManagers, secureRandom);
    
                };
    
    
    
                return SSLContextInit;
    
            });
    
        };
    
    
    
        const okHttp3CertificatePinnerCheck = (): any | undefined => {
    
            // -- Sample Java
    
            //
    
            // Example used to test this bypass.
    
            //
    
            // String hostname = "swapi.co";
    
            // CertificatePinner certificatePinner = new CertificatePinner.Builder()
    
            //         .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
    
            //         .build();
    
            // OkHttpClient client = new OkHttpClient.Builder()
    
            //         .certificatePinner(certificatePinner)
    
            //         .build();
    
            // Request request = new Request.Builder()
    
            //         .url("https://swapi.co/api/people/1")
    
            //         .build();
    
            // Response response = client.newCall(request).execute();
    
            return wrapJavaPerform(() => {
    
                const certificatePinner: CertificatePinner = Java.use("okhttp3.CertificatePinner");
    
    
    
                const CertificatePinnerCheck = certificatePinner.check.overload("java.lang.String", "java.util.List");
    
    
    
                // tslint:disable-next-line:only-arrow-functions
    
                CertificatePinnerCheck.implementation = function () {
    
                };
    
    
    
                return CertificatePinnerCheck;
    
            });
    
        };
    
    
    
    
    
        const okHttp3CertificatePinnerCheckOkHttp = (): any | undefined => {
    
            // -- Sample Java
    
            //
    
            // Example used to test this bypass.
    
            //
    
            // String hostname = "swapi.co";
    
            // CertificatePinner certificatePinner = new CertificatePinner.Builder()
    
            //         .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
    
            //         .build();
    
            // OkHttpClient client = new OkHttpClient.Builder()
    
            //         .certificatePinner(certificatePinner)
    
            //         .build();
    
            // Request request = new Request.Builder()
    
            //         .url("https://swapi.co/api/people/1")
    
            //         .build();
    
            // Response response = client.newCall(request).execute();
    
            return wrapJavaPerform(() => {
    
                const certificatePinner: CertificatePinner = Java.use("okhttp3.CertificatePinner");
    
    
    
                const CertificatePinnerCheckOkHttp = certificatePinner.check$okhttp.overload("java.lang.String", "u15");
    
    
    
                // tslint:disable-next-line:only-arrow-functions
    
                CertificatePinnerCheckOkHttp.implementation = function () {
    
                };
    
    
    
                return CertificatePinnerCheckOkHttp;
    
            });
    
        };
    
        const appceleratorTitaniumPinningTrustManager = (): any | undefined => {
    
            return wrapJavaPerform(() => {
    
                const pinningTrustManager: PinningTrustManager = Java.use("appcelerator.https.PinningTrustManager");
    
    
    
                const PinningTrustManagerCheckServerTrusted = pinningTrustManager.checkServerTrusted;
    
    
    
                // tslint:disable-next-line:only-arrow-functions
    
                PinningTrustManagerCheckServerTrusted.implementation = function () {
    
                };
    
    
    
                return PinningTrustManagerCheckServerTrusted;
    
            });
    
        };
    
    
    
        // Android 7+ TrustManagerImpl.verifyChain()
    
        // The work in the following NCC blog post was a great help for this hook!
    
        // hattip @AdriVillaB :)
    
        // https://www.nccgroup.trust/uk/about-us/newsroom-and-events/
    
        //  blogs/2017/november/bypassing-androids-network-security-configuration/
    
        //
    
        // More information: https://sensepost.com/blog/2018/tip-toeing-past-android-7s-network-security-configuration/
    
        const trustManagerImplVerifyChainCheck = (): any | undefined => {
    
            return wrapJavaPerform(() => {
    
                const trustManagerImpl: TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
    
    
    
                // https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/
    
                //  platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650
    
                const TrustManagerImplverifyChain = trustManagerImpl.verifyChain;
    
                // tslint:disable-next-line:only-arrow-functions
    
                TrustManagerImplverifyChain.implementation = function (untrustedChain: any, trustAnchorChain: any,
    
                    host: any, clientAuth: any, ocspData: any, tlsSctData: any) {
    
    
    
                    // Skip all the logic and just return the chain again :P
    
                    return untrustedChain;
    
                };
    
    
    
                return TrustManagerImplverifyChain;
    
            });
    
        };
    
    
    
    
    
        // Android 7+ TrustManagerImpl.checkTrustedRecursive()
    
        // The work in the following method is based on:
    
        // https://techblog.mediaservice.net/2018/11/universal-android-ssl-pinning-bypass-2/
    
        const trustManagerImplCheckTrustedRecursiveCheck = (): any | undefined => {
    
            return wrapJavaPerform(() => {
    
                const arrayList: ArrayList = Java.use("java.util.ArrayList");
    
                const trustManagerImpl: TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
    
    
    
                // https://android.googlesource.com/platform/external/conscrypt/+/1186465/src/
    
                //  platform/java/org/conscrypt/TrustManagerImpl.java#391
    
                const TrustManagerImplcheckTrustedRecursive = trustManagerImpl.checkTrustedRecursive;
    
                // tslint:disable-next-line:only-arrow-functions
    
                TrustManagerImplcheckTrustedRecursive.implementation = function (certs: any, host: any, clientAuth: any, untrustedChain: any,
    
                    trustAnchorChain: any, used: any) {
    
    
    
                    // Return an empty list
    
                    return arrayList.$new();
    
                };
    
    
    
                return TrustManagerImplcheckTrustedRecursive;
    
            });
    
        };
    
    
    
        const phoneGapSSLCertificateChecker = (): any | undefined => {
    
            return wrapJavaPerform(() => {
    
                const sslCertificateChecker: SSLCertificateChecker = Java.use("nl.xservices.plugins.SSLCertificateChecker");
    
    
    
                const SSLCertificateCheckerExecute = sslCertificateChecker.execute;
    
    
    
                SSLCertificateCheckerExecute.overload(
    
                    "java.lang.String", "org.json.JSONArray", "org.apache.cordova.CallbackContext").implementation =
    
                    // tslint:disable-next-line:only-arrow-functions
    
                    function (str: any, jsonArray: any, callBackContext: any) {
    
                        callBackContext.success("CONNECTION_SECURE");
    
                        return true;
    
                    };
    
            });
    
        };
    
    
    
        sslContextEmptyTrustManager();
    
        okHttp3CertificatePinnerCheck();
    
        okHttp3CertificatePinnerCheckOkHttp();
    
        appceleratorTitaniumPinningTrustManager();
    
        trustManagerImplVerifyChainCheck();
    
        trustManagerImplCheckTrustedRecursiveCheck();
    
        phoneGapSSLCertificateChecker();
    
    
    
    }
    
  • 相关阅读:
    二分和三分
    windows对拍及其应用
    RMQ与st表
    图论最短路
    图论最小生成树
    贪心问题
    [转载]图论500题
    第二次重建博客。。
    二分和三分题
    树状数组
  • 原文地址:https://www.cnblogs.com/Tu9oh0st/p/15749246.html
Copyright © 2020-2023  润新知