手把手教你使用更多的原生安卓开发app的框架frida API
回复“书籍”即可获赠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(this, arguments);
}
}
主动调用
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", {
onMatch: function (obj) {
//可以调用对象的字段和方法,如果字段和属性重名,字段需要加_
console.log(obj.getInfo(),obj.currency.value,obj._currency.value);
}, onComplete: function () {
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", {
onMatch: function (obj) {
console.log(
"Java.choose Wallet$InnerStructure: ", obj.bankCardsList.value
);
}, onComplete: function () {
}
});
匿名类
匿名类通常是所在的类后面跟$数字,在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(this, arguments);
}
}
}
//
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", {
onMatch: function (obj) {
console.log(obj.ordinal());
}, onComplete: function () {
}
})
//打印枚举类的属性
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({
onMatch: function (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);
}
},
onComplete: function () {
}
});
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(this, arguments);
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", {
onMatch: function (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);
}, onComplete: function () {
}
})
})
}
效果演示
总结
本次主要还是一些理论知识,其实有的之前都已经了解过了,只不过这次把常用的都列出来了。主要还是各种hook,和主动调用。
人生没有白走的路,加油!如果在操作过程中有任何问题,记得下面留言,我们看到会第一时间解决问题。
越努力,越幸运。我是码农星期八,如果觉得还不错,记得动手点赞一下哈,感谢你的观看。
小伙伴们,快快用实践一下吧!如果在学习过程中,有遇到任何问题,欢迎加我好友,我拉你进Python学习交流群共同探讨学习。
------------------- End -------------------
往期精彩文章推荐:
欢迎大家点赞,留言,转发,转载,感谢大家的相伴与支持
想加入Python学习群请在后台回复【入群】
万水千山总是情,点个【在看】行不行
/今日留言主题/
随便说一两句吧~~
评论