Learning materials: "Android Frida Reverse and Packet Capture Actual Combat" Chen Jialin / Author
Article directory
basic environment
- Install Android Studio and configure adb
- A rooted real machine or an Android virtual machine
Chapter 3 Frida reverse entry Java layer hook
3.1 Frida basics
3.1.3 Basic knowledge of Frida
Frida has two modes of operation
- CLI (command line mode)
injects js code into the process through the command line - RPC mode
injects js code into the process through python, essentially using js code for hook
There are two ways for frida to operate App
- In spwan mode
, the right to start the app is handed over to frida. Even if the target app is started, it will be
restarted by frida. The -f parameter can be specified to operate the app in spwan mode - The attach mode
app has been started, and frida injects the program through ptrace to execute the hook. Frida defaults to the attach mode operation app
3.1.4Frida IDE configuration
- Install node and npm environment
- git download frida-agent-example repository
git clone https://github.com/oleavr/frida-agent-example.git 下载frida-agent-example仓库
cd frida-agent-example/
npm install
Use vscode to open, create a folder in the frida-agent-example folder to write scripts
3.2 Getting started with frida script
3.2.1 The concept of frida script
Example:
setTimeout(function(){
Java.perform(function(){
console.log("hello,world")
})
})
代码分析:
1. 调用setTimeout方法将匿名函数注册到js运行库中
2. 在函数中调用Java.perform方法,将匿名函数注册到App的java运行库中,并执行函数
After running frida-server on the mobile phone, you can use the frid-ps -U command to view the running process
and then use frida -U -l test0.js android.process.media to inject the script into the process, and you can find that helloworld is printed after injection
In the above parameters, -U specifies the usb device, -l specifies the path where the injection script is located, and the last androdi.process.media is the name of the process running on the device
3.2.2 Java layer hook basics
1. A preliminary study on hooks
android studio project code
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));
}
}
Running results: the android studio console will continue to output x+y, and x and y will increase gradually
hook script code:
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)
Inject the script into the process frida -U -l test0.js com.example.hooktest1
You can see that after the injection, the command console will output the content of the js script function
Look at the android studio command console, you can find that the console has been outputting sum=: 732
Indicates that calling this.fun(666,66) in the script is successful
2. The hook of the overloaded function
Slightly modified code:
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();
}
Program running result:
hook script:
//定位重载函数,使用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 result
Console:
3.2.3 Java layer active call
The above implementation is passive call: that is, the function is executed with the normal logic of the app.
Active call can directly call the key function without the need for the app to execute the function.
Here are two situations:
- Class function (static method) Just use Java.use to find the class where the function is located
- Instance method (dynamic method) Use the Java.choose api function to find the specified class instance in the java heap
1. Active call example 1
Program code:
Added two methods of secret and 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!");
}
}
Program execution result:
hook script:
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 results
You can see that the class and address of the instance object are successfully printed
The console can see that the two functions secret and staticSecret have been successfully called
Note: The results of calling these two functions are in the android studio console, not in the frida console. The frida console outputs the content in the js script.
2. Active call example 2
Slightly modify the above program code, add two private variables, try to hook the variable value
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!");
}
}
Program running result:
hook script:
//调用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)
The hook result
shows that the program first calls the getTotalValue function and outputs the value of total.
Then we call the script function callSecretFunc() in the frida console to call the secret function to modify the value of total.
Call getTotalValue again to output the value of total and find that the modification is successful.
Summarize:
- To get the instance variable in the class, you need to use .value to get its value. If you can’t get the value by printing directly (you can get information about the variable, because total itself is a reference type)
- In the frida console, we can repeatedly call the script function to achieve the purpose of repeatedly calling the program function
Chapter 4 Objection Quick Start
4.2 Installation and use of Objection
4.2.1 objection installation
pip install -U objection
Note: Since frida is updated quickly, it is necessary to ensure that the objection version is released after frida. The latest objection version is 1.11.0, the corresponding frida version is up to 14.2.14, and frida-tools is 9.2.2
4.2.2 objection use
1. Inject process into REPL
Objection is connected to the device through USB by default, and there is no need to use the -U parameter to specify usb
to 'set' the application demonstration injection process command like Frida: objection -g com.android.settings explore
Successful injection will display the following information:
In this way, you have successfully entered the REPL interface of objection, and you can enter exit to exit
2. Common commands:
Spacebar prompts for commands
-
help If you don't know the effect of the current command, you can use help before the command, and the explanation information of the command will be output
-
The jobs command
jobs list displays the current job
jobs kill deletes the job
-
frida command
View frida information
-
Memory roaming related commands
(1) android hooking list classes
print all classes in memory
(2) android hooking search classes key
searches for all classes containing the keyword key
(3) android hooking search methods key
searches for all methods that contain the keyword key
(4) android hooking list class_methods classname
View all methods of the class named classname
(5) android hooking list activities (services receivers providers)
print four major components, list all the activity activities of the process (service receiver provider)
-
Hook related commands
android hooking watch class_method methodName
hook the specified method
Continuously updating...