手把手教你使用更多的原生安卓开发app的框架frida API

共 24255字,需浏览 49分钟

 ·

2022-06-24 17:33

点击上方“Python爬虫与数据挖掘”,进行关注

回复“书籍”即可获赠Python从入门到进阶共10本电子书

蜡烛有心还惜别,替人垂泪到天明。

大家好,我是码农星期八!本教程只用于学习探讨,不允许任何人使用技术进行违法操作,阅读教程即表示同意!

前言

上节课学习了如何hook关键代码定位,本节课来继续学习如何学习更多的frida API吧!

官方文档

frida的官方JavaScriptAPI:https://frida.re/docs/javascript-api/

用到的app

app-release.apk

hook普通方法和静态方法

普通方法和静态方法hook是一样的,不需要区分。

如果是主动调用的话,是需要区分静态方法和普通方法的。

代码

//普通方法
let money = Java.use("com.xiaojianbang.hook.Money");
    money.getInfo.implementation = function ({
    console.log("money.getInfo 被调用");
    return this.getInfo();
}
//静态方法
money.setFlag.overload('java.lang.String').implementation = function (str{
    console.log("money.getInfo 被调用");
    console.log("参数 str:", str);
    return this.setFlag(str);
}

函数参数和返回值的修改

let money = Java.use("com.xiaojianbang.hook.Money");
money.getInfo.implementation = function ({
    console.log("money.getInfo 被调用");
    return "getInfo 返回值被修改了";
}

hook构造方法($init)

let money = Java.use("com.xiaojianbang.hook.Money");
money.$init.implementation = function (str, i{
    console.log(str, i)
    return this.$init("张三"2000);

}

对象参数的构造与修改($new)

//构造
let wallet = Java.use("com.xiaojianbang.hook.Wallet");
let money = Java.use("com.xiaojianbang.hook.Money");

wallet.deposit.implementation = function (arg_money{
    console.log("arg arg_money:",arg_money);
    //deposit需要的是Money对象,这里通过$new new出来
    return this.deposit(money.$new("张三",10))
}
修改
wallet.deposit.implementation = function (a{
    console.log("arg a:", a);
    //修改传过来Money对象的值
    a.setAmount(15);
    console.log(a.getAmount())
    return this.deposit(a)
}

打印HashMap

function printMap(map{
    var result = {};
    var keyset = map.keySet();
    var it = keyset.iterator();
    while (it.hasNext()) {
        var keystr = it.next().toString();
        var valuestr = map.get(keystr).toString();
        result[keystr] = valuestr;
    }
    return JSON.stringify(result);
}

通过类型上转方式

function printMap2(map{
 return Java.cast(map, Java.use("java.util.HashMap"));
}

重载方法 使用overload进行区分

var utils = Java.use("com.xiaojianbang.hook.Utils");
utils.getCalc.overload('int''int').implementation = function (a, b{
    console.log(a, b);
    return this.getCalc(a, b);
}
utils.getCalc.overload('int''int''int').implementation = function (a, b, c{
    console.log(a, b, c);
    return this.getCalc(a, b, c);
}
utils.getCalc.overload('int''int''int''int').implementation = function (a, b, c, d{
    console.log(a, b, c, d);
    return this.getCalc(a, b, c, d);
}

hook所有重载

var utils = Java.use("com.xiaojianbang.hook.Utils");
//overloads获取的重载的函数
var overloadsArr = utils.getCalc.overloads;
for (var i = 0; i < overloadsArr.length; i++) {
    overloadsArr[i].implementation = function ({
        showStack();
        var params = "";
        for (var j = 0; j < arguments.length; j++) {
            //arguments是参数列表,arguments[0]就是第一个参数
            params += arguments[j] + " ";
        }
        console.log("utils.getCalc is called! params is: ", params);
        // if(arguments.length == 2){
        //     return this.getCalc(arguments[0], arguments[1]);
        // }else if(arguments.length == 3){
        //     return this.getCalc(arguments[0], arguments[1], arguments[2]);
        // }else if(arguments.length == 4){
        //     return this.getCalc(arguments[0], arguments[1], arguments[2], arguments[3]);
        // }
        console.log(this);
        return this.getCalc.apply(thisarguments);
    }
}

主动调用

var money = Java.use("com.xiaojianbang.hook.Money");
money.setFlag("静态方法主动调用");
// 普通方法 实例化+对象.方法()
var moneyObj = money.$new("卢布"1000);
console.log(moneyObj.getInfo());

//Java.choose,搜索内存中的某个对象,传入类即可
Java.choose("com.xiaojianbang.hook.Money", {
    onMatchfunction (obj{
//可以调用对象的字段和方法,如果字段和属性重名,字段需要加_
        console.log(obj.getInfo(),obj.currency.value,obj._currency.value);
    }, onCompletefunction ({
        console.log("内存中的Money对象搜索完毕");

    }
})

获取和修改类的字段

let money = Java.use("com.xiaojianbang.hook.Money");
//静态字段需要通过value才能获取具体值
console.log(money.flag.value);
//设置值直接设置即可
money.flag.value = "fuck";
console.log(money.flag.value);
//普通字段
let moneyObj = money.$new("张三"10);
console.log(moneyObj.currency.value);
moneyObj.currency.value = "普通字段修改";
console.log(moneyObj.currency.value);

hook内部类和匿名类

内部类

内部类好说,直接后面+$即可

let wallet$InnerStructure = Java.use("com.xiaojianbang.hook.Wallet$InnerStructure")
console.log(wallet$InnerStructure)
//通过choose找到InnerStructure对象,然后调用它的方法或值
Java.choose("com.xiaojianbang.hook.Wallet$InnerStructure", {
    onMatchfunction (obj{
        console.log(
            "Java.choose Wallet$InnerStructure: ", obj.bankCardsList.value
        );
    }, onCompletefunction ({

    }
});

匿名类

匿名类通常是所在的类后面跟$数字,在smail中比较容易看到。

let money$1 = Java.use("com.xiaojianbang.app.MainActivity$1");
money$1.getInfo.implementation = function ({
    var result = this.getInfo();
    console.log("money.getInfo result: ", result);
    return result;
}

枚举所有已加载的类

console.log(Java.enumerateLoadedClassesSync().join("\n"))

枚举类的所有方法

let wallet = Java.use("com.xiaojianbang.hook.Wallet")
//方法
let methods = wallet.class.getDeclaredMethods();
for (let i = 0; i < methods.length; i++) {
    console.log(methods[i].getName());
}
//构造
let constructors = wallet.class.getDeclaredConstructors();
console.log("============================");
for (let i = 0; i < constructors.length; i++) {
    console.log(constructors[i].getName());
}
// 字段
let fields = wallet.class.getDeclaredFields()
console.log("============================");
for (let i = 0; i < fields.length; i++) {
    console.log(fields[i].getName());
}
//内部类
var classes = wallet.class.getDeclaredClasses();
console.log("============================");
for (let i = 0; i < classes.length; i++) {
    console.log(classes[i].getName());
    //classes[i] 这里得到的已经是类的字节码,不需要再.calss
    var Wallet$InnerStructure = classes[i].getDeclaredFields();
    for (let j = 0; j < Wallet$InnerStructure.length; j++) {
        console.log(Wallet$InnerStructure[j].getName());
    }
}

hook类的所有方法

function hookFunc(cls,methodName{
    console.log(methodName);
    var overloadsArr = cls[methodName].overloads;
    for (let j = 0; j < overloadsArr.length; j++) {
        overloadsArr[j].implementation = function ({
            var params = "";
            for (var k = 0; k < arguments.length; k++) {
                params += arguments[k] + " ";
            }
            console.log("cls." + methodName + " is called! params is: ", params);
            return this[methodName].apply(thisarguments);
        }
    }
}
//
var utils = Java.use("com.xiaojianbang.hook.Utils");
var methods = utils.class.getDeclaredMethods();
for (let i = 0; i < methods.length; i++) {
    var methodName = methods[i].getName();
    hookFunc(utils,methodName);
}

registerClass 给app注入一个类

const MyWeirdTrustManager = Java.registerClass({
    name'com.xiaojianbang.app.MyRegisterClass',
    implements: [Java.use("com.xiaojianbang.app.TestRegisterClass")],
    fields: {
        description'java.lang.String',
        limit'int',
    },
    methods: {
        $init() {
            console.log('Constructor called');
        },
        test1: [{
            returnType'void',
            argumentTypes: [],
            implementation() {
                console.log('test1 called');
            }
        }, {
            returnType'void',
            argumentTypes: ['java.lang.String''int'],
            implementation(str, num) {
                console.log('test1(str, num) called', str, num);
            }
        }],
        test2(str, num) {
            console.log('test2(str, num) called', str, num);
            return null;
        },
    }
});
var myObj = MyWeirdTrustManager.$new();
myObj.test1();
myObj.test1("xiaojianbang1"100);
myObj.test2("xiaojianbang2"200);
myObj.limit.value = 10000;
console.log(myObj.limit.value);

frida注入dex

Java.openClassFile("/data/local/tmp/patch.dex").load();
var test = Java.use("com.xiaojianbang.myapplication.Test");
var utils = Java.use("com.xiaojianbang.hook.Utils");
utils.shufferMap.implementation = function (map{
    var result = test.print(map);
    console.log(result);
    return result;
}

hook 枚举类(Java.choose)

Java.choose("com.xiaojianbang.app.Season", {
    onMatchfunction (obj{
        console.log(obj.ordinal());
    }, onCompletefunction ({

    }
})
//打印枚举类的属性
console.log(Java.use("com.xiaojianbang.app.Season").values());

frida写文件

var ios = new File("/sdcard/xiaojianbang.txt""w");
ios.write("xiaojianbang is very good!!!\n");
ios.flush();
ios.close();

android Context(上下文)的获取

var current_application = Java.use('android.app.ActivityThread').currentApplication();
var context = current_application.getApplicationContext();

Java.cast  向上转型的

// 向上转型的,不能用toString直接得到结果,比如Map、List类型的打印
var utils = Java.use("com.xiaojianbang.hook.Utils");
utils.shufferMap2.implementation = function (map{
    console.log("map: ", map);
    console.log("map: ", printMap(map));
    console.log("map: ", printMap2(map));
    var result = Java.cast(map, Java.use("java.util.HashMap"));
    console.log("map: ", result);
    return this.shufferMap2(result);
}

数组的构建

var utils = Java.use("com.xiaojianbang.hook.Utils");
console.log(
    utils.myPrint(["xiaojianbang""QQ:243757""VX:xia88""公众号"])
);
//构建
var strarr = Java.array(
    "Ljava.lang.String;",
    ["123""123""VX:123""123"]
);
console.log(utils.myPrint(strarr));

Object数组的构建

var utils = Java.use("com.xiaojianbang.hook.Utils");
var bankCard = Java.use("com.xiaojianbang.hook.BankCard");
var bankCardObj = bankCard.$new("xiaojianbang""123456789""CBDA"1"15900000000");
var integer = Java.use("java.lang.Integer");
var boolean = Java.use("java.lang.Boolean");
//var objarr = Java.array(
// "Ljava.lang.Object;",
// ["xiaojianbang", integer.$new(30), boolean.$new(true), bankCardObj]
//);
console.log(
    utils.myPrint(["xiaojianbang", integer.$new(30), boolean.$new(true), bankCardObj])
);

Arraylist的主动调用

let arrayList = Java.use("java.util.ArrayList").$new();
let integer = Java.use("java.lang.Integer");
let boolean = Java.use("java.lang.Boolean");
let bankCard = Java.use("com.xiaojianbang.hook.BankCard");
let bankCardObj = bankCard.$new("xiaojianbang""123456789""CBDA"1"15900000000");
arrayList.add("xiaojianbang");
arrayList.add(integer.$new(30));
arrayList.add(boolean.$new(true));
arrayList.add(bankCardObj);

var utils = Java.use("com.xiaojianbang.hook.Utils");
console.log(utils.myPrint(arrayList));

hook动态加载的dex(Java.enumerateClassLoaders)

console.log("-->:", Java.enumerateLoadedClassesSync().join("\n"));
// 这样hook的话基本上是加载之后的,不会出现hook不到,所以需要通过这种方式
Java.enumerateClassLoaders({
    onMatchfunction (loader{
        try {
            //loader这就是红波浪线
            Java.classFactory.loader = loader;
            var dynamic = Java.use("com.xiaojianbang.app.Dynamic");
            console.log("dynamic: ", dynamic);
            //console.log(dynamic.$new().sayHello());
            dynamic.sayHello.implementation = function ({
                console.log("hook dynamic.sayHello is run!");
                return "xiaojianbang";
            }
        } catch (e) {
            console.log("e:",loader);
        }
    },
    onCompletefunction ({
    }
});

hook动态加载的dex(DexClassLoader)

var dexClassLoader = Java.use("dalvik.system.DexClassLoader");
dexClassLoader.loadClass.overload('java.lang.String').implementation = function (className{
    //console.log(className);
    var result = this.loadClass(className);
    //console.log("class: ", result);
    //console.log("class.class: ", result.class);
    //console.log("xxxxxxxx: ", result.getDeclaredMethods());
    if("com.xiaojianbang.app.Dynamic" === className){
        Java.classFactory.loader = this;
        var dynamic = Java.use("com.xiaojianbang.app.Dynamic");
        console.log("dynamic: ", dynamic);
        //var clazz = dynamic.class;
        //console.log("xxxxxxxx: ", clazz.getDeclaredMethods()[0].invoke(clazz.newInstance(), []));
        //console.log(dynamic.$new().sayHello());
        dynamic.sayHello.implementation = function ({
            console.log("dynamic.sayHello is called");
            return "xiaojianbang";
        }
        console.log(dynamic.$new().sayHello());

    }
    return result;
}

让hook只在指定函数内生效

var mainActivity = Java.use("com.xiaojianbang.app.MainActivity");
var stringBuilder = Java.use('java.lang.StringBuilder');
mainActivity.generateAESKey.implementation = function ({
    console.log("mainActivity.generateAESKey is called!");
    stringBuilder.toString.implementation = function ({
        var result = this.toString();
        console.log(result);
        return result;
    };
    var result = this.generateAESKey.apply(thisarguments);
    stringBuilder.toString.implementation = null;  //取消hook
    return result;
};

简单示例

主动调用登录按钮

xx牛.apk

如何通过frida点击这个登录按钮呢?

代码

//主动点击登录按钮
function call_login_btn({
    Java.perform(function ({
        //通过寻找内存中的对象方式找到LoginActivity
        Java.choose("com.dodonew.online.ui.LoginActivity", {
            onMatchfunction (objectWrapper{
                //构建上下文
                let current_application = Java.use('android.app.ActivityThread').currentApplication();
                let context = current_application.getApplicationContext();
                //构建OnClick需要的View
                let view = Java.use("android.view.View")
                let viewObj = view.$new(context);
                //设置按钮id
                viewObj.setId(2131558593);
                //点击
                objectWrapper.onClick(viewObj);
            }, onCompletefunction ({

            }
        })
    })
}

效果演示

总结

本次主要还是一些理论知识,其实有的之前都已经了解过了,只不过这次把常用的都列出来了。主要还是各种hook,和主动调用。

人生没有白走的路,加油!如果在操作过程中有任何问题,记得下面留言,我们看到会第一时间解决问题。

越努力,越幸运。我是码农星期八,如果觉得还不错,记得动手点赞一下哈,感谢你的观看。

小伙伴们,快快用实践一下吧!如果在学习过程中,有遇到任何问题,欢迎加我好友,我拉你进Python学习交流群共同探讨学习。

------------------- End -------------------

往期精彩文章推荐:

欢迎大家点赞,留言,转发,转载,感谢大家的相伴与支持

想加入Python学习群请在后台回复【入群

万水千山总是情,点个【在看】行不行

/今日留言主题/

随便说一两句吧~~

浏览 40
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报