侧边栏壁纸
博主头像
Fup1p1 's Blog 博主等级

梦想是财富自由~

  • 累计撰写 38 篇文章
  • 累计创建 24 个标签
  • 累计收到 9 条评论

目 录CONTENT

文章目录

【Android逆向】三、Frida逆向与利用自动化

Fup1p1
2023-01-30 / 1 评论 / 0 点赞 / 1574 阅读 / 0 字 / 正在检测是否收录...

01 Frida开发和调试环境搭建

详见大佬博客r0ysue

02 Objection

不想写了,自己看r0的文章吧。
Objection

03 Frida上手和逆向三段

关于frida启动

好文章

frida -U com.kevin.android -l hook.js//直接附加
frida -UF -l hook.js//直接附加到当前应用中去
package com.example.myapplication;

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="@@@###";
    private Static String total2="@@@###";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            fun(50,80);
        }
    }
    String fun(String x){
        total +=x;
        return x.toLowerCase();
    }
    int fun(int x,int y){
        Log.d("Fup1p1 m =",String.valueOf(x+y));
        Log.d("Fup1p1 tolowercase",fun("LOWERCASE"));
        return x+y;
    }
    String secret(){
        return total;
    }
    public static secret2(){
    	return total2;
    }

}

js脚本指南

一.所有针对Java层的Hook脚本必须处于Java.perform()的包装内,表示将其中的函数注入到java运行时。
二.使用Java.use()API去获取指定类的handle,类似于java中调用类静态方法的方式去获取对应的函数。如果函数有多个重载,要加.overload()
三.setImmediate()表示注入后立即执行,setTimeout(xxx,1000)表示延迟1000ms。

函数的重载

错误代码

function main(){
    Java.perform(function(){
        Java.use("com.example.myapplication.MainActivity").fun.implementation=function(arg1,arg2){
            var result=this.fun(114,514);
            console.log("arg1 arg2 result" ,arg1,arg2,result)
            return result;
        }
    })
}
setImmediate(main)

由于app中重载了fun函数,所以我们在hook调用fun时报错。

image

正确代码

function main(){
    Java.perform(function(){
        Java.use("com.example.myapplication.MainActivity").fun.overload("int","int").implementation=function(arg1,arg2){
            var result=this.fun(arg1,arg2);
            console.log("arg1 arg2 result" ,arg1,arg2,result)
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//打印调用栈
            return result;
        }
    })
}
setImmediate(main)

image-1676191907407

动态要找到实例然后主动调用,静态直接调用

动态

 Java.choose("com.example.myapplication.MainActivity",{
            onMatch:function(instance){
                console.log("found instance:" ,instance)
                instance.secret();
                console.log("found instance :",instance.secret())

            },onComplete:function(){}
        })

静态

var result =Java.use("com.example.myapplication.MainActivity").secret2()
        console.log(result)

总结

一篇好文,链接

静态方法

直接Java.use就行了

非静态,有两种思路

直接获取内存中已存在的对象

因为运行过程中对象的成员的值可能已经发生了变化,所以如果重新创建一个对象,新对象的值还是初始值,在调用算法或者函数的时候,可能会产生影响。

Java.choose('xxx.xxx.xxx ', //这里写类名
{
  //onMatch 匹配到对象时执行的函数
  onMatch: function (instance)
  {
  instance.funcxxx();
  },
 //堆中搜索完成后执行的回调函数
  onComplete: function ()
  {
  }
});
创建一个新对象

需要使用$new()

Java.use("com.xxx.xxx").$new()//无参构造函数
Java.use("com.xxx.xxx").$new(arg1....)//有参构造函数

frida构造数组,对象,Map和参数

打印数组

char arr[][] = new char[4][]; // 创建一个4行的二维数组
        arr[0] = new char[] { '春', '眠', '不', '觉', '晓' }; // 为每一行赋值
        arr[1] = new char[] { '处', '处', '闻', '啼', '鸟' };
        arr[2] = new char[] { '夜', '来', '风', '雨', '声' };
        arr[3] = new char[] { '花', '落', '知', '多', '少' };
Java.use("java.util.Arrays").toString.overload('[C').implementation=function(x){
        var result=this.toString(x);
        console.log("x,result",x,result);
        return result;
   }

我这里是可以正常输出x的,如果x输出时两个object,就用JSON.stringify(),将把一个 JavaScript 对象序列化为一个 JSON 字符串。

console.log("x,result",JSON.stringify(x),result);

或者使用gson打印,需要下载。然后放在frida-server同目录下。

Java.openClassFile("/data/local/tmp/xxx.dex").load();
const gson =Java.use("com.xxx.xxx.gson");
console.log("x,result",gson.$new().toJson(x),result);

自己构造一个数组

Java.use("java.util.Arrays").toString.overload('[C').implementation=function(x){
            var chararray=Java.array("char",['1','2','3','4','5']);
            var result=this.toString(chararray);
            console.log("x,result",x,result);
            return result;
       }

或者

Java.use("java.util.Arrays").toString.overload('[C').implementation=function(x){
            return Java.use("java.lang.String").$new(Java.array('char',['1','1','4','5','1']));
       }

类型转换

写一个水类

public class Water { // 水 类
    public static String flow(Water W) { // 水 的方法
        // SomeSentence
        Log.d("2Object", "water flow: I`m flowing");
        return "water flow: I`m flowing";
    }

    public final String still(Water W) { // 水 的方法
        // SomeSentence
        Log.d("2Object", "water still: still water runs deep!");
        return "water still: still water runs deep!";
    }
}

写一个饮料类去继承水类

public class Juice extends Water { // 果汁 类 继承了水类

    public String fillEnergy(){
        Log.d("2Object", "Juice: i`m fillingEnergy!");
        return "Juice: i`m fillingEnergy!";
    }

    public static void main() {

        Water w1 = new Water();
        flow(w1) ; //

        Juice J = new Juice(); // 实例化果汁类对象
        flow(J) ; // 调用水的方法  向上转型 J → W

        Water w2 = new Juice();
        ((Juice) w2).fillEnergy();//需要类型转换
    }
}

先调用一下水类

    var Waterhandle=null;
    Java.choose("com.r0ysue.a0526printout.Water",{
        onMatch:function(instance){
            console.log("found instance!",instance);
            console.log("water instance call!",instance.still(instance));
            Waterhandle=instance;
        },onComplete:function(){
            console.log("search complete!")
        }
    })

强制类型转换

实测发现,只能子类强制类型转换成父类,父类转子类的时候会报错,告诉你这不可能实现。。。

var Juicehandle=null;
    Java.choose("com.r0ysue.a0526printout.Juice",{
        onMatch:function(instance){
            console.log("found instance",instance);
            console.log("Juice method called",instance.fillEnergy());
            Juicehandle=instance;
        },onComplete:function(){"Search completed"}
    })
    var Waterhandle=Java.cast(Juicehandle,Java.use("com.r0ysue.a0526printout.Water"));//强制类型转换
    console.log(Waterhandle.still(Waterhandle));

根据接口创建一个java的类

public class milk implements liquid {
    public String flow(){
        Log.d("3interface", "flowing : interface ");
        return "nihao";
    };
    public static void main() {
        milk m = new milk();
        m.flow();
    }
}
public interface liquid {
    public String flow();
}

frida实现

 var beer=Java.registerClass({
        name:'com.r0ysue.a0526printout.beer',
        implements:[Java.use('com.r0ysue.a0526printout.liquid')],
        methods: {
            flow:function(){
                console.log("look i am beer!");
                return "taste good!";
            }
        }
        });
        console.log("beer.flow:",beer.$new().flow())

打印Map

        Map<String, String> mapr0ysue = new HashMap<>(); // 创建Map集合对象
        mapr0ysue.put("ISBN 978-7-5677-8742-1", "Android项目开发实战入门"); // 向Map集合中添加元素
        mapr0ysue.put("ISBN 978-7-5677-8741-4", "C语言项目开发实战入门");
        mapr0ysue.put("ISBN 978-7-5677-9097-1", "PHP项目开发实战入门");
        mapr0ysue.put("ISBN 978-7-5677-8740-7", "Java项目开发实战入门");
        Log.d("5map", "key值toString"+mapr0ysue.toString());
  Java.choose("java.util.HashMap",{
        onMatch:function(instance){
            if(instance.toString().indexOf("ISBN")!=-1){//遍历
                console.log("found HashMap",instance);
                console.log("HashMap toString",instance.toString());
            }
        },onComplete:function(){console.log("Search Completed!")

        }
    })
0

评论区