重生之我要成为 Frida 糕手:Frida 0x2-Frida 0x7

本文最后更新于 2025年3月28日 晚上

今天时间多点,多看了几道,终于要周末了嘿嘿,Java 层的 hook 练习结束了,然后就是 Native 层了

Frida 0x2

运行

这次没有输入也没有按钮

分析

直接反编译,代码还是挺简单的,流程也清晰

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
28
29
30
31
32
33
34
35
36
37
package com.ad2001.frida0x2;

import android.os.Bundle;
import android.util.Base64;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class MainActivity extends AppCompatActivity {
static TextView t1;

public static void get_flag(int a) {
if(a == 4919) {
try {
Cipher cipher0 = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(new byte[16]);
cipher0.init(2, new SecretKeySpec("HILLBILLWILLBINN".getBytes(), "AES"), iv);
String decryptedText = new String(cipher0.doFinal(Base64.decode("q7mBQegjhpfIAr0OgfLvH0t/D0Xi0ieG0vd+8ZVW+b4=", 0)));
MainActivity.t1.setText(decryptedText);
}
catch(Exception e) {
e.printStackTrace();
}

return;
}
}

@Override // androidx.fragment.app.FragmentActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(layout.activity_main);
MainActivity.t1 = (TextView)this.findViewById(id.textview);
}
}

MainActivity 类

MainActivity 类中有一个静态方法 get_flag() ,如果传入的 参数 a==4919,那么就解密 flag 输出

可以发现 get_flag() 方法在程序中并没有被调用,所以我们需要在注入时去调用这个静态方法

方法就是:主动去调用静态方法

Hook

Method 1

demo

1
2
3
4
5
6
7
8
9
10
11
12
function hook2(){
var MainActivity=Java.use("com.ad2001.frida0x2.MainActivity");
MainActivity.get_flag(4919);
}

function main(){
Java.perform(function (){
hook2();
})
}

setImmediate(main);

先启动程序(必须),然后进行 hook,直接把代码写在终端运行,保证在启动之后运行,如果像以前一样加上文件执行会 hook 不到,看到 SWDD 用 frida -U 'Frida 0x2' -l hook2.js 命令能执行,但是我的 cmd 和 PowerShell 都不能转义空格,单引号双引号也不行,然后就识别不到进程名称,所以这个方法实现不了

Method 2

更加简洁的 demo,虽然核心代码差不多一样,但是方法 1 中多了个 setImmediate() 函数,它会比程序先执行,也就是说在程序还没执行之前它调用的函数就已经执行完了,如果没有运行程序的话,相当于没有 hook

本方法中是注入之后立即调用执行

1
2
3
4
Java.perform(function() {
var MainActivity = Java.use("com.ad2001.frida0x2.MainActivity");
MainActivity.get_flag(4919);
});

先打开程序用 -F 命令直接附加到当前前台正在运行的程序上,秒出结果

1
frida -U -F -l hook2.js

Method 3

对于方法 1 的 setImmediate(main),还可以改为 setTimeout(main,1000),参考 SWDD 的:https://blog.shangwendada.top/index.php/2024/03/03/fridafrida%e6%93%8d%e4%bd%9c%e6%89%8b%e5%86%8c/

对于 setImmediatesetTimeouthttps://www.cnblogs.com/fsjohnhuang/p/4151595.html

1
2
3
4
5
6
7
8
9
10
11
12
function hook2(){
var MainActivity=Java.use("com.ad2001.frida0x2.MainActivity");
MainActivity.get_flag(4919);
}

function main(){
Java.perform(function (){
hook2();
})
}

setTimeout(main,1000);

Conclusion

主要是 hook 调用未被执行的静态方法,指定的类中包含了静态的方法,则我们可以直接调用该方法

hook 模板如下:

1
2
3
4
Java.perform(function (){
var <class_reference> = Java.use(“<package_name>.<class>”);
<class_reference>.function(val); // 需要调用的方法名称
})

Frida 0x3

运行

这一次有按钮了,并且点击会出现 【TRY AGAIN】

反编译

还是反编译看 Jvav 代码

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.ad2001.frida0x3;

import android.os.Bundle;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class MainActivity extends AppCompatActivity {
TextView t1;

@Override // androidx.fragment.app.FragmentActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(layout.activity_main);
Button btn = (Button)this.findViewById(id.button);
this.t1 = (TextView)this.findViewById(id.textView);
btn.setOnClickListener(new View.OnClickListener() {
@Override // android.view.View$OnClickListener
public void onClick(View v) {
byte[] arr_b1;
if(Checker.code == 0x200) {
Toast.makeText(MainActivity.this.getApplicationContext(), "YOU WON!!!", 1).show();
SecretKeySpec KS = new SecretKeySpec("glass123".getBytes(), "Blowfish");
byte[] arr_b = Base64.getDecoder().decode("MKxsZsY9Usw3ozXKKzTF0ymIaC8rs0AY74GnaKqkUrk=");
try {
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(2, KS);
arr_b1 = cipher.doFinal(arr_b);
}
catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
catch(NoSuchPaddingException e) {
throw new RuntimeException(e);
}
catch(IllegalBlockSizeException e) {
throw new RuntimeException(e);
}
catch(BadPaddingException e) {
throw new RuntimeException(e);
}
catch(InvalidKeyException e) {
throw new RuntimeException(e);
}

String decryptedString = new String(arr_b1, Charset.forName("UTF-8"));
MainActivity.this.t1.setText(decryptedString);
return;
}

Toast.makeText(MainActivity.this.getApplicationContext(), "TRY AGAIN", 1).show();
}
});
}
}

分析

MainActivity 类

可以看到设置了一个按钮点击监听器,当点击按钮时,会检查 Checker.code 是否为 0x200,如果是则显示 flag

Checker 类

分析 Checker 类能发现有一个静态变量 code,值为 0,还有一个静态方法 increase()code+2,但是在程序中并没有被调用

这时有两个方法:

  1. 直接 hook Checker 类的 code 变量,值为 0x200
  2. 主动调用 increase() 方法让 code 累加到 0x200

Hook

hook 静态变量 code

现在 hook 代码中的类不再是 MainActivity 而是 Checker 了,

1
2
3
4
5
6
7
8
9
10
11
function hook3(){
var Checker=Java.use("com.ad2001.frida0x3.Checker");
Checker.code.value=0x200;
}

function main(){
Java.perform(function (){
hook3();
})
}
setImmediate(main)

点击一下就得出结果

主动调用 increase() 方法

写一个简单的 for 循环,累加 0x100 次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function hook3(){
var Checker=Java.use("com.ad2001.frida0x3.Checker");
for (var i=0;i<0x100;i++){
Checker.increase();
console.log("value:",Checker.code.value); //可以选择不打印
}
}

function main(){
Java.perform(function (){
hook3();
})
}
setImmediate(main)

当累加到 512 时就得到结果

Conclusion

主要是去改变类中的静态变量的值

Frida 更改变量值的模板:

1
2
3
4
Java.perform(function (){
var <class_reference> = Java.use(“<package_name>.<class>”);
<class_reference>.<variable>.value = <value>; //需要修改的变量
})

Frida 0x4

运行

也是一个没有输入的只有一个静态页面

分析

反编译看代码,这次用的 Jadx

MainActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ad2001.frida0x4;

import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {
TextView t1;

/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.t1 = (TextView) findViewById(R.id.txtview);
}
}

Check

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ad2001.frida0x4;

/* loaded from: classes3.dex */
public class Check {
public String get_flag(int a) {
if (a == 1337) {
byte[] decoded = new byte["I]FKNtW@]JKPFA\\[NALJr".getBytes().length];
for (int i = 0; i < "I]FKNtW@]JKPFA\\[NALJr".getBytes().length; i++) {
decoded[i] = (byte) ("I]FKNtW@]JKPFA\\[NALJr".getBytes()[i] ^ 15);
}
return new String(decoded);
}
return "";
}
}

Check 类

MainActivity 类中没啥好分析的,重点来分析 Check

Check 类中有一个 get_flag 方法,当 a==1337 时,会输出解密的字符串,可以发现程序没有对这个方法调用,并且它也不是静态方法,这是一个方法的声明,所以需要先实例化,再通过实例去调用这个方法返回结果

Hook

使用 $new() 实例化 Check 类的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function hook4(){
var Check =Java.use("com.ad2001.frida0x4.Check");
var CheckObj=Check.$new();
var result=CheckObj.get_flag(1337);
console.log(result);
}

function main(){
Java.perform(function(){
hook4();
})
}

setImmediate(main);

直接输出结果

Conclusion

非静态方法 -> 实例化 -> 通过实例调用方法

Frida 创建类实例的模板如下

1
2
3
4
5
6
7
Java.perform(function() {

var <class_reference> = Java.use("<package_name>.<class>");
var <class_instance> = <class_reference>.$new(); // 创建类的实例
<class_instance>.<method>(); // 调用该实例中的方法

})

Frida 0x5

运行

也是一个什么都没有的静态页面

分析

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
28
29
30
31
32
33
34
35
36
37
38
39
package com.ad2001.frida0x5;

import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {
TextView t1;

/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.t1 = (TextView) findViewById(R.id.textview);
}

public void flag(int code) {
if (code == 1337) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec("WILLIWOMNKESAWEL".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(new byte[16]);
cipher.init(2, secretKeySpec, iv);
byte[] decodedEnc = Base64.getDecoder().decode("2Y2YINP9PtJCS/7oq189VzFynmpG8swQDmH4IC9wKAY=");
byte[] decryptedBytes = cipher.doFinal(decodedEnc);
String decryptedText = new String(decryptedBytes);
this.t1.setText(decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

MainActivity 类

有一个 flag 方法,与上一题类似,但这一题是 flag 方法在 MainActivity 类里,并且 MainActivity 已经在应用程序启动的时候实例化了,使用 Java.use 获取 MainActivity 类并调用 new() 创建实例会导致错误,所以可以获取 MainActivity 的实例,然后调用就可以了

Hook

在现有的实例上调用方法

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function hook5(){
Java.choose("com.ad2001.frida0x5.MainActivity",{
onMatch: function (instance){
console.log(instance);
instance.flag(1337)
},
onComplete: function (){
console.log("endhook")
}
})
}

function main(){
Java.perform(function(){
hook5();
})
}

setTimeout(main,500)

demo 中使用了一些 API 和 回调函数:

  • Java.choose:遍历 Java 堆,枚举指定类的活动实例。
  • onMatch() 回调函数:每当找到一个实例时触发定义的操作。
  • onComplete() 回调函数:执行一些清理工作,或者根据请求的结果更新。

Conclusion

大概流程:调用 MainActivity 中的非静态方法 -> 通过 Java.choose 搜索实例对象 -> 通过 onMatch() 回调进行 hook 逻辑 -> onComplete() 进行结束处理

Frida 在现有实例上调用方法的模板:

1
2
3
4
5
6
7
8
9
Java.performNow(function() {
Java.choose('<Package>.<class_Name>', {
onMatch: function(instance) {
// 匹配到类实例时进行的操作
},
onComplete: function() {}
// 完成操作后的回调函数
});
});

这里的 Java.performNow 是 Frida 中的一个函数,用于在 Java 运行时环境中执行代码

Frida 0x6

运行

还是一样的静态页面

分析

MainActivity 类

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
28
29
30
31
32
33
34
35
36
37
package com.ad2001.frida0x6;

import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {
TextView t1;

/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.t1 = (TextView) findViewById(R.id.textview);
}

public void get_flag(Checker A) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
if (1234 == A.num1 && 4321 == A.num2) {
Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec secretKeySpec = new SecretKeySpec("MySecureKey12345".getBytes(), "AES");
cipher.init(2, secretKeySpec);
byte[] decryptedBytes = Base64.getDecoder().decode("QQzMj/JNaTblEHnIzgJAQkvWJV2oK9G2/UmrCs85fog=");
String decrypted = new String(cipher.doFinal(decryptedBytes));
this.t1.setText(decrypted);
}
}
}

Checker 类

1
2
3
4
5
6
7
package com.ad2001.frida0x6;

/* loaded from: classes3.dex */
public class Checker {
int num1;
int num2;
}

MainActivity 类

MainActivity 类的 get_flag() 方法传入一个 Checker 实例,当 num1==1234num2==4321 时获取 flag,这个 get_flag() 方法也并没有被调用

Checker

两个变量 num1num2

所以解决方法就是创建 Checker 类的实例,给两个变量设置值,获取 MainActivity 实例然后将实例作为参数调用 get_flag 方法

Hook

创建一个符合条件的实例,传递给 get_flag 方法

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
function hook6(){
Java.choose("com.ad2001.frida0x6.MainActivity",{
onMatch: function (instance){
console.log(instance);

var Checker =Java.use("com.ad2001.frida0x6.Checker");
var CheckerObj=Checker.$new();

//实例化对象
CheckerObj.num1.value=1234;
CheckerObj.num2.value=4321;

instance.get_flag(CheckerObj); //调用 get_flag 方法
},
onComplete: function (){
console.log("endhook");
}
})
}

function main(){
Java.perform(function(){
hook6();
})
}

setTimeout(main,500)

Conclusion

MainActivity 中非静态方法且参数为非静态变量进行对象参数调用

Frida 使用对象参数调用方法的模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Java.performNow(function() {
Java.choose('<Package>.<class_Name>', {
onMatch: function(instance) {
var <class_reference> = Java.use("<package_name>.<class>");
var <class_instance> = <class_reference>.$new(); // 创建类的实例

<class_reference>.<variable>.value = <value>; // 实例化对象

instance.<method>(class_instance); // 调用方法
},
onComplete: function() {}
// 完成操作后的回调函数
});
});

Frida 0x7

运行

依旧是一样的静态界面

分析

MainActivity 类

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.ad2001.frida0x7;

import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {
TextView t1;

/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.t1 = (TextView) findViewById(R.id.textview);
Checker ch = new Checker(123, 321);
try {
flag(ch);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e2) {
throw new RuntimeException(e2);
} catch (BadPaddingException e3) {
throw new RuntimeException(e3);
} catch (IllegalBlockSizeException e4) {
throw new RuntimeException(e4);
} catch (NoSuchPaddingException e5) {
throw new RuntimeException(e5);
}
}

public void flag(Checker A) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
if (A.num1 > 512 && 512 < A.num2) {
Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec secretKeySpec = new SecretKeySpec("MySecureKey12345".getBytes(), "AES");
cipher.init(2, secretKeySpec);
byte[] decryptedBytes = Base64.getDecoder().decode("cL/bBqDmfO0IXXJCVFwYLeHp1k3mQr+SP6rlQGUPZTY=");
String decrypted = new String(cipher.doFinal(decryptedBytes));
this.t1.setText(decrypted);
}
}
}

Checker 类

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ad2001.frida0x7;

/* loaded from: classes3.dex */
public class Checker {
int num1;
int num2;

/* JADX INFO: Access modifiers changed from: package-private */
public Checker(int a, int b) {
this.num1 = a;
this.num2 = b;
}
}

MainActivity 类

Checker ch = new Checker(123, 321); 创建了一个 Checker 实例,然后 Checker 类的对象调用 flag 方法传入该对象,之后对 num1num2 的两个值分别进行判断,两个参数必须都大于 512

Checker 类

两个变量 num1num2,通过构造函数去赋值

Hook

通过构造函数传参

跟 0x6 一样的步骤,不过要通过构造函数传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function hook7(){
Java.choose("com.ad2001.frida0x7.MainActivity",{
onMatch: function (instance){
console.log(instance);

var Checker =Java.use("com.ad2001.frida0x7.Checker");
var CheckerObj=Checker.$new(520,520); //直接赋值

instance.flag(CheckerObj); // hook Checker 方法
},
onComplete: function (){
console.log("endhook");
}
})
}

function main(){
Java.perform(function(){
hook7();
})
}

setTimeout(main,500)

在实例化对象时我直接赋值了,也可以先实例化后赋值

1
2
3
4
var a = 520;
var b = 520;
var check = checker.$new(a,b);
instance.flag(check);

hook 构造方法

Checker 类中声明了构造方法,所以我们也可以直接 hook 构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function hook7(){
var Checker =Java.use("com.ad2001.frida0x7.Checker");
Checker.$init.implementation=function(a,b){
this.$init(520,520);
}

}

function main(){
Java.perform(function(){
hook7();
})
}

setImmediate(main);

Conclusion

Frida Hook 构造方法的模板

1
2
3
4
5
6
7
8
9
10
Java.perform(function() {
var <class_reference> = Java.use("<package_name>.<class>");
<class_reference>.$init.implementation = function(<args>){

/*
自定义方法实现
*/

}
});

重生之我要成为 Frida 糕手:Frida 0x2-Frida 0x7
http://example.com/2025/03/28/重生之我要成为-Frida-糕手-Frida-0x2-Frida-0x7/
作者
butt3rf1y
发布于
2025年3月28日
许可协议