1.使用 jadx 进行逆向工程的基础知识。
2.应具备理解 Java 代码的能力。
3.具备编写小型 JavaScript 代码片段的能力。
4.熟悉 adb。
5.设备已 root。
6.Frida环境配置。
让我们从非常基础的知识开始。
什么是钩子?
Hook是指拦截和修改应用程序或Android系统中函数或方法行为的过程。例如,我们可以钩取我们应用程序中的一个方法,并通过插入我们自己的实现来改变其功能。
现在,让我们尝试在一个应用程序中钩取一个方法。我们将使用JavaScript API 来完成这个任务,但值得注意的是,Frida也支持Python。
首先让我提供给你一个模板,然后我们一步步来解释。
Java.perform(function() {
var <class_reference> = Java.use("<package_name>.<class>");
<class_reference>.<method_to_hook>.implementation = function(<args>) {/*
我们自己的方法实现
*/}
})
◆Java.perform
是 Frida 中用于创建一个特殊上下文的函数,让你的脚本能够与 Android 应用程序中的 Java 代码进行交互。它就像是打开了一扇门,让你能够访问并操纵应用程序内部运行的 Java 代码。一旦进入这个上下文,你就可以执行诸如钩取方法或访问 Java 类等操作来控制或观察应用程序的行为。
◆var <class_reference> = Java.use("<package_name>.<class>");
在这里,你声明一个变量<class_reference>
来表示目标 Android 应用程序中的一个 Java 类。你使用Java.use
函数指定要使用的类,该函数接受类名作为参数。<package_name>
表示 Android 应用程序的包名,<class>
表示你想要与之交互的类。
<package_name>
:
◆<class_reference>.<method_to_hook>.implementation = function(<args>) {}
在所选的类内部,通过<class_reference>.<method_to_hook>
符号访问你想要钩取的方法。这是你可以定义自己的逻辑以在钩取的方法被调用时执行的地方。<args>
表示传递给函数的参数。
通过Jadx分析Frida-labs 0x1
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(C0570R.layout.activity_main);
final EditText editText = (EditText) findViewById(C0570R.C0573id.editTextTextPassword);
this.f103t1 = (TextView) findViewById(C0570R.C0573id.textview1);
final int i = get_random();
((Button) findViewById(C0570R.C0573id.button)).setOnClickListener(new View.OnClickListener() { // from class: com.ad2001.frida0x1.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View view) {
String obj = editText.getText().toString();
if (TextUtils.isDigitsOnly(obj)) {
MainActivity.this.check(i, Integer.parseInt(obj));
} else {
Toast.makeText(MainActivity.this.getApplicationContext(), "Enter a valid number !!", 1).show();
}
}
});
}
可以发现,在onCreate方法中,有一个监听事件,监听了button的点击,当按钮点击下去之后,程序首先判断输入是不是数字,是数字的话,就将其从string转化为int,再进入check中与i比较,因此我们需要检查check方法。
void check(int i, int i2) {
if ((i * 2) + 4 == i2) {
Toast.makeText(getApplicationContext(), "Yey you guessed it right", 1).show();
StringBuilder sb = new StringBuilder();
for (int i3 = 0; i3 < 20; i3++) {
char charAt = "AMDYV{WVWT_CJJF_0s1}".charAt(i3);
if (charAt < 'a' || charAt > 'z') {
if (charAt >= 'A') {
if (charAt <= 'Z') {
charAt = (char) (charAt - 21);
if (charAt >= 'A') {
}
charAt = (char) (charAt + 26);
}
}
sb.append(charAt);
} else {
charAt = (char) (charAt - 21);
if (charAt >= 'a') {
sb.append(charAt);
}
charAt = (char) (charAt + 26);
sb.append(charAt);
}
}
this.f103t1.setText(sb.toString());
return;
}
Toast.makeText(getApplicationContext(), "Try again", 1).show();
}
本方法显而易见就是检查输入是否能够满足i*2 + 4 == i2,如果满足则将flag输出到f103t1所绑定的textView控件上,其中用于判断的i则来自get_random。
int get_random() {
return new Random().nextInt(100);
}
显而易见,本方法就只是普通的返回一个随机数。
对于本样例程序,我们有两种方法去解决,首先我们可以直接hook程序逻辑。更改随机产生的值为一个固定值。或者hook check方法更改check方法传入的参数。
function hook(){
var MainActivity = Java.use("com.ad2001.frida0x1.MainActivity");
MainActivity.get_random.implementation = function (){
return 0;
}
}function main(){
Java.perform(function (){
hook();
})
}setImmediate(main);
代码解释如下:
1.首先定义了一个名为hook
的JavaScript函数,其中包含了对目标应用特定方法的hook逻辑。
hook函数通过Frida的Java API来获取目标应用中的MainActivity类。
然后,它通过Java.use()方法获取了MainActivity类的引用,使得我们可以访问该类的方法。
最后,hook函数将MainActivity类中的get_random方法进行了修改。它用自定义的实现替换了原有方法的实现,使得每次调用get_random方法时都返回固定值0。
2.接着定义了一个名为main
的JavaScript函数,其中包含了Frida的Java.perform()
方法,用于执行指定的hook逻辑。
3.最后,通过setImmediate()
函数调用main
函数,确保在Frida脚本启动后立即执行。
如果我们检查check函数的参数,第一个参数i表示随机数,而第二个参数i2对应于用户输入的数字。让我们使用Frida来捕获并转储这两个参数。
在处理具有参数的方法时,重要的是使用overload(arg_type)关键字指定预期的参数类型。此外,在钩入方法时确保包括这些指定的参数在你的实现中。在这里,我们的check()函数接受两个整数参数,所以我们可以这样指定:
a.check.overload(int, int).implementation = function(a, b) {
...
}
function hook2(){
var MainActivity = Java.use("com.ad2001.frida0x1.MainActivity");
MainActivity.check.overload('int','int').implementation = function (a,b){
console.log("Origin i and i2 = ",a,b);
return this.check(a,b);
}
}function main(){
Java.perform(function (){
hook2();
})
}setImmediate(main);
我们可以使用console.log查看传入的a与b是什么。
在this.check(a,b);
中的a,b改为自己设定的值就可以了。
在之前讲到的Java.use Api中,如果我们指定的类中包含了静态的方法,则我们可以直接调用该方法。模板如下:
Java.perform(function (){
var <class_reference> = Java.use("<package_name>.<class>");
a.function(val);
})
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;/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {/* renamed from: t1 */
static TextView f103t1;/* 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(C0569R.layout.activity_main);
f103t1 = (TextView) findViewById(C0569R.C0572id.textview);
}public static void get_flag(int a) {
if (a == 4919) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec("HILLBILLWILLBINN".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(new byte[16]);
cipher.init(2, secretKeySpec, iv);
byte[] decryptedBytes = cipher.doFinal(Base64.decode("q7mBQegjhpfIAr0OgfLvH0t/D0Xi0ieG0vd+8ZVW+b4=", 0));
String decryptedText = new String(decryptedBytes);
f103t1.setText(decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
function hook(){
var MainActivity = Java.use("com.ad2001.frida0x2.MainActivity");
MainActivity.get_flag(4919);
}function main(){
Java.perform(function (){
hook();
})
}setImmediate(main);
frida -U -f com.ad2001.frida0x2 -l .\Hook.js
frida -U 'Frida 0x2' -l .\Hook.js
解决方法1
能够成功hook的时候,就可以推断出,是由于我们启动main函数使用的是setImmediate(main),是立即启动可能会导致脚本注入的速度比程序启动的速度快。因此我们可以改用setTimeout(main,1000)
,也就是延迟1秒钟启动程序。Java.perform(function (){ var <class_reference> = Java.use("<package_name>.<class>");
<class_reference>.<variable>.value = <value>;})
function hook(){
var a = Java.use("com.ad2001.frida0x3.Checker");
a.code.value = 512;
}function main(){
Java.perform(function (){
hook();
})
}
setImmediate(main);
Check ch = new Check();
String flag = ch.get_flag(1337);
Java.perform(function() { var <class_reference> = Java.use("<package_name>.<class>");
var <class_instance> = <class_reference>.$new(); // Class Object
<class_instance>.<method>(); // 调用方法})
function hook(){
console.log("Hook Success!");
var Check = Java.use("com.ad2001.frida0x4.Check");
var Check_obj = Check.$new();
var String = Check_obj.get_flag(1337);
console.log(String);
}function main(){
Java.perform(function (){
hook();})
}setImmediate(main);
function hook(){
var MainActivity = Java.use("com.ad2001.frida0x5");
var MainActivity_obj = MainActivity.$new();
}
MainActivity
或任何Android组件可能会很棘手,因为Android的生命周期和线程规则。Android组件,如Activity
子类,依赖于应用程序上下文进行正确运行。在Frida中,您可能缺少必要的上下文。Android UI组件通常需要具有关联Looper
的特定线程。如果涉及UI任务,请确保在具有活动Looper
的主线程上执行。活动是较大的Android应用程序生命周期的一部分。创建MainActivity
的实例可能需要应用处于特定状态,并且通过Frida管理整个生命周期可能并不直接。总之,为MainActivity
创建实例并不是一个好主意。MainActivity
的一个实例(或AndroidManifest.xml文件中指定的启动器活动)。创建MainActivity
实例是Android应用程序生命周期的一部分。因此,我们可以使用frida获取MainActivity
的实例,然后调用flag()
方法来获取我们的标志。Java.performNow
:用于在Java运行时环境中执行代码的函数。Java.choose
:在运行时枚举指定Java类(作为第一个参数提供)的实例。Java.performNow(function() {
Java.choose('<包名>.<类名>', {
onMatch: function(instance) {
// 待办事项
},
onComplete: function() {}
});
});
onMatch
回调函数在Java.choose
操作期间找到指定类的每个实例时执行。onMatch
回调中定义自定义操作,以在每个实例上执行。function(instance) {}
,instance
参数表示目标类的每个匹配实例。您可以使用任何其他名称。onComplete
回调在Java.choose
操作完成后执行操作或清理任务。此块是可选的,如果您在搜索完成后不需要执行任何特定操作,则可以选择将其留空。Java.choose
API,让我们开始编写我们的frida脚本。com.ad2001.frida0x5
MainActivity
flag
Java.performNow(function() {
Java.choose('com.ad2001.frida0x5.MainActivity', {
onMatch: function(instance) {
// 待办事项
},
onComplete: function() {}
});
});
MainActivity
实例时包含一个console.log
语句以打印一条消息。由于在枚举完成后我们没有任何特定的操作要执行,我们可以将onComplete
块留空。Java.performNow(function() {
Java.choose('com.ad2001.frida0x5.MainActivity', {
onMatch: function(instance) {
console.log("找到实例");
},
onComplete: function() {}
});
});
function hook(){
Java.choose('com.ad2001.frida0x5.MainActivity',{
onMatch:function (MainActivity){
MainActivity.flag(1337);
console.log("Hook Success!");
},onComplete:function (){}
})
}function main(){
Java.perform(function (){
hook();
})
}setImmediate(main);
get_flag()
方法,在应用程序中没有被调用。如果调用此方法,它将使用AES
解密标志,并将标志设置在Textview中。如果我们检查get_flag
方法,它只接受一个参数,这个参数是Checker
类的一个实例。参数被命名为A
,其类型是Checker
。public void get_flag(Checker A) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
// 方法体
}
A.num1
是否等于1234
,以及A.num2
是否等于4321
。如果条件成立,该方法将继续使用AES解密加密字符串,并将解密后的结果设置在TextView中。因此,让我们检查一下Checker类。num1
应该等于1234
,num2
应该等于4321
,以满足if
条件执行解密并设置标志的代码块。请记住,这个类也没有实例。get_flag
方法的参数是Checker
类的一个对象。我将总结解决这个问题的步骤如下:Checker
类的实例。num1
设置为1234,num2
设置为4321。MainActivity
的实例。get_flag
方法。Checker
类的实例。var checker = Java.use("com.ad2001.frida0x6.Checker");
var checker_obj = checker.$new(); // 类对象
num1
和num2
的值。checker_obj.num1.value = 1234;
checker_obj.num2.value = 4321;
MainActivity
的实例。我们可以使用Java.performNow
和Java.choose
API。我们在之前的挑战中已经做过了。Java.performNow(function() {
Java.choose('com.ad2001.frida0x6.MainActivity', {
onMatch: function(instance) {
console.log("找到实例");},
onComplete: function() {}
});
})
Checker
类的实例。Java.performNow(function() {
Java.choose('com.ad2001.frida0x6.MainActivity', {
onMatch: function(instance) {
console.log("找到实例");var checker = Java.use("com.ad2001.frida0x6.Checker");
var checker_obj = checker.$new(); // 类对象
checker_obj.num1.value = 1234;
checker_obj.num2.value = 4321;},
onComplete: function() {}
});
});
Checker
类的实例来调用get_flag
方法。Java.performNow(function() {
Java.choose('com.ad2001.frida0x6.MainActivity', {
onMatch: function(instance) {
console.log("找到实例");var checker = Java.use("com.ad2001.frida0x6.Checker");
var checker_obj = checker.$new(); // 类对象
checker_obj.num1.value = 1234; // num1
checker_obj.num2.value = 4321; // num2
instance.get_flag(checker_obj); // 调用get_flag方法},
onComplete: function() {}
});
});
PS C:\Users\ajind> frida -U -f com.ad2001.frida0x6
Java.perform(function() {
var <class_reference> = Java.use("<package_name>.<class>");
<class_reference>.$init.implementation = function(<args>){/*
*/
}
});
$init
关键字。function hook(){
var Checker = Java.use("com.ad2001.frida0x7.Checker");
Checker.$init.implementation = function (a,b){
console.log("Origin num",a,b);
this.$init(600,600);
console.log("Hook Success");
}
}function main(){
Java.perform(function (){
hook();
})
}
setImmediate(main);
看雪ID:Shangwendada
https://bbs.kanxue.com/user-home-979679.htm
# 往期推荐
2、Glibc-2.35下对tls_dtor_list的利用详解
球分享
球点赞
球在看
点击阅读原文查看更多