android逆向之 Frida

一.安装 python 并配置环境

1.python 下载地址 https://www.python.org,自行选择版本
2.编写工具 PyCharm 下载地址:Pycharm 下载

安装好之后配置环境变量,此步骤自行利用搜索引擎解决

二.安装 Frida 并配置环境

①命令行安装
frida下载地址:https://pypi.org/project/frida/#
frida-tools地址: https://pypi.org/project/frida-tools/#history(会自动根据 tools 的版本下载匹配的 frida

1.pip install frida
2.pip install frida-tools

==mac遇到安装验证问题:==
1.<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
解决:找到安装的 python3.7,运行里面的:Install Certificates.command文件
执行上述命令,如果还是失败,pip 更改为 pip3

②安装 frida-server
下载地址:frida-server 下载
找到对应的 frida-server-android 的对应文件,需要和本地 frida 版本匹配:frida --version 查看

1.解压对应文件,放到手机执行目录
adb push 文件路径 /data/local/tmp

2.给权限
chmod 777 ./文件名

3.端口转发
adb forward tcp:27042 tcp:27042(固定的端口号)

4.查看 frida运行是否成功
frida-ps -U 不报错就说明成功了

三.新建 frida-hook

注意:需要勾选Inherit global sit-packages和make available to all projects

四.常用的frida js API

1.Java.use(包名+类名) ,获取类对象
2.Java.cast(Class,类对象),类型转换
3.Java.array(‘Ljava.lang.String;Ljava.lang.Object;’,["",obj]),构建任意数组 类型eg:参数集合
4.overload ,重载方法
5.类对象.KaTeX parse error: Can't use function '\.' in math mode at position 15: 方法名,构造方法 6.类对象\̲.̲new,新建对象;
7.类对象.$dispose()销毁;
8.Java.choose(className, callbacks),查找指定类实例
9.ptr 重新赋值,多用于参数
10.replace 替换值,多用于返回值

五.常用的模板

㈠ .java层 hook

①普通方法、构造方法模板

jscode = """
	Java.perform(function () {
	    var utils = Java.use(包名+类名);
	    utils.方法名.implementation = function (参数1, 参数 2...) {
	        console.log("start  hook...");

	        //如果function 指定参数的情况下,使用下面方式打印参数
	        send(参数1)
	        send(参数2)

	        //如果function 中不指定参数的情况下,使用下面方式打印参数
			send(arguments[0]);
	        send(arguments[1]);

	        //更改参数
	        arguments[0] = 4000;
	        send(arguments[0]);
	        return this.方法名(arguments[0],arguments[1]);
	    }
	});
	"""

②构造方法

jscode = """
	Java.perform(function () {
	    var utils = Java.use(包名+类名);
	    utils.$init.implementation = function (a,b) {
	        console.log("Hook Start...");
			send(arguments[0]);
	        send(arguments[1]);

	        //更改构造参数值
	        arguments[0] = 4000;
	        send(arguments[0]);
	        return this.$init(arguments[0],arguments[1]);
	    }
	});
	"""

③重载方法

	jscode = """
	Java.perform(function () {
	    var utils = Java.use(包名+类型);
	    utils.方法名.overload("参数类型").implementation = function (a) {
	        console.log("Hook Start...");
			send(arguments[0]);
	        arguments[0] = 4000;
	        send(arguments[0]);
	        return this.方法名(arguments[0]);
	    }
	});
	"""
	
	注意,除了基本类型,其他类型的参数均需要包名+类型eg:java.lang.String

④参数对象

jscode = """
	Java.perform(function(){
	    var utils = Java.use(引用包名+类名);
	    var obj=Java.use(实例化对象包名+类名);
	    
	    utils.方法名.overload(参数对象包名+类名).implementation = function(a)
	    {
	        console.log("Hook Start...");
	        send(a.getInfo());
	        //新建参数对象 通过$new
	        var newObj = obj.$new(参数 1, 参数 2);
	        //打印属性
	        send(newObj.方法名);
	        this.方法名(newObj);
	    }
	});
	"""

⑤修改属性

jscode = """
	Java.perform(function () {
	    var utils = Java.use(引用的包名+类名);
	    var money = Java.use(修改对象的包名+类型);
	    var clazz = Java.use('java.lang.Class')
	    utils.方法名.overload().implementation = function (a) {
	            //新建参数对象 通过$new
	 	        var newObj = obj.$new(参数 1, 参数 2);
	 	        //获取字段
	 	        var filed = Java.cast(newObj.getClass(),clazz).getDeclaredField(字段名);
	 	        //设置字段可用
	 	        filed.setAccessible(true);
	 	        
	 	        //修改字段
			    filed.setInt(newObj,100)
			    send(newObj.getInt())
	    }
	});
	"""

⑥内部类 $符号后面跟的就是内部类名字,可用 smali 代码查看

jscode = """
	Java.perform(function () {
	    var utils = Java.use('包名+类名$内部类名字');
	    utils.方法名.overload().implementation = function (a) {
	            //新建参数对象 通过$new
	 	        var newObj = obj.$new(参数 1, 参数 2);
	 	        //获取字段  Java.cast,类转换class 转换成实际的对象类
	 	        var filed = Java.cast(newObj.getClass(),clazz).getDeclaredField(字段名字);
	 	        //设置字段可用
	 	        filed.setAccessible(true);

	 	        //修改
			    filed.setInt(newObj,100)
			    send(newObj.getInt())
	    }
	});
	"""

⑦hook 所有重载方法

jscode = """
	Java.perform(function () {
	    //获取类
	    var messageDigest = Java.use('java.security.MessageDigest');
	    for(var i = 0;i<messageDigest.digest.overloads.length;i++){
	       messageDigest.digest.overloads[i].implementation = function (a) {
	                		//重载方法
	                        //打印参数
	                        for(var p = 0;p<arguments.length;p++){
	                            send(arguments[p]);
	                        }
	                        //调用方法
	                        return this[methodName].apply(this,arguments);
	        }
	    }
	});
	"""

⑧hook 所有方法

	jscode = """
	Java.perform(function () {
	    //获取类
	    var messageDigest = Java.use('java.security.MessageDigest');
	    //获取类中所有方法
	    var methods = messageDigest.class.getDeclaredMethods();
	    for(var i = 0;i<methods.length;i++){
	              //方法名
	              var methodName = methods[i].getName();
	              send(methodName)//重载方法
	              for(var m = 0;m<messageDigest[methodName].overloads.length;m++){
	                    messageDigest[methodName].overloads[m].implementation = function(){
	                        //重载方法
	                        //打印参数
	                        for(var p = 0;p<arguments.length;p++){
	                            send(arguments[p]);
	                        }
	                        //调用方法
	                        return this[methodName].apply(this,arguments);
	                    }
	              }
	    }
	});
	"""

㈡ .so 层 hook

①hook所有导出函数

	jsCode = """
	    var exports = Module.enumerateExportsSync("so库的名字.so");
	    for(var i=0;i<exports.length;i++){
	        send(exports[i])
	        send("name = "+exports[i].name + ",address = " + exports[i].address)
	    }
	"""

②hook 指定导出函数

jsCode = """
	    //内存中函数地址 = so模块起始地址(基址) + 偏移(分析so text后的地址)
	    //获取导出函数地址
	    var nativePointer = Module.findExportByName("so库的名字.so","方法名");
	    //打印地址
	    send(nativePointer);

	    Interceptor.attach(nativePointer,{
	        onEnter:function(args){
	            send(args[0].toInt32());
	            send(args[1].toInt32());
	            send(args[2].toInt32());
	            send(args[3].toInt32());
	            send(args[4].toInt32());
	        },
	        onLeave:function(retVal){
	            send(retVal.toInt32());
	        }

	    });

	"""

③遍历查找指定 so 库的基址

jsCode = """
	    //setImmediate超时异步任务
	    setImmediate(function(){
	        send("遍历开始->>")
	        Process.enumerateModules({
	            onMatch:function(exp){
	                if(exp.name == "so库的名字.so"){
	                    send("找到对应 so 库了");
	                    send("name = " + exp.name + ",base = " + exp.base +",size = "+exp.size+",path = "+exp.path);
	                    send(exp);
	                    return "stop"
	                }
	            },
	            onComplete:function(){
	                send("遍历完成了");
	            }
	        })
	    })

	"""

④基址,方法内存地址查找so 偏移地址

jsCode = """
	    //获取基址
	    var basePointer = Module.findBaseAddress("so库的名字.so");
	    send(basePointer);
	    
	    //获取方法在内存中的地址 = so基址 + 偏移
	    var voidPointer = Module.findExportByName("so库的名字.so","方法名");
	    send(voidPointer);
	    
	    //偏移 arm 指令直接使用偏移;thumb 指令需要偏移+1(offset+1)
	    var offset = voidPointer - basePointer;
	    send(offset);
	    
	    setImmediate(function(){
	        //附加
	        Interceptor.attach(basePointer.add(offset),{
	            onEnter: function(args) {
	                send(args[0].toInt32());
	                send(args[1].toInt32());
	                send(args[2].toInt32());
	                send(args[3].toInt32());
	                send(args[4].toInt32());
	            },
	            onLeave: function(retVal){
	                send("计算结果:" + retVal.toInt32());
	            }
	        });
	    });
    
	"""

五.注入启动 app

hook比较靠前的方法:frida -U -f 程序包名 -l 路径下的/xhs.js --no-pause
运行时hook方法:frida -U -l 路径下的/xhs.js 程序包名
结束:contro+c

分析jni 调用日志:jnitrace-l l库名.so 包名,对应链接https://www.cnpython.com/pypi/jnitrace
hookJava调用 native:https://github.com/zhkl0228/unidbg

更多 Frida API,点击Frida API

六.配合抓包工具 charles

1.charles安装ssl
打开 charles->help>SSL Proxying -> install charles root certificate ->install charles root certificate on a mobile device or rote browser -> 找到电脑里面的证书,选择信任

2.手机安装证书
浏览器访问chls.pro/ssl -> 下载证书 -> 进入手机设置->安全 ->从 SD 卡安装 ->选择刚才下载的文件安装即可

一.执行 frida-server 手机就重启
联发科 CPU 64高版本 frida-server 都会有问题,12.5.0 以下都可以
遇到问题可以参照下面的地址寻求解决方案frida问题集锦

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值