本文最后更新于 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 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
命令直接附加到当前前台正在运行的程序上,秒出结果

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/
对于 setImmediate
和 setTimeout
:https://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 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 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
,但是在程序中并没有被调用

这时有两个方法:
- 直接 hook
Checker
类的 code
变量,值为 0x200
- 主动调用
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;
public class MainActivity extends AppCompatActivity { TextView t1;
@Override 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;
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;
public class MainActivity extends AppCompatActivity { TextView t1;
@Override 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;
public class MainActivity extends AppCompatActivity { TextView t1;
@Override 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;
public class Checker { int num1; int num2; }
|
MainActivity 类
MainActivity
类的 get_flag()
方法传入一个 Checker
实例,当 num1==1234
且 num2==4321
时获取 flag,这个 get_flag()
方法也并没有被调用

Checker
两个变量 num1
和 num2

所以解决方法就是创建 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); }, 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;
public class MainActivity extends AppCompatActivity { TextView t1;
@Override 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;
public class Checker { int num1; int num2;
public Checker(int a, int b) { this.num1 = a; this.num2 = b; } }
|
MainActivity 类
先 Checker ch = new Checker(123, 321);
创建了一个 Checker
实例,然后 Checker
类的对象调用 flag
方法传入该对象,之后对 num1
和 num2
的两个值分别进行判断,两个参数必须都大于 512

Checker 类
两个变量 num1
和 num2
,通过构造函数去赋值

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); }, 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>){
} });
|