一..js脚本hook一个方法
1.工具:1.jadx----用于进行静态分析,也可以进行动态调试(JEB似乎更好)。
雷神模拟器 9(Android 9)----原本自带的adb.exe好像有问题,建议另行自找。(需要root)
Frida Frida server
例题:Frida 0x1
2.拿到附件,一个.apk文件,我们可以先用MT管理器看看APP入口
找到了之后,就可以用jadx打开,先静态分析看看逻辑
package com.ad2001.frida0x1;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Random;
/* loaded from: classes.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 bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
final EditText editText = (EditText) findViewById(R.id.editTextTextPassword);
this.t1 = (TextView) findViewById(R.id.textview1);
final int i = get_random();
((Button) findViewById(R.id.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();
}
}
});
}
int get_random() {
return new Random().nextInt(100);
}
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.t1.setText(sb.toString());
return;
}
Toast.makeText(getApplicationContext(), "Try again", 1).show();
}
}
可以看到,APP中位于com.ad2001.frida0x1包MainActivity类的onCreate方法在程序初始化时会调用get_random()方法生成一个介于0到100之间的随机数并赋值给变量i。同时,监听button的点击,当按下按钮后,程序首先判断输入的内容是不是数字,如果是数字就将其从string转化为int,并将该整数与生成的随机数一起传递给check方法。接下来,继续分析check**方法实现了哪些功能。
check方法被传入了两个参数,一个是我们在APP页面输入后提交的的数字i2,一个是程序启动时调用get_random方法生成的随机数i。该方法会检查表达式(i * 2) + 4 == i2)是否成立,如果成立则将正确的flag输出到绑定的textView控件上,反之则会提示“Try again”。
当然,这个题其实是可以直接逆的,写脚本爆破,但是为了学习frida的用法,我们使用frida来hook出每次程序运行时所生成的随机数i是多少,然后我们再将正确答案输入,便可得到正确flag。
PS:因为使用frida进行hook,也是属于动态调.apk文件,所以我们得在aok文件的AndroidMainfestxml清单里查看application中是否有可调式的属性,如果没有得自行加入。
然后就可以开始使用frida进行hook了。
3.具体操作
首先先查看adb的连接是否有问题,在终端输入adb devices
然后输入adb shell进入手机,接着输入su进入超级管理,再输入cd /data/local/tmp进入相应目录(网上大部分都是将frida server存入此文件夹。(注:frida和frida server版本需一致,同时frida server的类型需与虚拟机(手机)的架构一直,可用指令adb shell getprop ro.product.cpu.abi查看具体信息)。进入目录之后,可以看看文件夹里是否有frida server这个文件,
然后修改文件权限chmod 755 frida-server-16.2\ .1-android-x86_64,紧接着就可以运行了。然后,我另外再打开一个终端窗口,分别输入
adb forward tcp:27042 tcp:27042**adb forward tcp:27043 tcp:27043** 进行端口转发监听
最后输入frida-ps -U或者frida-ps -R看看能否成功输出进程列表,检查frida能否正常使用,
我们还需编写脚本
// 使用Java.perform函数创建一个特殊的Java上下文,用于执行挂钩和Java类操作
Java.perform(function() {
// 获取应用程序中com.ad2001.frida0x1.MainActivity类的引用
var MainActivity = Java.use("com.ad2001.frida0x1.MainActivity");
// 重写MainActivity类的get_random方法的实现
MainActivity.get_random.implementation = function() {
// 当get_random方法被调用时,输出一条日志信息
console.log("MainActivity.get_random is called");
// 调用原始的get_random方法并获取其返回值
var ret_val = this.get_random();
// 输出get_random方法的返回结果
console.log("MainActivity.get_random result is " + ret_val);
// 返回原始方法的返回值
return ret_val;
};
});
保存好,然后我们使用frida -U -f com.ad2001.frida0x1 -l test.js(test.js是脚本的绝对路径)
再在jadx中点击运行,就可以看到输出了此次app运行时生成的随机数是多少,再计算 (74 * 2) + 4 = 152,输入,就可得到flag
这就是利用frida hook一个方法的完整过程。
总结:Frida Hook方法的脚本模板:
Java.perform(function() {
var <class_reference> = Java.use(“<package_name>.<class>”);
<class_reference>.<method_to_hook>.implementation = function(<args>) {
/*
我们自己的方法实现
*/
}
}
二..js脚本hook一个静态方法
1.工具:还是那几样东西。
2.例题:Frida 0x2
3.分析:
拿到附件apk文件,先安装到雷电9里运行一下,
没有输入,就是很直白,hook。那么接下来我们用jadx反编译为java看看静态逻辑。
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 {
static 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);
t1 = (TextView) findViewById(R.id.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);
t1.setText(decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
逻辑很简单,有一个静态方法get_flag,比较一个参数a是不是4919,是的话就会解密输出flag,一个AES加密,但是,这里没有任何方法会调用这个静态方法,那么我们就可以利用frida编写脚本调用这个静态方法,并且将参数a的值改为正确的,那么就可以输出flag。使用frida的前期工作和例题一一样,但是因为这是静态方法,如果使用指令frida -U -f 包名 -l 脚本会出现hook不上的情况,所以我们可以先运行程序,然后使用指令frida -U 'Frida 0x2' -l .\Hook.js,就可以输出flag了。
脚本:
Java.perform(function() {
// 声明了一个变量a来表示目标Android应用程序中的Java类。
// Java.use函数指定要使用com.ad2001.frida0x2包中的MainActivity类。
var a = Java.use("com.ad2001.frida0x2.MainActivity");
// 调用get_flag方法并传入参数4919
a.get_flag(4919);
});
运行脚本:
得到flag
总结:Frida 调用静态方法的脚本模板:
Java.perform(function (){
var <class_reference> = Java.use(“<package_name>.<class>”);
a.function(val); // 需要调用的方法名称
})
三. .js脚本更改变量的值
1.拿到附件apk文件,依旧先安装到雷电模拟器上运行一下看看回显,
框框一顿点没什么用,那么就开始用jadx静态分析一波吧,看看函数逻辑,
package com.ad2001.frida0x3;
import android.os.Bundle;
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;
/* 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);
Button btn = (Button) findViewById(R.id.button);
this.t1 = (TextView) findViewById(R.id.textView);
btn.setOnClickListener(new View.OnClickListener() { // from class: com.ad2001.frida0x3.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
if (Checker.code == 512) {
byte[] bArr = new byte[0];
Toast.makeText(MainActivity.this.getApplicationContext(), "YOU WON!!!", 1).show();
byte[] KeyData = "glass123".getBytes();
SecretKeySpec KS = new SecretKeySpec(KeyData, "Blowfish");
byte[] ecryptedtexttobytes = Base64.getDecoder().decode("MKxsZsY9Usw3ozXKKzTF0ymIaC8rs0AY74GnaKqkUrk=");
try {
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(2, KS);
byte[] decrypted = cipher.doFinal(ecryptedtexttobytes);
String decryptedString = new String(decrypted, Charset.forName("UTF-8"));
MainActivity.this.t1.setText(decryptedString);
return;
} 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);
}
}
Toast.makeText(MainActivity.this.getApplicationContext(), "TRY AGAIN", 1).show();
}
});
}
}
逻辑还是很清晰的,就是com.ad2001.frida0x3包的Checker类中检查参数code的值是否是512,是的话就会输出flag,再来看看Checker类的逻辑,
有个increase方法,主函数中有个检测按钮的点击次数,点一次就加2,那么这个题要解出来的话,要么点256次,要么直接解密,但似乎加密的逻辑不是很清晰,再一个方法就是用frida修改code的值,直接改为512,这样一来就可以直接输出flag了。
脚本:
/***
* Java.perform 是Frida中的一个函数,用于为脚本创建特殊上下文,
* 进入此上下文后,可以执行挂钩方法或访问Java类等操作来控制或观察应用程序的行为。
**/
Java.perform(function () {
// 声明了一个变量a来表示目标Android应用程序中的Java类。
// Java.use函数指定要使用com.ad2001.frida0x3包中的Checker类。
var a = Java.use("com.ad2001.frida0x3.Checker");
// 使用变量a将所选类的code变量值修改为512
a.code.value = 512;
});
使用frida还是和前面的步骤一样,然后我们输入指令frida -U -f com.ad2001.frida0x3 -l hook.js,然后再使用jadx进行动态调试,为的是控制程序运行,点击两次运行,会出现最开始的页面,最后点击Click me,就会出现flag
得到flag
总结:Frida 更改变量值的脚本模板
Java.perform(function (){
var <class_reference> = Java.use(“<package_name>.<class>”);
<class_reference>.<variable>.value = <value>; //需要修改的变量
})
四. .js脚本创建类实例
工具不变,拿到附件apk文件,依旧先安装到雷神模拟器上安装运行一下。没有任何输入框或者按钮,只有一个打招呼的。那么就丢到jadx里分析看看具体函数逻辑
这是MainActivity类,没啥有用的信息。再看看Check类
判断参数a的值是否是1337,是的话就会把解码的flag输出,但是,这个类以及类中的方法并没有在程序的任何地方被使用,因此我们仅仅打开程序无法获取到flag的值,同时,这里的get_flag方法并不是静态方法。所以我们就可以利用frida创建一个实例,再用这个实例来调用这个方法,进而输出flag。
那么我们就可以开始操作了,前期开始工作是一样的,然后我们直接使用脚本,
Java.perform(function() {
var check = Java.use("com.ad2001.frida0x4.Check");
// 声明了一个变量 check 来表示目标 Android 应用程序中的 Java 类。
// Java.use 函数指定要使用 com.ad2001.frida0x4 包的 Check 类。
var check_obj = check.$new();
// $new() 是 Frida 中的方法,用于实例化特定类的对象;
// 使用 $new() 方法创建 Check 类的实例 check_obj。
var res = check_obj.get_flag(1337);
// 使用 check_obj 实例调用 get_flag,并传入参数 1337。
// 创建一个变量 res 来保存 get_flag 方法的返回值。
console.log("FLAG:" + res);
// 将保存的 flag 输出到脚本运行日志中。
});
拿到flag
总结:Frida 创建类实例的脚本模板
Java.perform(function() {
var <class_reference> = Java.use("<package_name>.<class>");
var <class_instance> = <class_reference>.$new(); // 创建类的实例
<class_instance>.<method>(); // 调用该实例中的方法
})
五.最后
Frida相关例题链接:(https://github.com/DERE-ad2001/Frida-Labs)