Materiales de aprendizaje: "Android Frida Reverse y Packet Capture Actual Combat" Chen Jialin / Autor
Directorio de artículos
entorno básico
- Instalar Android Studio y configurar adb
- Una máquina real rooteada o una máquina virtual Android
Capitulo 3 Frida entrada inversa Gancho de capa Java
3.1 Fundamentos de Frida
3.1.3 Conocimientos básicos de Frida
Frida tiene dos modos de operación
- CLI (modo de línea de comandos)
inyecta código js en el proceso a través de la línea de comandos - Modo RPC
Inyecte el código js en el proceso a través de python, esencialmente use el código js para el gancho
Frida tiene dos formas de operar la aplicación
- En el modo spwan
, Frida tiene derecho a iniciar la aplicación. Incluso si se inicia la aplicación de destino,
Frida la reiniciará. Se puede especificar el parámetro -f para operar la aplicación en modo spwan. - Se ha iniciado la aplicación del modo adjuntar
y frida inyecta el programa a través de ptrace para ejecutar el enlace.
3.1.4Configuración del IDE de Frida
- Instalar nodo y entorno npm
- git descargar repositorio frida-agent-example
git clone https://github.com/oleavr/frida-agent-example.git 下载frida-agent-example仓库
cd frida-agent-example/
npm install
Use vscode para abrir, cree una carpeta en la carpeta frida-agent-example para escribir scripts
3.2 Primeros pasos con el guion de frida
3.2.1 El concepto de guion frida
Ejemplo:
setTimeout(function(){
Java.perform(function(){
console.log("hello,world")
})
})
代码分析:
1. 调用setTimeout方法将匿名函数注册到js运行库中
2. 在函数中调用Java.perform方法,将匿名函数注册到App的java运行库中,并执行函数
Después de ejecutar frida-server en el teléfono móvil, puede usar el comando frid-ps -U para ver el proceso en ejecución
y luego usar frida -U -l test0.js android.process.media para inyectar el script en el proceso, y puede encontrar que helloworld se imprime después de la inyección
En los parámetros anteriores, -U especifica el dispositivo usb, -l especifica la ruta donde se encuentra el script de inyección y el último androdi.process.media es el nombre del proceso que se ejecuta en el dispositivo
3.2.2 Conceptos básicos de gancho de capa Java
1. Un estudio preliminar sobre anzuelos
código de proyecto de estudio android
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));
}
}
Ejecución de resultados: la consola de Android Studio continuará emitiendo x+y, y x e y aumentarán gradualmente
código de secuencia de comandos de enlace:
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)
Inyecte el script en el proceso frida -U -l test0.js com.example.hooktest1
Puede ver que después de la inyección, la consola de comandos generará el contenido de la función de script js
Mire la consola de comandos de Android Studio, puede encontrar que la consola ha estado generando sum=: 732
Indica que llamar a this.fun(666,66) en el script es exitoso
2. El gancho de la función sobrecargada
Código ligeramente modificado:
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();
}
Resultado de la ejecución del programa:
guión de gancho:
//定位重载函数,使用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)
resultado de gancho
Consola:
3.2.3 Llamada activa de la capa Java
La implementación anterior es una llamada pasiva: es decir, la función se ejecuta con la lógica normal de la aplicación. La
llamada activa puede llamar directamente a la función clave sin necesidad de que la aplicación ejecute la función.
Aquí hay dos situaciones:
- Función de clase (método estático) Simplemente use Java.use para encontrar la clase donde se encuentra la función
- Método de instancia (método dinámico) Use la función Java.choose api para encontrar la instancia de clase especificada en el montón de Java
1. Ejemplo de llamada activa 1
Código del programa:
se agregaron dos métodos de secreto y 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!");
}
}
Resultado de la ejecución del programa:
guión de gancho:
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)
Resultados de enlace
Puede ver que la clase y la dirección del objeto de instancia se imprimen correctamente
La consola puede ver que las dos funciones secret y staticSecret se han llamado con éxito
Nota: Los resultados de llamar a estas dos funciones están en la consola de Android Studio, no en la consola de Frida. La consola de Frida genera el contenido en el script js.
2. Ejemplo de llamada activa 2
Modifique ligeramente el código del programa anterior, agregue dos variables privadas, intente enganchar el valor de la variable
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!");
}
}
Resultado de la ejecución del programa:
guión de gancho:
//调用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)
El resultado del enlace
muestra que el programa primero llama a la función getTotalValue y genera el valor del total.
Luego llamamos a la función de script callSecretFunc() en la consola de Frida para llamar a la función secreta para modificar el valor del total.
Llame a getTotalValue nuevamente para generar el valor del total y encuentre que la modificación es exitosa.
Resumir:
- Para obtener la variable de instancia en la clase, debe usar .value para obtener su valor. Si no puede obtener el valor imprimiendo directamente (puede obtener información sobre la variable, porque el total en sí mismo es un tipo de referencia)
- En la consola frida, podemos llamar repetidamente a la función de script para lograr el propósito de llamar repetidamente a la función del programa.
Capítulo 4 Objeción Inicio rápido
4.2 Instalación y uso de Objeción
4.2.1 instalación de objeciones
pip install -U objeción
Nota: Dado que frida se actualiza rápidamente, es necesario asegurarse de que la versión de objeción se publique después de frida. La última versión de objeción es 1.11.0, la versión de frida correspondiente es hasta 14.2.14 y frida-tools es 9.2.2
4.2.2 uso de objeciones
1. Inyectar proceso en REPL
La objeción está conectada al dispositivo a través de USB de manera predeterminada, y no es necesario usar el parámetro -U para especificar usb
para 'configurar' el comando del proceso de inyección de demostración de la aplicación como Frida: objeción -g com.android.settings explore
La inyección exitosa lo hará mostrar la siguiente información:
De esta manera, ha ingresado con éxito a la interfaz de objeción REPL y puede ingresar exit to exit
2. Comandos comunes:
Indicaciones de la barra espaciadora para los comandos
-
ayuda Si no conoce el efecto del comando actual, puede usar la ayuda antes del comando, y se generará la información de explicación del comando.
-
El comando de trabajos
lista de trabajos muestra el trabajo actual
trabajos matar elimina el trabajo
-
comando frida
Ver información de frida
-
Comandos relacionados con la itinerancia de memoria
(1) las clases de la lista de conexión de Android
imprimen todas las clases en la memoria
(2) la clave de clases de búsqueda de enganche de Android
busca todas las clases que contienen la clave de palabra clave
(3) la clave de métodos de búsqueda de enganche de Android
busca todos los métodos que contienen la clave de palabra clave
(4) lista de enganches de Android class_methods classname
Ver todos los métodos de la clase llamada classname
(5) las actividades de la lista de enganches de Android (proveedores de receptores de servicios)
imprimen cuatro componentes principales, enumeran todas las actividades de actividad del proceso (proveedor de receptores de servicios)
-
Enganche los comandos relacionados con
android hooking watch class_method methodName
enganche el método especificado
Actualizando continuamente...