[安全] Frida Labs 0x1

Frida Labs 自带的 Solution 写得很好!!

分析过程

先使用工具 ( jadx, jeb … ) 查看反编译之后的代码, 代码主要逻辑在 MainActicity 中

/* JADX INFO: loaded from: classes.dex */
public class MainActivity extends AppCompatActivity {
 
    /* JADX INFO: renamed from: t1 */
    TextView f103t1;
 
    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(C0536R.layout.activity_main);
        final EditText editText = (EditText) findViewById(C0536R.id.editTextTextPassword);
        Button button = (Button) findViewById(C0536R.id.button);
        this.f103t1 = (TextView) findViewById(C0536R.id.textview1);
        final int i = get_random();
        button.setOnClickListener(new View.OnClickListener() { // from class: com.ad2001.frida0x1.MainActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View view) {
                String string = editText.getText().toString();
                if (TextUtils.isDigitsOnly(string)) {
                    MainActivity.this.check(i, Integer.parseInt(string));
                } else {
                    Toast.makeText(MainActivity.this.getApplicationContext(), "Enter a valid number !!", 1).show();
                }
            }
        });
    }
 
    int get_random() {
        return new Random().nextInt(100);
    }
 
    /* JADX WARN: Removed duplicated region for block: B:13:0x0031 A[PHI: r0
  0x0031: PHI (r0v7 char) = (r0v5 char), (r0v11 char) binds: [B:19:0x0040, B:12:0x002f] A[DONT_GENERATE, DONT_INLINE]] */
    /*
        Code decompiled incorrectly, please refer to instructions dump.
    */
    void check(int rnd, int input) {
        if ((rnd * 2) + 4 == input) {
            Toast.makeText(getApplicationContext(), "Yey you guessed it right", 1).show();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 20; i++) {
                char cCharAt = "AMDYV{WVWT_CJJF_0s1}".charAt(i);
                if (cCharAt >= 'a' && cCharAt <= 'z') {
                    cCharAt = (char) (cCharAt - 21);
                    if (cCharAt < 'a') {
                    }
                } else if (cCharAt >= 'A' && cCharAt <= 'Z' && (cCharAt = (char) (cCharAt - 21)) < 'A') {
                    cCharAt = (char) (cCharAt + 26);
                }
                sb.append(cCharAt);
            }
            this.f103t1.setText(sb.toString());
            return;
        }
        Toast.makeText(getApplicationContext(), "Try again", 1).show();
    }
}

可见程序的逻辑是将 输入的数字 和 预先生成的随机数 进行某种比较 ((rnd * 2) + 4 == input), 通过之后则解密 flag
由于是 Frida 的学习, 不展示静态解法

Frida 是一款 Hook 工具, 可以在程序运行过程中修改其中的某些内容
这里想要 check 中的解密成功被运行, 有两种 hook 思路

  1. hook get_random 函数, 打印其返回值或者更改其返回值
  2. hook check 函数, 使其一定通过判断

Frida 前期准备

你需要模拟器或者真机, 这里试过 Android Studio 的模拟器 和 MuMu模拟器 都可以用
Android Studio 模拟器的性能不太好, 建议选别的
模拟器记得开 root

安装 Frida 客户端

pip install frida frida-tools

Frida 的仓库中下载对应版本的 Frida server

使用 frida --version 来查看 Frida 的版本
使用 adb shell getprop ro.product.cpu.abi 来查看运行平台的版本 (Frida 也可以 hook Windows 程序之类的, 但是 Frida labs 都是安卓题, 所以这里查看安卓平台的版本)

下载的服务器务必与 服务器运行的平台版本客户端Frida版本 统一

接下来我们运行 frida-server, 下方以你的服务器的文件名为准

  1. 将 frida-server 推送到模拟器上
    adb push frida-server /data/local/tmp/
    
  2. 进入 adb shell
    adb shell
    
  3. 设置运行权限
    chmod +x /data/local/tmp/frida-server
    
  4. 运行 frida-server
    /data/local/tmp/frida-server &
    

注: 通常在这里需要新开一个终端来执行剩下的操作

如果你想获取设备上安装的软件包列表,可以使用以下命令:

frida-ps -Uai
  • frida-ps: 显示 Android 设备上正在运行的进程信息。
  • -U: 此选项用于列出 USB 连接设备(物理设备或模拟器)上的进程。
  • -a: 此选项用于列出所有进程,而不仅仅是当前用户拥有的进程。
  • -i: 此选项用于包含每个进程的详细信息,例如进程 ID (PID) 和进程名称。

你也可以用该命令确定已经能正确连接上 frida-server

Hook 一个 Java 层方法

Java.perform(() => {
    const <class_reference> = Java.use("<package_name>.<class>");
    <class_reference>.<method_to_hook>.implementation = 
    function(<args>) {
        /*
            我们自己对该方法的实现
        */
    };
});
  • Java.perform 是 Frida 中的一个函数,用于为你的脚本创建一个特殊的上下文,以便与 Android 应用程序中的 Java 代码进行交互。它就像打开了一扇门,可以访问和操作 App 内部运行的 Java 代码。一旦进入这个上下文,你就可以执行诸如 Hook 方法或访问 Java 类等操作,以控制或观察 App 的行为。

  • const <class_reference> = Java.use("<package_name>.<class>");

    在这里,你声明一个变量 <class_reference> 来表示目标 Android 应用程序中的一个 Java 类。你使用 Java.use 函数指定要使用的类,该函数将类名作为参数。<package_name> 代表 Android 应用程序的包名,<class> 代表你想要交互的类。

  • <class_reference>.<method_to_hook>.implementation = function(<args>) {}

    在选定的类中,你可以通过使用 <class_reference>.<method_to_hook> 符号来访问你想要 Hook 的方法。在这里你可以定义当 Hook 的方法被调用时要执行的逻辑。<args> 代表传递给函数的参数。

    当处理带有参数的方法 Hook 时,重要的是使用 overload(arg_type) 关键字指定预期的参数类型。

不能使用箭头函数, 这样会没办法获取到 this
具体实现可以参考示例代码, 代入之后就知道是什么意思了

示例代码

import Java from "frida-java-bridge";
 
if (Java.available) {
    Java.perform(() => {
        const MainActivity = Java.use("com.ad2001.frida0x1.MainActivity");
        // MainActivity.get_random.implementation = 
        // function () {
        //     let v = this.get_random();
        //     console.log(`the random number is: ${v}`);
        //     return v;
        //     // return 0;
        // };
 
        MainActivity.check.overload("int", "int").implementation = 
        function (rnd: number, input: number) {
            console.log(`the input number should be: ${rnd * 2 + 4}`);
            this.check(rnd, rnd * 2 + 4);
        };
    });
} else {
    console.log("No Java VM in this process");
}

比较舒服的 Frida 脚本编写流程推荐

下载 frida-agent-example 项目, 根据其中的 README.md 文件设置开发环境, 这样你的 Frida 脚本可以使用现代化的 TypeScript 来编写, 同时有了代码补全和提示

  1. git clone https://github.com/oleavr/frida-agent-example.git
  2. cd frida-agent-example/
  3. 编辑 ./agent/index.ts 这个文件, 这是你的 Frida 脚本所在的文件
  4. npm install
    这一步可能会需要 npm audit fix, 跟着你的 npm 的提示做就好
  5. 可选, 使用 npm run watch 进行及时编译, 能让你在更改脚本之后立即编译, 不需要过多执行 npm install
  6. frida -U -f com.example.android -l _agent.js
    _agent.js 是你编写的 ts 代码经过编译后的样子, 可以直接作为 Frida 脚本运行

注: 通常在 npm run watch 之后需要重新开一个终端来执行其它操作