一.安装 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问题集锦