发布于 

Frida 初步使用和逆向

FRIDA

Frida是一个动态代码插桩工具,主要用于执行动态分析。它允许你在程序运行时注入自己的代码监控执行流程。使用该工具可以实现对应用程序的Hook监控修改函数调用消息传递

  • 适用平台:支持Windows、macOS、Linux、iOS和Android等多个平台。
  • 使用场景:逆向工程、安全评估、开发与调试等。
  • 核心组件:包括一个核心库以及多个语言绑定(如PythonNode.js)。

案例

0x01 frida_example_1.0.apk

image.png

使用frida hook
frida-ps -U -a 查看包名
frida -U -f 包名 -l hook.js

hook.js源码如下:

// 定义主要用于hook Java层代码的函数
function hook_java() {
    // 使用Java.perform执行hook
    Java.perform(function () {
        // 获取LoginActivity,并打印以便调试
        var LoginActivity = Java.use("com.example.androiddemo.Activity.LoginActivity");
        console.log(LoginActivity);
        // 修改LoginActivity类中的a方法实现
        LoginActivity.a.overload('java.lang.String', 'java.lang.String').implementation = function (str, str2) {
            // 在内部,首先调用原始方法并保存结果
            var result = this.a(str, str2);
            // 打印调用参数和结果
            console.log("LoginActivity.a:", str, str2, result);
            // 返回原始方法的结果
            return result;
        };

        // 获取FridaActivity1类引用并打印
        var FridaActivity1 = Java.use("com.example.androiddemo.Activity.FridaActivity1");
        console.log(FridaActivity1);

        // 修改FridaActivity1类a方法的实现,这里没有调用原始函数,而是直接返回固定字符串
        FridaActivity1.a.implementation = function (barr) {
            // 打印方法被调用的信息
            console.log("FridaActivity1.a");
            // 返回固定的字符串
            return "R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=";
        };

        // 表明hook_java函数已经被调用
        console.log("hook_java");
    });
}

// 定义函数,用于主动调用FridaActivity2中的方法
function call_FridaActivity2() {
    // 使用Java.perform执行主动调用
    Java.perform(function () {
        // 获取FridaActivity2类引用
        var FridaActivity2 = Java.use("com.example.androiddemo.Activity.FridaActivity2");
        // 主动调用FridaActivity2中的setStatic_bool_var静态方法
        FridaActivity2.setStatic_bool_var();
 
        // 选择FridaActivity2实例,并修改实例变量
        Java.choose("com.example.androiddemo.Activity.FridaActivity2", {
            onMatch: function (instance) {
                // 修改实例变量bool_var值
                instance.setBool_var();
            },
            onComplete: function () {
                // 选择完成时的回调
            }
        });
    });
}

// 定义函数,用于主动调用FridaActivity3,并修改其成员变量
function call_FridaActivity3() {
    // 使用Java.perform执行主动调用
    Java.perform(function () {
        // 获取FridaActivity3类引用
        var FridaActivity3 = Java.use("com.example.androiddemo.Activity.FridaActivity3");
        // 修改FridaActivity3的静态成员变量static_bool_var的值
        FridaActivity3.static_bool_var.value = true;

        // 打印修改后的值以确认
        console.log(FridaActivity3.static_bool_var.value);

        // 选择FridaActivity3的实例,并修改它们的成员变量值
        Java.choose("com.example.androiddemo.Activity.FridaActivity3", {
            onMatch: function (instance) {
                // 修改非静态成员变量的值
                instance.bool_var.value = true;
                // 修改名称冲突的成员变量值
                instance._same_name_bool_var.value = true;
                // 打印出修改后的值
                console.log(instance.bool_var.value, instance._same_name_bool_var.value);
            },
            onComplete: function () {
                // 选择完成回调
            }
        });
    });
}

// 定义函数,用于hook FridaActivity4中的InnerClasses内部类的多个方法
function hook_InnerClasses() {
    Java.perform(function () {
        // 获取InnerClasses类引用,并打印
        var InnerClasses = Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses");
        console.log(InnerClasses);
        // 修改InnerClasses类中的check方法,始终返回true,共有6个check方法
        InnerClasses.check1.implementation = function () { return true; };
        InnerClasses.check2.implementation = function () { return true; };
        InnerClasses.check3.implementation = function () { return true; };
        InnerClasses.check4.implementation = function () { return true; };
        InnerClasses.check5.implementation = function () { return true; };
        InnerClasses.check6.implementation = function () { return true; };
    });
}

// 定义函数,用于hook InnerClasses类的所有方法,并返回true
function hook_mul_function() {
    Java.perform(function () {
        // 指定需要hook的类的名称
        var class_name = "com.example.androiddemo.Activity.FridaActivity4$InnerClasses";
        // 获取类引用
        var InnerClasses = Java.use(class_name);
        // 获取InnerClasses类声明的所有方法
        var all_methods = InnerClasses.class.getDeclaredMethods();
        // 遍历所有方法
        for (var i = 0; i < all_methods.length; i++) {
            // 获取单个方法的引用
            var method = (all_methods[i]);
            // 将方法转化为字符串
            var methodStr = method.toString();
            // 获取方法名
            var substring = methodStr.substr(methodStr.indexOf(class_name) + class_name.length + 1);
            var methodname = substring.substr(0, substring.indexOf("("));
            // 打印方法名称
            console.log(methodname);

            // 修改对应方法的实现,始终返回true
            InnerClasses[methodname].implementation = function () {
                console.log("hook_mul_function:", this);
                return true;
            }
        }
    });
}

// 定义函数用于hook动态加载的dex文件内的类和方法
function hook_dyn_dex() {
    Java.perform(function () {
        // 获取FridaActivity5类引用
        var FridaActivity5 = Java.use("com.example.androiddemo.Activity.FridaActivity5");
        // 选择FridaActivity5的实例,并打印动态dex类的名称
        Java.choose("com.example.androiddemo.Activity.FridaActivity5", {
            onMatch: function (instance) {
                // 打印动态加载的dex的类名
                console.log(instance.getDynamicDexCheck().$className);
            }, onComplete: function () {
                // 选择完成回调
            }
        });

        // 枚举类加载器,寻找包含DynamicCheck类的类加载器
        Java.enumerateClassLoaders({
            onMatch: function (loader) {
                try {
                    // 如果找到了DynamicCheck类,则切换loader为当前类加载器
                    if (loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")) {
                        console.log(loader);
                        Java.classFactory.loader = loader;
                    }
                } catch (error) {
                    // 捕获异常
                }
            }, onComplete: function () {
                // 枚举完成回调
            }
        });

        // 获取动态加载的DynamicCheck类
        var DynamicCheck = Java.use("com.example.androiddemo.Dynamic.DynamicCheck");
        // 打印以便调试
        console.log(DynamicCheck);
        // 修改DynamicCheck类中的check方法实现,始终返回true
        DynamicCheck.check.implementation = function () {
            console.log("DynamicCheck.check");
            return true;
        }
    });
}

// 定义函数用于hook FridaActivity6内的Frida6Class0、Frida6Class1和Frida6Class2类的check方法
function hook_FridaActivity6() {
    Java.perform(function () {
        // 修改Frida6Class0类的check方法,始终返回true
        var Frida6Class0 = Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class0");
        Frida6Class0.check.implementation = function () {
            return true;
        };
        // 修改Frida6Class1类的check方法,始终返回true
        var Frida6Class1 = Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class1");
        Frida6Class1.check.implementation = function () {
            return true;
        };
        // 修改Frida6Class2类的check方法,始终返回true
        var Frida6Class2 = Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class2");
        Frida6Class2.check.implementation = function () {
            return true;
        };
    });
}

// 定义函数用于枚举并hook FridaActivity6包下所有类的check方法
function hook_mul_class() {
    Java.perform(function () {
        // 枚举已加载的类
        Java.enumerateLoadedClasses({
            onMatch: function (name, handle) {
                // 如果是FridaActivity6包下的类,则hook它的check方法
                if (name.indexOf("com.example.androiddemo.Activity.Frida6") >= 0) {
                    console.log(name);
                    var fridaclass6 = Java.use(name);
                    fridaclass6.check.implementation = function () {
                        console.log("frida 6 check:", this);
                        return true;
                    };
                }
            }, onComplete: function () {
                // 枚举完成回调
            }
        })
    });
}

// 定义主函数,开始执行hook_java
function main() {
    hook_java();
}

// 使用setImmediate,保证main函数在Frida环境准备完毕后执行
setImmediate(main);

step

  • 0x01 - 反编译分析静态代码,编写hook.js脚本

    • 使用适当的反编译工具(如apktooljadxdex2jar等),对目标APK进行反编译,从而获取到代码的结构和逻辑。
    • 通过阅读代码了解程序的工作流程、准确定位需要hook的方法。
    • 结合Frida的API编写脚本hook.js,实现对特定方法的拦截和修改。
  • 0x02 - 提升编码能力,解决遇到的挑战

    • 深化对JavaScript的理解,学习更多关于Frida脚本编写的技巧。
    • 学会阅读和理解Frida的错误信息,提升debug能力。
    • 持续编码,对遇到的困难时刻保持好奇心,逐个击破在hook过程中遇到的关卡。
  • 0x03 - 耐心与细节关注,确保功能实现的稳定性

    • 实现功能时保持耐心,不要急于求成,理解每一行hook代码的含义及对应的影响。
    • 注重脚本的稳健性和异常处理,确保在不同条件下脚本都能正常工作。
    • 重视对hook代码测试的全面性,包括边界情况和潜在的性能问题。
  • 0x04 - 学习移动安全和系统内置安全机制

    • 深入了解AndroidiOS操作系统的安全特性,例如沙箱、权限模型、签名机制等。
    • 学习如何绕过或者利用这些安全机制来实现更高级的hook技术
  • 0x05 - 熟悉Frida的高级用法

    • 除了基本的hook,还可以学习Frida用于内存操作模块枚举动态分析等高级功能。
    • 了解如何结合其他工具,例如ObjectionRadare2,以丰富动态分析的过程。
  • 0x06 - 融会贯通,实践复杂案例

    • 将学过的理论知识应用到更复杂的案例分析之中,比如加固过的或者有反调试机制的APP
    • 运用创造性思维,開发自定义的Frida模块插件,以解决特定分析问题。

QA

  1. adb 如何把字符串复制到模拟器中?

    通过 adb (Android Debug Bridge) 将字符串复制到安卓模拟器中,可以使用以下方式:
    使用 adb shell input text 命令:
    sh
    adb shell input text '你的字符串'
    请确保你的字符串中没有空格。如果需要输入空格,可以使用 %s 来代表空格。例如,要输入 Hello World,使用以下命令:
    sh
    adb shell input text 'Hello%sWorld'
    借助剪贴板 (API Level 11 及以上)
    sh
    adb shell am broadcast -a clipper.set -e text '你的字符串'
    注意:上述方法需要你的设备上有一个名为 clipper 的应用,该应用可以响应 clipper.set 的广播并更新剪贴板内容。如果你没有安装类似功能的应用,你需要自己开发或安装一个。
    以上是将字符串发送到模拟器的两种方式,具体使用哪一种取决于你具体的需求以及安卓模拟器上的环境配置。如果你在执行这些命令的时候遇到任何问题,确保你的 adb 环境配置正确,并且你的设备已经连接到了 adb

🔗链接

52 FRIDA
看需 FRIDA-hook-java