zoukankan      html  css  js  c++  java
  • 针对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();
    
    
    
    }
    
  • 相关阅读:
    linux常用命令
    mysql 开发基础系列20 事务控制和锁定语句(上)
    sql server 性能调优之 资源等待 CXPACKET
    mysql 开发基础系列19 触发器
    mysql 开发基础系列18 存储过程和函数(下)
    mysql 开发基础系列17 存储过程和函数(上)
    sql server 性能调优之 资源等待PAGEIOLATCH
    mysql 开发基础系列16 视图
    mysql 开发基础系列15 索引的设计和使用
    sql server 性能调优之 当前用户请求分析 (1)
  • 原文地址:https://www.cnblogs.com/Tu9oh0st/p/15749246.html
Copyright © 2011-2022 走看看