下载&运行(frida-server)
https://github.com/frida/frida/releases?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTgxOTM3OTksImZpbGVHVUlEIjoiNXJrOWRZUEdERHV6V2VxeCIsImlhdCI6MTY1ODE5MzQ5OSwidXNlcklkIjo3NDA3NDU0MH0.k5Mx_5jL58VPGG5gk1rN0R3mIJpdp1_JHDiNqH9fxFo
模拟器下载
在本地解压后 使用adb上传到模拟器
比如本地文件在 D: 目录下 我想传输到模拟器/data/snow目录下输入:
adb push D:frida /data/snow #将frida文件复制到/data/snow目录下
adb shell
cd /data/Snow
chmod 777 frida-server
./frida-server
把服务运行起来
测试是否成功运行:
在本地重新开一个终端
frida-ps -U
看是否有回显
出现正在运行的进程就算成功
本地下载(frida)
pip install firda
pip install frida-tools
题目
分析
拖到jadx-gui中分析
如果正常逆向的话是要去分析a方法的加密流程去逆向
但hook的方便性就体现出来了 我们可以调用a方法并把加密结果打印出来
这样就可以省去逆向的流程了
脚本
下载frida的语法 这样在写代码时可以智能识别代码(自动补全)
npm i @types/frida-gum
在本地写好脚本
js脚本:
functionhook_java() {
Java.perform(function () { //注入到java运行库 function为匿名函数
varLoginActivity = Java.use("com.example.androiddemo.Activity.LoginActivity"); //想要hook的函数所在类的类名
console.log(LoginActivity); //console.log控制台打印
LoginActivity.a.overload('java.lang.String', 'java.lang.String').implementation = function (str, str2) { //overload为函数重载
var result = this.a(str, str2); //调用原来的函数
console.log("LoginActivity.a:", str, str2, result);
return result;
};
console.log("hook_java");
});
}
functionmain(){
hook_java();
}
setImmediate(main);
语法
脚本中使用function关键字定义了一个main()函数,用于存放Hook脚
本,然后调用Frida的API函数Java.perform()将脚本中的内容注入到Java
运行库,这个API的参数是一个匿名函数,函数内容是监控和修改Java函
数逻辑的主体内容。注意,这里的Java.perform()函数非常重要,任何对
App中Java层的操作都必须包裹在这个函数中,否则Frida运行起来后就会
报错
在Java.perform()函数包裹的匿名函数中,首先调用了Frida的API函
数Java.use(),这个函数的参数是Hook的函数所在类的类名,参数的类型
是一个字符串类型,比如Hook的fun()函数所在类的全名为
com.roysue.demo02.MainActivity,那么传递给这个函数的参数就
是"com.roysue.demo02.MainActivity"。这个函数的返回值动态地为相应
Java类获取一个JavaScript
Wrapper,可以通俗地理解为一个JavaScript
对象。
在获取到对应的JavaScript对象后,通过“.”符号连接fun这个对应的
函数名,然后加上implementation关键词表示实现MainActivity对象的
fun()函数,最后通过“=”这个符号连接一个匿名函数,参数内容和原Java
的内容一致。不同的是,JavaScript是一个弱类型的语言,不需要指明参
数类型。此时一个针对MainActivity类的fun()函数的Hook框架就完成
了。
这个匿名函数的内容取决于逆向开发和分析人员想修改这个被Hook
的函数的哪些运行逻辑。比如调用console.log()函数把参数内容打印出
来,通过this.fun()函数再次调用原函数,并把原本的参数传递给这个
fun()函数。简而言之,就是重新执行原函数的内容,最后将这个函数的
返回值直接通过return指令返回。
在Hook一个函数时,还有一个地方需要注意,那就是最好不要修改
被Hook的函数的返回值类型,否则可能会引起程序崩溃等问题,比如直
接通过调用原函数将原函数的返回值返回。
hook
在当前脚本目录下运行cmd
运行hook.js脚本
frida -U AndroidDemo -l hook.js
AndroidDemo为题目的进程名 hook.js 为我们写的hook脚本
进程名用 frida-ps -U 查看
连接成功之后直接在程序中点击注册即可看到回显
输入之后直接再次点击注册就可以了
这样我们就进入了下一个关卡
python脚本
import frida, sys
defon_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
var LoginActivity = Java.use("com.example.androiddemo.Activity.LoginActivity");
console.log(LoginActivity);
LoginActivity.a.overload('java.lang.String', 'java.lang.String').implementation = function (str, str2) {
var result = this.a(str, str2); //调用原来的函数
console.log("LoginActivity.a:", str, str2, result);
return result;
};
console.log("hook_java");
});
"""
process = frida.get_usb_device().attach('AndroidDemo')
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()
修改函数返回值
这样我们直接将a方法的返回值改为后面的数值就行了
functionhook_java() {
Java.perform(function () {
varLoginActivity = Java.use("com.example.androiddemo.Activity.LoginActivity");
console.log(LoginActivity);
LoginActivity.a.overload('java.lang.String', 'java.lang.String').implementation = function (str, str2) {
var result = this.a(str, str2); //调用原来的函数
console.log("LoginActivity.a:", str, str2, result);
return result;
};
varFridaActivity1 = Java.use("com.example.androiddemo.Activity.FridaActivity1");
console.log(FridaActivity1);
//hook函数,没有调用原来的函数,直接返回值
FridaActivity1.a.implementation = function (barr) {
console.log("FridaActivity1.a");
return"R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=";
};
console.log("hook_java");
});
}
functionmain() {
hook_java();
}
setImmediate(main);
函数重载
注册关卡用到了函数重载这里再详细看下
例子:
这里fun函数进行了重载 如果我们直接输入下面的脚本去运行 会提示错误
因为没有去指定我们想要的函数类型
functionmain(){
console.log("Script loaded successfully ")
Java.perform(function(){
console.log("Inside java perform function")
varMainAcitivity = Java.use('com.roysue.demo02.MainActivity')
console.log("Java.Use.Successfully!") //定位类成功!
//Hook重装函数
MainAcitivity.fun.implementation = function(x,y){
console.log("x => ",x,", y => ",y)
var ret_value = this.fun(2, 5);
return ret_value
}
})
}
setImmediate(main)
Frida运行后提示“Error: fun(): has more than one
overload”,这是函数的重载导致Frida不知道具体应该Hook哪个函数而出
现的问题。其实Frida已经提供了解决方案(use
.overload(<signature>)),就是指定函数签名,将报错中
的.overload('java.lang.String')或者.overload('int', 'int')添加到要Hook的函
数名后、关键词implementation之前。当然,相应的参数和具体函数逻辑
也得修改,这里仅演示两个int作为参数类型的函数的Hook,String类型
也可以先不填写overload先执行脚本 最后frida会有回显提示你该怎样去填写
正确脚本:
functionmain(){
console.log("Script loaded successfully ")
Java.perform(function(){
console.log("Inside java perform function")
varMainAcitivity = Java.use('com.roysue.demo02.MainActivity')
console.log("Java.Use.Successfully!") //定位类成功!
// hook 重载函数
MainAcitivity.fun.overload('int', 'int').implementation = function(x,y){
console.log("orignal args: x => ",x,", y => ",y)
var ret_value = this.fun(2, 5);
return ret_value
}
})
}
setImmediate(main)
主动调用
在Java中,类中的函数可分为两种:类函数和实例方法。通俗地讲,
就是静态的方法和动态的方法。类函数使用关键字static修饰,和对应类
是绑定的,如果类函数还被public关键词修饰着,在外部就可以直接通过
类去调用;实例方法则没有关键字static修饰,在外部只能通过创建对应
类的实例再通过这个实例去调用。在Frida中主动调用的类型会根据方法
类型区分开。如果是类函数的主动调用,直接使用Java.use()函数找到类
进行调用即可;如果是实例方法的主动调用,则需要在找到对应的实例后
对方法进行调用。
这里用到了Frida中非常重要的一个API函数
Java.choose(),这个函数可以在Java的堆中寻找指定类的实例。
例子:
在这里添加了两个secret()函数:一个是没有static修饰的secret实例方法,一个是有static关键字修饰的staticSecret类方法。
所以对应两种写法 需要进行区分
functionmain(){
console.log("Script loaded successfully ")
Java.perform(function(){
console.log("Inside java perform function")
varMainAcitivity = Java.use('com.roysue.demo02.MainActivity')
// 静态函数主动调用
MainAcitivity.staticSecret();
// Error: secret: cannot call instance method without an instance
MainAcitivity.secret();
// 动态函数主动调用
Java.choose('com.roysue.demo02.MainActivity',{
onMatch: function(instance){
console.log('instance found',instance)
instance.secret()
},
onComplete: function(){
console.log('search Complete')
}
})
})
}
setImmediate(main)
获取实例的值
脚本:
functiongetTotalValue(){
Java.perform(function(){
// console.log("Inside java perform function")
varMainAcitivity = Java.use('com.roysue.demo02.MainActivity')
// 动态函数主动调用
Java.choose('com.roysue.demo02.MainActivity',{
onMatch: function(instance){
// console.log('instance found',instance)
// instance.secret()
console.log('total value = ',instance.total.value)
// console.log('secret func exec success')
},
onComplete: function(){
console.log('search Complete')
}
})
})
}
setImmediate(getTotalValue)
长
按
关
注
网络安全社团公众号
微信号 : qlnu_ctf
新浪微博:齐鲁师范学院网络安全社团
原文始发于微信公众号(齐鲁师院网络安全社团):Frida学习笔记
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论