【Android安全】Frida 复制任意类型对象数组 | 处理对象数组

Frida 复制任意类型对象数组

需求是,第一次hook到一个方法的参数,类型是任意类型的数组([Ljava.security.cert.X509Certificate;);第二次hook到一个方法,要将之前hook到的数组赋给其参数。

复制参数可以使用Java.retain(obj),但由于Frida不认为对象数组是java.lang.Object对象(参考https://bbs.pediy.com/thread-261052-1.htm),所以对整个[Ljava.security.cert.X509Certificate;调用Java.retain是不行的,报错如下:

{‘type’: ‘error’, ‘description’: ‘TypeError: not a function’, ‘stack’: ‘TypeError: not a function\n at retain (frida/node_modules/frida-java-bridge/lib/class-factory.js:123)\n at retain (frida/node_modules/frida-java-bridge/index.js:267)\n at (/script1.js:79)\n at apply (native)\n at ne (frida/node_modules/frida-java-bridge/lib/class-factory.js:620)\n at (frida/node_modules/frida-java-bridge/lib/class-factory.js:598)’, ‘fileName’: ‘frida/node_modules/frida-java-bridge/lib/class-factory.js’, ‘lineNumber’: 123, ‘columnNumber’: 1}

取而代之,可以对数组的每个元素进行Java.retain,再将它们组装回数组。

代码如下:

jscode = """
setImmediate(function() {
Java.perform(function () {

    // 主动加载apk
    var DEXCL = null
    var DEXFactory = null
    function loadAPK(path){
        var ActivityThread = Java.use("android.app.ActivityThread");
        var app = ActivityThread.currentApplication();
        Java.classFactory.cacheDir = "/data/data/" + app.getPackageName() + "/cache";
        Java.classFactory.codeCacheDir = "/data/data/" + app.getPackageName() + "/code_cache";
        var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
        DEXCL = DexClassLoader.$new(path, Java.classFactory.codeCacheDir, null, DexClassLoader.getSystemClassLoader());
        DEXFactory = Java.ClassFactory.get(DEXCL);
        DEXFactory.cacheDir = "/data/data/" + app.getPackageName() + "/cache";
        DEXFactory.codeCacheDir = "/data/data/" + app.getPackageName() + "/code_cache";
    }

    loadAPK('/data/user_de/0/com.google.android.gms/app_chimera/m/00000009/CronetDynamite.apk');


    Java.enumerateClassLoaders({
        onMatch: function (loader) {
            try {
                // cronet apk contains class: org.chromium.net.AndroidNetworkLibrary
                if (loader.findClass("org.chromium.net.AndroidNetworkLibrary")) {
                    // should be: /data/user_de/0/com.google.android.gms/app_chimera/m/00000009/CronetDynamite.apk
                    if (loader.toString().indexOf("CronetDynamite") != -1) {
                        Java.classFactory.loader = loader;
                        send("loader: " + loader);
                        // console.log(loader);
                    }
                    
                }
            } catch (error) {

            }
        }, onComplete: function () {
        }
    });

    Java.deoptimizeEverything();
    
    // android.net.http.X509TrustManagerExtensions.checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, java.lang.String) : java.util.List

	var class_name = 'android.net.http.X509TrustManagerExtensions';
	// var DymClass = Java.use(class_name);
    var DymClass = DEXFactory.use(class_name);

    var time = 0;

    var ArrayX509Certificate = Java.array("Ljava.security.cert.X509Certificate;",[]);
    var tempArg0;
    var tempArg1;
    var tempArg2;

	DymClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String').implementation = function (arg1,arg2,arg3)
	{
        var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
        if(bt.indexOf("dh.a") != -1 && arg3.indexOf("www.googleapis.com") != -1) {
        // if (1) {
            send('hooked');
            console.log("Backtrace:" + bt);

            send('orin arg1 : ' + arg1);
            send('orin arg2 : ' + arg2);
            send('orin arg3 : ' + arg3);
            
            // 1st time
            if(time==0){
                send('1st time ');
                time = time + 1;

                var retval = this.checkServerTrusted(arg1,arg2,arg3);

                // store arg1
                send('orin arg1[0] : ' + arg1[0]);
                send('orin arg1[1] : ' + arg1[1]);
                send('orin arg1[2] : ' + arg1[2]);
                
                tempArg0 = Java.retain(arg1[0]);
                tempArg1 = Java.retain(arg1[1]);
                tempArg2 = Java.retain(arg1[2]);

                return retval;
            }

            // 2nd time
            if (time==1) {
                send('2nd time ');
                ArrayX509Certificate = [tempArg0,tempArg1,tempArg2];
                send('new arg1 : ' + ArrayX509Certificate);
                return this.checkServerTrusted(ArrayX509Certificate,arg2,arg3);
            }
        }
        else{
            return this.checkServerTrusted(arg1,arg2,arg3);
        }
		
        
	}

});

});

 function byteToString(arr) {
	if(typeof arr === 'string') {
		return arr;
	}
	var str = '',
		_arr = arr;
	for(var i = 0; i < _arr.length; i++) {
		var one = _arr[i].toString(2),
			v = one.match(/^1+?(?=0)/);
		if(v && one.length == 8) {
			var bytesLength = v[0].length;
			var store = _arr[i].toString(2).slice(7 - bytesLength);
			for(var st = 1; st < bytesLength; st++) {
				store += _arr[st + i].toString(2).slice(2);
			}
			str += String.fromCharCode(parseInt(store, 2));
			i += bytesLength - 1;
		} else {
			str += String.fromCharCode(_arr[i]);
		}
	}
	return str;
}

function utf8ByteToUnicodeStr(utf8Bytes){
    var unicodeStr ="";
    for (var pos = 0; pos < utf8Bytes.length;){
        var flag= utf8Bytes[pos];
        var unicode = 0 ;
        if ((flag >>>7) === 0 ) {
            unicodeStr+= String.fromCharCode(utf8Bytes[pos]);
            pos += 1;

        } else if ((flag &0xFC) === 0xFC ){
            unicode = (utf8Bytes[pos] & 0x3) << 30;
            unicode |= (utf8Bytes[pos+1] & 0x3F) << 24;
            unicode |= (utf8Bytes[pos+2] & 0x3F) << 18;
            unicode |= (utf8Bytes[pos+3] & 0x3F) << 12;
            unicode |= (utf8Bytes[pos+4] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+5] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 6;

        }else if ((flag &0xF8) === 0xF8 ){
            unicode = (utf8Bytes[pos] & 0x7) << 24;
            unicode |= (utf8Bytes[pos+1] & 0x3F) << 18;
            unicode |= (utf8Bytes[pos+2] & 0x3F) << 12;
            unicode |= (utf8Bytes[pos+3] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+4] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 5;

        } else if ((flag &0xF0) === 0xF0 ){
            unicode = (utf8Bytes[pos] & 0xF) << 18;
            unicode |= (utf8Bytes[pos+1] & 0x3F) << 12;
            unicode |= (utf8Bytes[pos+2] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+3] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 4;

        } else if ((flag &0xE0) === 0xE0 ){
            unicode = (utf8Bytes[pos] & 0x1F) << 12;;
            unicode |= (utf8Bytes[pos+1] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+2] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 3;

        } else if ((flag &0xC0) === 0xC0 ){ //110
            unicode = (utf8Bytes[pos] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+1] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 2;

        } else{
            unicodeStr+= String.fromCharCode(utf8Bytes[pos]);
            pos += 1;
        }
    }
    return unicodeStr;
}

"""
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值