APP动态分析系列 - Frida的基础用法(下)

admin 2024年4月12日23:42:28评论8 views字数 13004阅读43分20秒阅读模式
APP动态分析系列 - Frida的基础用法(下)

APP动态分析系列 - Frida的基础用法(下)

APP动态分析系列 - Frida的基础用法(下)

大狗涉网线索分析平台是无糖信息旗下一款针对“新型电信网络诈骗犯罪”专业化、智能化、跨境化等特征,提供涉诈网站识别及监控、预警数据分析、案件研判一体化服务。为公安部门在预警防范、侦查破案提供多方位、深层次、多类型的情报信息及数据支持,助力公安机关形成反网络犯罪的打击能力。

《APP动态分析系列-Frida的基础用(上)》文章中我们讲到,在分析恶意APP样本时需要Hook一个方法、调用静态方法、更改变量的值的三种单靠静态和逆向分析无法解决问题,需要引入基于Frida的动态插桩技术来实现的分析场景。本篇文章将继续探讨类似的以下四个场景:

  • 创建类实例:例如,在分析APP加密通信模块时,对于非静态的加密方法,我们需要创建该加密算法类的实例来调用这个加密方法,以深入了解加密机制。

  • 在现有实例上调用方法:例如,在绕过APP权限验证时,需要获取权限验证类的实例,并调用验证方法来模拟不同的权限验证场景。

  • 使用对象参数调用方法:例如,在分析订单处理功能时,需要使用不同的订单对象参数调用订单处理类的方法,来尝试绕过订单验证步骤。

  • Hook构造函数:例如,在分析APP隐私数据收集合规性时,需要Hook应用程序的构造函数来观察初始化过程中的数据收集操作,以识别APP是否违规收集敏感隐私信息。

使用Frida实现这些功能,我们需要安装前置的Python、Frida、安卓模拟器及其依赖环境(可参考官方文档:https://frida.re/docs/home/),并掌握通过Frida实例化类、调用方法、创建对象参数、Hook构造函数等基础知识。【大狗涉网线索分析平台】的【云真机操作台】引入的Frida 脚本功能,可以省去复杂的安装过程,实现一键运行 Frida 脚本,让大家可以在无糖浏览器中随时随地对恶意 APP 进行动态调试,大大提高工作效率和便利性。

接下来,我们将对以上四个分析场景中涉及到的Frida基础用法进行讲解和演示。

基础用法示例

本篇文章将继续以Frida-lab中的题目为例,为大家介绍云真机操作台中Frida脚本基础用法和背景知识的下半篇。Frida-lab是Github上的一个开源项目,是专为学习Frida for Android而设计的一系列挑战,包含多个CTF风格的APP样本,旨在帮助初学者掌握Frida及其常用的API基础知识。

项目链接:https://github.com/DERE-ad2001/Frida-Labs

使用工具
  • Jadx反编译工具:jadx是一个功能强大、使用简单的Android反编译利器,适合开发者在逆向工程和代码分析时使用。(官方网站:https://github.com/skylot/jadx)

  • JavaScript脚本:我们将使用JavaScript API来完成Frida脚本的编写。(API文档地址:https://frida.re/docs/javascript-api/)(值得注意的是,Frida也支持Python。)

  • Frida框架:本次我们使用【大狗涉网线索分析平台】-【云真机操作台】中引入的Frida脚本功能。

前期知识储备
  • 了解Frida框架的基本原理和架构。

  • 了解Hook技术拦截和修改函数或方法的基础知识。

  • 掌握使用jadx进行逆向工程的基础知识。

  • 具备理解Java代码的能力。

  • 具备编写小型JavaScript代码片段的能力。

01
用法4:创建类实例

APP下载地址:https://github.com/DERE-ad2001/Frida-Labs/tree/main/Frida%200x4

01
了解APP样本

在下载Challenge 0x4.apk文件并上传至大狗云真机操作平台后,可以看到APP程序为一个静态页面,只有一段打招呼的提示词,没有任何输入框和按钮(图1)。我们将APK放入jadx进行静态分析。

APP动态分析系列 - Frida的基础用法(下)

图1. 应用程序Challenge 0x4界面

02
静态分析

使用jadx反编译APP,查看反编译源代码,找到程序入口。分析代码可以发现,应用程序MainActivity类中只进行了简单的初始化界面操作,没有更多有用信息(图2)。我们再看看com.ad2001.frida0x4包中定义的另一个类Check,Check类中定义了一个get_flag方法,此方法会检查传入的参数是否等于1337,如果是则将flag解码并作为函数的返回值。但是这个类以及类中的方法并没有在程序的任何地方被使用,因此我们仅仅打开程序无法获取到flag的值。

APP动态分析系列 - Frida的基础用法(下)

图2. MainActivity类

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 "";
    }
}

分析完APP可以知道,我们要找的flag藏在Check类的get_flag方法中,想要得到这个flag我们需要调用get_flag方法并将传入的参数设置为1337。在《APP动态分析系列 - Frida的基础用法(上)》用法2中我们已经讲到如何调用一个静态方法,但是这里的get_flag方法并不是一个静态方法,要调用该方法,我们首先需要创建这个方法所在类的实例,再使用该实例进行调用。让我们来编写Frida脚本实现。

03
编写Frida脚本
我们需要使用Frida框架,编写一个JavaScript脚本,创建一个Check类的实例,然后再使用这个实例,调用它的get_flag方法,并将传入的参数设置为1337。

首先明确,我们需要实例化的Check类和需要调用的get_flag方法,位于程序的com.ad2001.frida0x4包中,创建Check类的实例,我们需要使用Frida中的$new()方法来实例化特定类的对象,然后使用创建的实例调用get_flag方法,并将传入参数设置为1337,其代码实现如下:(值得注意的是,这里我们需要获取get_flag方法返回的flag的值,因此还需要在脚本中创建一个变量来保存并输出到运行日志中。)

/***
Java.perform 是Frida中的一个函数,用于为脚本创建特殊上下文,进入此上下文后,可以执行挂钩方法或访问Java类等操作来控制或观察应用程序的行为。

**/


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输出到脚本运行日志中。

})

04
运行脚本

进入云真机操作台启动APP,在Frida脚本功能区点击【新增脚本】,命名为Challenge_0x4_hook,将编写好的代码复制进来,点击【加载脚本】。由于创建Check类实例并调用get_flag方法的操作,不需要在程序初始化时执行,因此运行脚本时不需要重启APP。脚本运行成功后,get_flag方法执行后返回的flag将被打印到运行日志中(图3)。

APP动态分析系列 - Frida的基础用法(下)

图3. 运行脚本获取flag

05
总结

Frida 创建类实例的脚本模板:

Java.perform(function() {

  var <class_reference> = Java.use("<package_name>.<class>");
  var <class_instance> = <class_reference>.$new(); // 创建类的实例
  <class_instance>.<method>(); // 调用该实例中的方法

})
02
用法5:在现有实例上调用方法

APP下载地址:https://github.com/DERE-ad2001/Frida-Labs/tree/main/Frida%200x5

01
了解APP样本

在下载Challenge 0x5.apk文件并上传至大狗云真机操作平台后,可以看到APP程序界面依然没有任何有效信息(图4)。我们将APK放入jadx进行静态分析。

APP动态分析系列 - Frida的基础用法(下)

图4.  应用程序Challenge 0x5界面

02
静态分析

使用jadx反编译APP,查看反编译源代码,找到程序入口。分析代码可以发现,应用程序MainActivity类中定义了一个未被调用的方法flag,在该方法内部,会对传入的code参数进行检查,如果code的值等于1337,就会则解密flag,设置给TxtView控件(图5)。

APP动态分析系列 - Frida的基础用法(下)

图5. MainActivity类

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();
            }
        }
    }

分析完APP我们发现,程序在MainActivity类中定义了一个flag方法,当该方法传入参数code的值等于1337时,会将正确的flag输出到TxtView控件上。但是该方法没有在程序的任何位置被调用,我们需要借助Frida框架来调用这个方法,以获取flag。跟上一用法相同的是我们需要通过flag方法所在的MainActivity类的实例来调用该方法,但在使用Frida脚本创建MainActivity类的实例会出现错误(图6)。

APP动态分析系列 - Frida的基础用法(下)

图6.创建MainActivity类实例

原因在于MainActivity是Android组件,由于Android的生命周期和线程规则,Android组件需要依赖于应用程序上下文运行,在Frida中,我们缺少必要的上下文。例如:Android UI组件通常需要具有关联Looper的特定线程,如果涉及UI任务,需要在具有活动Looper的主线程上执行。总之,创建MainActivity的实例可能需要应用处于特定状态,并通过Frida管理整个生命周期,并不建议大家这样做。

我们有一个更好的解决办法。由于创建MainActivity实例是Android应用程序生命周期的一部分,当Android应用程序启动时,系统会创建MainActivity的一个实例(或AndroidManifest.xml文件中指定的启动器活动)。因此,我们可以使用Frida获取MainActivity的实例,然后调用flag方法来获取我们的标志。

03
编写Frida脚本

我们需要使用Frida框架,编写一个JavaScript脚本,获取程序中MainActivity类的实例,然后调用flag方法来获取标志。

首先明确,我们需要获取的MainActivity类实例和需要调用的flag方法,位于程序的com.ad2001.frida0x5包中。这次我们需要用到Frida的Java.performNowJava.choose API(分别用于在Java运行环境中执行代码和在运行时枚举指定Java类的实例),获取应用程序创建的MainActivity类实例,然后调用该实例的flag方法,并传入参数1337的代码实现如下:

/*
Java.performNow是Frida中的一个函数,用于在Java运行时环境中执行代码。
*/


Java.performNow(function() {

  Java.choose('com.ad2001.frida0x5.MainActivity', {
    // Java.choose在运行时枚举作为第一个参数传入的Java类的实例。
    // 第二个参数需要传入包含onMatch和onComplete两个回调函数的选项对象。

      onMatch: function(instance
        // 处理匹配到的类实例的回调函数
        // instance参数表示MainActivity类的每个匹配实例

        console.log("Instance found");
        // 找到实例时打印一条提示信息。
        instance.flag(1337); 
        // 调用flag方法,并传入参数1337。
    },
    onCompletefunction() {}
    // 完成操作后的回调函数
  });

});

下面是关于这段代码的解释:

  • Java.performNow(...)用于在Java运行时环境中执行代码的函数。(Java.performNow和Java.perform的区别在于:Java.performNow是一个同步方法,会立即执行传入的函数,并等待函数执行完毕后才继续执行后续代码;Java.perform是一个异步方法,会将传入的函数放入Frida的队列中,等待下一个Java调用时再执行。)

  • Java.choose('com.ad2001.frida0x5.MainActivity', {...}):在运行时枚举com.ad2001.frida0x5包中MainActivity类的实例,当找到匹配的对象时,会调用onMatch回调函数,在这个回调函数中可以对找到的对象进行操作。当选择操作完成时,会调用onComplete回调函数。

  • onMatch: function(instance){...}:在 Java.choose找到匹配的对象时,用于对每个对象进行操作的回调函数,instance用于表示目标类的每个匹配实例。

  • console.log("Instance found"):在找到MainActivity的实例时,将"Instance found"打印到运行日志中。

  • instance.flag(1337):调用获取到的MainActivity实例的flag方法,并传入参数1337。

  • onComplete: function() {...}:在 Java.choose 完成操作时,用于执行清理操作或输出结束信息的回调函数。这里在搜索完成后不需要执行任何特定操作,我们将其留空。

04
运行脚本

进入云真机操作台启动APP,在Frida脚本功能区点击【新增脚本】,命名为Challenge_0x5_hook,将编写好的代码复制进来,点击【加载脚本】。虽然MainActivity实例是在程序启动时被创建,但是我们需要在实例创建成功(程序启动完成)之后才能使用Frida脚本获取实例,因此运行脚本时不需要重启APP。脚本运行成功后,成功调用flag方法并将解码后的flag输出到TxtView控件上(图7)。

APP动态分析系列 - Frida的基础用法(下)

图7. 运行脚本获取flag

05
总结

Frida 在现有实例上调用方法的脚本模板:

   Java.performNow(function() {
  Java.choose('<Package>.<class_Name>', {
    onMatchfunction(instance{
      // 匹配到类实例时进行的操作
    },
    onCompletefunction() {}
    // 完成操作后的回调函数
  });
});

03

用法6:使用对象参数调用的方法

APP下载地址:https://github.com/DERE-ad2001/Frida-Labs/tree/main/Frida%200x6

01
了解APP样本

在下载Challenge 0x6.apk文件并上传至大狗云真机操作平台后,发现APP程序界面跟之前一样没有任何有效信息(图8)。直接将APK放入jadx进行静态分析。

APP动态分析系列 - Frida的基础用法(下)

图8. 应用程序Challenge 0x6界面

02
静态分析

使用jadx反编译APP,查看反编译源代码,找到程序入口。分析代码可以发现,MainActivity类中同样有一个没有被调用的方法get_flag(图9)。该方法被传入了一个Checker类型的参数A,方法中会检查A参数的变量num1和num2,如果num1等于1234且num2等于4321,则会将flag解密并设置给TxtView控件。我们进一步查看Checker类中的内容。

APP动态分析系列 - Frida的基础用法(下)

图9.MainActivity类

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类中定义了两个变量,num1和num2,并且这个类在程序中没有实例(图10)。

APP动态分析系列 - Frida的基础用法(下)

图10. Checker类

分析完APP我们可以知道,程序中有一个没有被调用的方法get_flag,该方法对传入的Checker类型参数A进行检查,如果参数A的num1变量值等于1234且num2变量值等于4321,就会将flag解码并输出。为了得到方法中的flag,我们需要获取MainActivity类的实例来调用这个方法,同时还需要满足方法内部对传入参数A.num1和A.num2变量值的验证。因此,我们还需要创建一个Checker类的实例,并设置好实例中num1和num2变量的值,在调用get_flag方法时将这个实例作为参数传递。

03
编写Frida脚本

我们需要使用Frida框架,编写一个JavaScript脚本,完成以下操作:

  • 创建一个Check类的实例;

  • 将这个实例的num1参数设置为1234,num2参数设置为4321;

  • 获取程序启动时创建的MainActivity类的实例;

  • 通过MainActivity类实例调用get_flag方法,并使用设置好的Check类实例作为参数。

首先明确MainActivity类和Check类位于程序的com.ad2001.frida0x6包中,上述功能的代码实现如下:

/*
Java.performNow是Frida中的一个函数,用于在Java运行时环境中执行代码。
*/


Java.performNow(function() {

  Java.choose('com.ad2001.frida0x6.MainActivity', {
    // Java.choose在运行时枚举MainActivity类的实例。
    // 第二个参数需要传入包含onMatch和onComplete两个回调函数的选项对象。

    onMatch: function(instance{
      // 处理匹配到的类实例的回调函数

      console.log("Instance found");
       // 处理匹配到的类实例的回调函数

      var checker = Java.use("com.ad2001.frida0x6.Checker");
       // Java.use函数指定要使用com.ad2001.frida0x6包的Check类。

      var checker_obj  = checker.$new();  
       // 使用$new()方法创建Check类的实例check_obj。
      checker_obj.num1.value = 1234
       // 将check_obj实例的num1变量值设置为1234
      checker_obj.num2.value = 4321
       // 将check_obj实例的num2变量值设置为4321

      instance.get_flag(checker_obj); 
       // 使用check_obj作为参数;
       // 通过获取到的MainActivity类的实例instance调用get_flag方法

    },
    onCompletefunction() {}
  });
});

04
运行脚本

进入云真机操作台启动APP,在Frida脚本功能区点击【新增脚本】,命名为Challenge_0x6_hook,将编写好的代码复制进来,点击【加载脚本】。由于我们需要在程序启动之后完成创建Check类实例和获取MainActivity类实例的操作,因此脚本运行时不需要重启APP。运行成功之后,完成get_flag方法调用以及对参数值的检验,flag被输出到程序界面上(图11)。

APP动态分析系列 - Frida的基础用法(下)

图11. 运行脚本获取flag

05
总结

Frida 使用对象参数调用方法的脚本模板:

Java.performNow(function() {
  Java.choose('<Package>.<class_Name>', {
    onMatchfunction(instance{
      var <class_reference> = Java.use("<package_name>.<class>");
      var <class_instance> = <class_reference>.$new(); // 创建类的实例
      /*
      设置实例参数
      */
      instance.<method>(class_instance); // 使用对象参数调用方法
    },
    onComplete: function() {}
    // 完成操作后的回调函数
  });
});

04

用法7:Hook构造函数

APP下载地址:https://github.com/DERE-ad2001/Frida-Labs/tree/main/Frida%200x7

01
了解APP样本

下载Challenge 0x7.apk文件上传至大狗云真机操作平台,可以看到APP程序界面依旧没有任何有效信息(图12)。我们将APK放入jadx进行静态分析。

APP动态分析系列 - Frida的基础用法(下)

图12.应用程序Challenge 0x7界面

02
静态分析

使用jadx反编译APP,查看反编译源代码,找到程序入口。分析代码可以发现,MainActivity类中的onCreate方法在程序启动时使用123和321两个参数创建了一个Checker对象ch,并调用flag方法传入该对象。flag方法会检查传入的Checker对象A的属性num1和num2的值,如果满足条件A.num1 > 512 && 512 < A.num2,则执行一系列加密和解密操作,并将解密后的内容设置到TextView控件t1上显示出来(图13)。我们继续分析Checker类的构造。

APP动态分析系列 - Frida的基础用法(下)

图13.MainActivity类

Checker类定义了两个整型成员变量num1和num2,分别用于存储整数值。类中包含了一个构造函数Checker(int a, int b),该构造函数会在类对象创建(初始化)的时候自动执行,它接受两个整型参数a和b,并将这两个参数分别赋值给类的成员变量num1和num2(图14)。

APP动态分析系列 - Frida的基础用法(下)

图14.Checker类

分析完APP我们发现,程序在启动的时候创建了一个Checker类的对象,并将其初始化参数num1和num2设置为123和321,然后调用flag方法传入该对象。flag方法对传入Checker对象的num1和num2变量进行验证,如果满足num1和num2均大于512,则会将解密flag并设置到TextView控件上。由于程序初始化时设置的参数123和321不满足条件,因此应用程序界面没有显示flag。为了获取这个flag我们可以Hook Checker类对象的构造函数Checker(int a, int b),将参数a,b修改为大于512的值来绕过检验。

03
编写Frida脚本

我们需要使用Frida框架,编写一个JavaScript脚本,来Hook创建Checker类对象时执行的构造函数,并修改初始化的参数值。

首先明确,我们需要Hook的函数位于程序com.ad2001.frida0x7包中的Checker类中。Hook构造函数需要使用$init关键字,Hook创建Checker类对象执行的构造函数Checker(int a, int b),并将传入参数a,b修改为600,代码实现如下:

/***
Java.perform 是Frida中的一个函数,用于为脚本创建特殊上下文,进入此上下文后,可以执行挂钩方法或访问Java类等操作来控制或观察应用程序的行为。
**/


Java.perform(function (){

  var a = Java.use("com.ad2001.frida0x7.Checker");
    // 声明了一个变量a来表示目标Android应用程序中的Java类。
    // Java.use函数指定要使用com.ad2001.frida0x7包中的Checker类。

  a.$init.implementation = function (a,b){
    // $init关键字Hook Checker类对象创建时执行的构造函数

    console.log("Origin num:",a,b);
    // 打印a,b变量初始化时设置的值

    this.$init(600,600);
    // 调用Checker类构造函数将a,b变量值修改为600
  }
})

04
运行脚本

进入云真机操作台启动APP,在Frida脚本功能区点击【新增脚本】,命名为Challenge_0x7_hook,将编写好的代码复制进来,点击【加载脚本】。由于我们Hook的构造函数在程序初始化时被调用,因此运行脚本时需要【勾选重启APP】选项(图15)。脚本运行成功后构造函数中传入的a,b参数的值已经被修改为600,成功绕过flag函数对num1和num2参数的检验获取到flag(图16)。

APP动态分析系列 - Frida的基础用法(下)

图15.点击加载脚本勾选重启APP

APP动态分析系列 - Frida的基础用法(下)

图16.脚本运行成功获取flag

05
总结

Frida Hook构造函数的脚本模板为:

Java.perform(function() {
  var <class_reference> = Java.use("<package_name>.<class>");
  <class_reference>.$init.implementation = function(<args>){

    /*
    我们自己的方法实现
    */

  }
});

结语

以上为本期分享的四个Frida的基础用法,本系列文章后续还会分上、下两篇介绍Frida的进阶用法。欢迎相关公检法技术工作者注册安装【无糖浏览器】,自行通过【大狗平台专区】的【云真机操作台】复现研究。大家还可以根据Frida-lab项目中的参考答案,尝试更多有趣的解题方法。

参考链接

[1]用法4:https://github.com/DERE-ad2001/Frida-Labs/blob/main/Frida+0x4/Solution/Solution.md

[2]用法5:https://github.com/DERE-ad2001/Frida-Labs/blob/main/Frida+0x5/Solution/Solution.md

[3]用法6:https://github.com/DERE-ad2001/Frida-Labs/blob/main/Frida+0x6/Solution/Solution.md

[4]用法7:https://github.com/DERE-ad2001/Frida-Labs/blob/main/Frida+0x7/Solution/Solution.md

无糖浏览器-您身边的办案助手

01

下载地址(PC端与APP同链接):

http://browser.nosugar.tech

02

邀请码:注册邀请码可从已认证通过的公安民警处获得,完成注册流程并审核通过可开通完整使用权限。

如有疑问,可以扫描下方二维码进入无糖反网络犯罪研究中心。

APP动态分析系列 - Frida的基础用法(下)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月12日23:42:28
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   APP动态分析系列 - Frida的基础用法(下)https://cn-sec.com/archives/2651455.html

发表评论

匿名网友 填写信息