学习资料:《安卓Frida逆向与抓包实战》陈佳林/著
文章目录
- 基础环境
- 第三章 Frida逆向入门之Java层hook
- 3.1 frida基础
- 3.1.3frida基础知识
- frida存在两种操作模式
- frida操作App的方式有两种
- 3.1.4Frida IDE配置
- 3.2 frida脚本入门
- 3.2.1 frida脚本的概念
- 3.2.2 Java层hook基础
- 1.hook初探
- 2.重载函数的hook
- 3.2.3 Java层主动调用
- 1. 主动调用例1
- 2. 主动调用例2
- 第四章 Objection快速入门
- 4.2 Objection的安装与使用
- 4.2.1 objection安装
- 4.2.2 objection使用
- 1. 注入进程进入REPL
- 2. 常用命令:
基础环境
- 安装Android Studio并配置好adb
- 一台root的真机或者安卓虚拟机
第三章 Frida逆向入门之Java层hook
3.1 frida基础
3.1.3frida基础知识
frida存在两种操作模式
- CLI(命令行模式)
通过命令行将js代码注入进程 - RPC模式
通过python将js代码注入进进程,本质上还是使用js代码进行hook
frida操作App的方式有两种
- spwan模式
将启动app的权利交由frida控制,即使目标App已启动,也会被
frida重新启动.通过-f参数可以指定以spwan模式操作app - attach模式
app已经启动,frida通过ptrace注入程序从而执行hook.frida默认是attach模式操作app
3.1.4Frida IDE配置
- 安装node和npm环境
- git下载frida-agent-example仓库
git clone https://github.com/oleavr/frida-agent-example.git 下载frida-agent-example仓库
cd frida-agent-example/
npm install
使用vscode打开,在frida-agent-example文件夹内创建文件夹即可写脚本
3.2 frida脚本入门
3.2.1 frida脚本的概念
示例:
setTimeout(function(){
Java.perform(function(){
console.log("hello,world")
})
})
代码分析:
1. 调用setTimeout方法将匿名函数注册到js运行库中
2. 在函数中调用Java.perform方法,将匿名函数注册到App的java运行库中,并执行函数
在手机上运行frida-server之后,可以使用frid-ps -U命令查看运行的进程
然后可以使用frida -U -l test0.js android.process.media将脚本注入进程,可以发现注入后打印了helloworld
在上面的参数中,-U指定usb设备,-l指定注入脚本所在路径,最后的androdi.process.media则是设备上正在运行的进程名
3.2.2 Java层hook基础
1.hook初探
android studio项目代码
package com.example.hooktest1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int x=0,y=0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
fun(x,y);
x++;y++;
}
}
void fun(int x,int y)
{
Log.d("sum=",String.valueOf(x+y));
}
}
运行结果: android studio控制台会持续输出x+y,并且x和y逐次自增
hook脚本代码:
function main(){
console.log("Script loaded success")
//Java.perform是frida的api函数,可以将其中的脚本注入到java运行库,参数是一个匿名函数
//函数的主体内容是监控和修改java函数逻辑的主题内容,任何针对java层的操作都必须在这个api函数中
Java.perform(function(){
console.log("Inside java perform!")
//java.use获取hook函数所在类的类名
var Mainactivity=Java.use('com.example.hooktest1.MainActivity')
console.log("Java.use.success!")
//通过.连接函数名,比如这里的函数名是fun,表示想hookfun函数
//implementation表示实现该函数,也就是hook掉,这里可以写自己的函数
Mainactivity.fun.implementation=function(x,y)
{
console.log("x=",x,"y=",y,"x+y=",x+y) //打印参数,这里应该是函数原本没有被修改时的参数,即函数正常执行时的参数情况
var retvalue=this.fun(666,66) //再次调用原函数并且传递原本的参数fun,即重新执行原函数,在这里就可以修改参数
return retvalue //返回函数返回值,返回值最好不要修改类型,否则可能出错
}
})
}
//参数是要被执行的函数,例如传入main,表示frida注入app后立刻执行main
//setTimeout可以指定frida注入app多久之后执行函数,用于延时注入
setImmediate(main)
将脚本注入进程frida -U -l test0.js com.example.hooktest1
可以看到注入之后命令台会输出js脚本函数的内容
再看看android studio命令台,可以发现控制台一直在输出sum=: 732
说明脚本中调用this.fun(666,66)成功
2.重载函数的hook
略微修改代码:
package com.example.hooktest1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private String total="hello";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int x=0,y=0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
fun(x,y);
x++;y++;
Log.d("hooktest:",fun("HelloWorld!"));
}
}
void fun(int x,int y)
{
Log.d("sum=",String.valueOf(x+y));
}
String fun(String s){
return s.toLowerCase();
}
程序运行结果:
hook脚本:
//定位重载函数,使用overload即可,overload内指定重载函数参数类型,如果函数有返回值要注意返回
function main(){
console.log("Loaded sucess!")
Java.perform(function(){
console.log("Inside java perform")
var activity=Java.use("com.example.hooktest1.MainActivity")
console.log("定位activity成功")
activity.fun.overload('java.lang.String').implementation=function(x){
console.log("hook fun string=",x)
return x
}
activity.fun.overload('int','int').implementation=function(x,y){
console.log("x=",x,"y=",y)
var ret= this.fun(66,55)
return ret;
}
})
}
setImmediate(main)
hook结果
控制台:
3.2.3 Java层主动调用
上述实现的是被动调用:即随着app正常逻辑执行函数
主动调用则是可以直接调用关键函数,不需要app去执行该函数
这里又分两种情况:
- 类函数(静态方法) 直接使用Java.use找到函数所在类即可
- 实例方法(动态方法) 使用Java.choose这个api函数在java堆中寻找指定类实例
1. 主动调用例1
程序代码:
添加了secret和staticSecret两个方法
package com.example.hooktest1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int x=0,y=0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
fun(x,y);
x++;y++;
Log.d("hooktest:",fun("HelloWorld!"));
}
}
void fun(int x,int y)
{
Log.d("sum=",String.valueOf(x+y));
}
String fun(String s){
return s.toLowerCase();
}
void secret(){
Log.d("this is secret","find secret func!");
}
static void staticSecret(){
Log.d("this is staticSecret","Find static!");
}
}
程序执行结果:
hook脚本:
function main(){
Java.perform(function(){
console.log("Inside java perform")
var MainActivity=Java.use("com.example.hooktest1.MainActivity")
MainActivity.staticSecret()
//动态函数主动调用
//java.choose先从内存中寻找类的实例对象,然后再调用实例对象的函数
Java.choose('com.example.hooktest1.MainActivity',{
onMatch: function(instance){
console.log("instance found",instance)
instance.secret()
},
onComplete: function(){
console.log('search Complete')
}
})
})
}
setImmediate(main)
hook结果
可以看到成功打印了实例对象所在类及其地址
控制台可以看见成功调用了secret和staticSecret这两个函数
注意:调用这两个函数输出的结果是在android studio控制台,不在frida控制台,frida控制台输出的是js脚本中的内容.
2. 主动调用例2
略微修改上述程序代码,增加了两个私有变量,尝试hook变量值
package com.example.hooktest1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private String total="hello";
private int count=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int x=0,y=0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
fun(x,y);
x++;y++;
Log.d("hooktest:",fun("HelloWorld!"));
}
}
void fun(int x,int y)
{
Log.d("sum=",String.valueOf(x+y));
}
String fun(String s){
return s.toLowerCase();
}
void secret(){
total+="call"+count;
count++;
Log.d("this is secret","find secret func!");
}
static void staticSecret(){
Log.d("this is staticSecret","Find static!");
}
}
程序运行结果:
hook脚本:
//调用secret函数,需要控制台手动进行,
function callSecretFunc(){
Java.perform(function(){
Java.choose('com.example.hooktest1.MainActivity',{
onMatch:function(instance){
instance.secret()
},
onComplete:function(){
}
})
})
}
//获取total的值
function getTotalValue(){
Java.perform(function(){
Java.choose('com.example.hooktest1.MainActivity',{
onMatch:function(instance){
console.log("find instance=",instance)
console.log("totalAddr=",instance.total)//获取类变量的值要使用.value,total本身是一个引用类型
console.log("total=",instance.total.value)
},
onComplete:function(){
console.log("search end")
}
})
})
}
setImmediate(getTotalValue)
hook结果
可以看到程序先调用了getTotalValue函数,输出了total的值
然后我们在frida控制台调用callSecretFunc()脚本函数来调用secret函数修改total的值
再次调用getTotalValue输出total的值发现成功修改
总结:
- 获取类中的实例变量要用.value才可以得到他的值,如果直接打印得不到值(可以得到变量有关信息,因为total本身是一个引用类型)
- frida控制台中我们可以反复调用脚本的函数来达到反复调用程序函数的目的
第四章 Objection快速入门
4.2 Objection的安装与使用
4.2.1 objection安装
pip install -U objection
注意:由于frida更新较快,需要保证objection版本的发布时间在frida之后.最新的objection版本为1.11.0,对应的frida版本最大为14.2.14,frida-tools为9.2.2
4.2.2 objection使用
1. 注入进程进入REPL
objection默认通过USB连接设备,不需要像frida使用-U参数指定usb
以’设置’应用演示注入进程命令:objection -g com.android.settings explore
成功注入会显示如下信息:
这样就成功进入了objection的REPL界面,可以输入exit退出
2. 常用命令:
空格键可以提示输入命令
-
help 如果不知道当前命令是什么效果,可以在命令前跟help,会输出命令的解释信息
-
jobs命令
jobs list 显示当前作业
jobs kill 删除作业
-
frida命令
查看frida信息
-
内存漫游相关命令
(1)android hooking list classes
打印内存中的所有类
(2)android hooking search classes key
搜索包含关键词key的所有类
(3)android hooking search methods key
搜索包含关键词key的所有方法
(4)android hooking list class_methods classname
查看名为classname的类的所有方法
(5)android hooking list activities(services receivers providers)
打印四大组件,列出进程所有的activity活动(service receiver provider)
-
hook相关命令
android hooking watch class_method methodName
对指定方法进行hook
持续更新中…