Frida常用方法 [Java]

其实都是官方文档的东西, 需要什么最好是看官方文档

基础语法

符号 作用
$init 构造方法 Hook 的时候用
$new() 实例化对象的时候用
Hook重载 语法
int类型 int
float类型 float
boolean类型 boolean
string类型 java.lang.String
byte类型 [B
char类型 [C
list结构 java.util.List
安卓上下文结构 android.content.Context

主动调用函数

  • 静态函数 static 可以直接调用
  • 动态函数要用choose找到实例才能操作

静态函数主动调用

1
2
3
4
5
Java.perform(function x(){
var LoginActivity = Java.use("com.xx.xx.xx.xx");
LoginActivity.function();
}
)

动态函数主动调用

1
2
3
4
5
6
7
8
9
10
11
Java.perform(function x(){
Java.choose("com.xx.xx.xx.xx",{
onMatch: function(instance){
instance.function();
},
onComplete: function(){

}
});
}
)

修改变量

APP 代码如下

1
2
3
4
5
public class FridaTest extends BaseFridaTest{
private static boolean static_bool_var = false;
private boolean bool_var = false;
private boolean same_name_bool_var = false;
}

  • 静态变量 static 可以直接设置值
  • 动态变量要用choose找到实例才能操作

静态变量

道理和调用函数相同

1
2
3
4
var LoginActivity3 = Java.use("com.xx.xx.xx.xx");

// 设置变量值后面必须加 `.value` 才能设置
LoginActivity3.static_bool_var.value = true;

动态变量

1
2
3
4
5
6
7
8
9
10
// 动态变量必须 choose 找到示例才能操作
Java.choose("com.xx.xx.xx.xx",{
onMatch: function(instance){
// 设置变量值后面必须加 `.value` 才能设置
instance.bool_var.value = true;
instance._same_name_bool_var.value = true;
},
onComplete: function(){
}
});

枚举

枚举类中方法

有的情况下我们要hook一个类下所有的方法(不知道他的方法类型和数量),这个时候我们就要用到 java 的反射 getDeclaredMethods()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Hook类的所有方法
Java.perform(function(){
var md5 = Java.use("com.xxx.xxx.xxx");
var methods = md5.class.getDeclaredMethods();
for(var j = 0; j < methods.length; j++){
var methodName = methods[j].getName();
console.log(methodName);
// hook methodNmae 这个类的所有方法(难点在于每个方法的参数是不同的)
for(var k = 0; k < md5[methodName].overloads.length; k++){
md5[methodName].overloads[k].implementation = function(){
// 这是 hook 逻辑
for(var i = 0; i < arguments.length; i++){
console.log(arguments[i]);
}
return this[methodName].apply(this, arguments); // 重新调用
}
}

}
});

枚举所有类

1
2
3
4
5
6
7
8
9
10
11
12
13
setTimeout(function (){
Java.perform(function (){
console.log("\n[*] enumerating classes...");
Java.enumerateLoadedClasses({
onMatch: function(_className){
console.log("[*] found instance of '"+_className+"'");
},
onComplete: function(){
console.log("[*] class enuemration complete");
}
});
});
});

Hook 构造对象(自定义参数)

参数是一个自定义类

类是这样的

1
2
3
4
5
6
7
8
9
10
11
Java.perform(function () {
var money = Java.use('com.xx.app.Money') // 如果要自定义实例化就要获取并重写
var utils = Java.use('com.xx.app.Utils');
utils.test.overload("com.xx.app.Money").implementation = function (obj) {
console.log("参数自定义类Hook中...");
// 重新实例化 $new()
var mon = money.$new(999,'我的天')
send(mon.getInfo()); // 调用 这个对象的 getInfo
return mon.getInfo(); // 根据需求return
}
});

Hook interface 接口类

目前在app逆向中遇到interface搜索其实现还是一顿乱搜。
可以使用反射得到实现的接口数组,然后打印出来即可,这个问题终于可以解决了。

Hook 构造函数

类的内部如下

1
2
3
4
5
6
7
8
9
10
Java.perform(function () {
var Money = Java.use('com.xx.xx.xx');
Money.$init.implementation = function (a, b) {
console.log("构造函数Hook中...");
send(a);
send(b);
send("Success!");
return this.$init(a, b);
}
});

解析:

  • $init 构造函数 和 python语法类似
  • this.$init(a, b) 这里的 this 指代,原始app中的函数,$init() 表示该类的实例化

Hook 数组(char 和 byte)

有很多方法我这里直接用 gson.dex 方法,再肉丝的星球中下载

打印 CharArray 和 byteArray

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Java.perform(function () {
// 调用 自写dex
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');

// CharArray
Java.use("java.util.Arrays").toString.overload('[C').implementation = function(charArray){
var result = this.toString(charArray);
console.log("charArray,result:",charArray,result)
console.log("charArray Object Object:",gson.$new().toJson(charArray));
return result;
}

// byteArray
Java.use("java.util.Arrays").toString.overload('[B').implementation = function(byteArray){
var result = this.toString(byteArray);
console.log("byteArray,result):",byteArray,result)
console.log("byteArray Object Object:",gson.$new().toJson(byteArray));
return result;
}
});

生成数组替换结果

java代码和上面例子相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Java.perform(function () {
// 调用 自写dex
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');

// CharArray 替换
Java.use("java.util.Arrays").toString.overload('[C').implementation = function(charArray){
var newCharArray = Java.array('char', [ '一','去','二','三','里' ]);
var result = this.toString(newCharArray);
console.log("newCharArray,result:",newCharArray,result)

// 利用 gson.dex 来修改
console.log("newCharArray Object Object:",gson.$new().toJson(newCharArray));

// 官方api方法
var newResult = Java.use('java.lang.String').$new(Java.array('char', [ '烟','村','四','五','家']))
return newResult;
}

});

Hook 重载方法

例两个同名的函数。给多少个参数,调用有多少参数的对应方法 必须使用 overload()方法!!!

  • function test(a)
  • function test(a,b,c)

解决方法 overload() 指定

  • 如果是一个 int 类型的参数那么 overload("int") 要指定参数与类型
  • 如果是 string 类型 overload("java.lang.String") 全路径
1
2
3
4
5
6
var Utils = Java.use('com.xx.app.Utils');
Utils.test.overload("int").implementation = function () {
console.log("重写方法Hook中...");
send("重写方法 Success!");
return this.test(55555555);
}

Hook 类中的类

1
com.xxx.xxx.xxx.xxx$InnerClasses

利用符号 $ 来操作

Hook 被混淆的函数名

有的代码混淆,函数名已经是乱码符号

1
2
3
4
// 比如说这
int ֏(int x) {
return x + 100;
}

甚至有一些不可视, 所以可以先编码打印出来, 再用编码后的字符串去 hook.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Java.perform(
function x() {

var targetClass = "com.example.hooktest.MainActivity";

var hookCls = Java.use(targetClass);
var methods = hookCls.class.getDeclaredMethods();

for (var i in methods) {
console.log(methods[i].toString());
console.log(encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?$/, "$1")));
}

hookCls[decodeURIComponent("%D6%8F")]
.implementation = function (x) {
console.log("original call: fun(" + x + ")");
var result = this[decodeURIComponent("%D6%8F")](900);
return result;
}
}
)

Hook 函数的参数为 map 等特殊类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Java.perform(function () {
/*
var ShufferMap = Java.use('com.xx.xx.ShufferMap');
ShufferMap.show.implementation = function (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 += valuestr;
}
send(result);
return this.show(map);
}
*/
var HashMap = Java.use('java.util.HashMap');
var ShufferMap = Java.use('com.xx.app.ShufferMap');
ShufferMap.show.implementation = function (map) {
var hm = HashMap.$new();
hm.put("user","dajianbang");
hm.put("pass","87654321");
hm.put("code","123456");
return this.show(hm);
}
});

方法2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Java.perform(function(){
var ShufferMap = Java.use("com.xx.app.ShufferMap");
console.log(ShufferMap);
ShufferMap.show.implementation = function(map){
console.log(JSON.stringify(map));
//Java map的遍历
// var key = map.keySet();
// var it = key.iterator();
// var result = "";
// while(it.hasNext()){
// var keystr = it.next();
// var valuestr = map.get(keystr);
// result += valuestr;
// }
// console.log(result);
// return result;

map.put("pass", "wawa");
map.put("guanwang", "666");

var retval = this.show(map);
console.log(retval);
return retval;

}
});

Hook 找不到包名的类

我们要hook类似这种代码的时候,找不到引用包,我们可以通过查看 smali 代码来看到她的引用

输出调用栈

1
2
3
function showStacks() {
send('调用栈输出\n\t' + Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
}

调用外部 dex

dex 中变量名尽量取得特殊一点,防止重名。
使用案例 再上面《Hook 数组(char 和 byte)》 中就有引用肉丝的 dex 例子

手动注册类

frida 用 registerClass 自己构造一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
Java.perform(function(){
var liquid = Java.use("com.xx.xx.xx");
var beer = Java.registerClass({
name: 'com.xx.xx.xx',
implements: [liquid],
methods: {
flow: function () {
return "look, beer is flowing!";
}
}
});
console.log("beer.bubble:",beer.$new().flow())
})