51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1269|回复: 0
打印 上一主题 下一主题

如何让Frida实现Hook注入?

[复制链接]
  • TA的每日心情
    无聊
    4 天前
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-11-9 10:59:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    Frida 通过 C 语言将 QuickJS 注入到目标进程中,获取完整的内存操作权限,达到在程序运行时实时地插入额外代码和数据的目的。官方将调用代码封装为 python 库,当然你也可以直接通过其他的语言调用 Frida 中的 C 语言代码进行操作。
      Frida安装和启动
      电脑端 Frida 安装
      Frida 支持 python2 和 python3 版本,演示所使用的版本为 python3.8
    1. <font size="3">pip install frida-tools</font>
    复制代码
     如果在安装中卡住,需要在 Frida 的 pypi 页面下载对应系统的 egg 文件,对应页面地址为:https://pypi.org/project/frida/#files ,并将该文件放置到个人文件夹路径下,例如 C:\Users\当前用户名 ,再重新使用命令安装。
      安装完毕后可以通过命令frida --version 来查看安装的版本,确认是否安装成功。
      手机端 Frida-server 安装
      本次示例使用 Android App 作为目标程序,所以需要电脑端安装 SDK 环境,以便能够连接手机进行调试操作,还需在手机端准备一个 Frida-server,下载地址为:https://github.com/frida/frida/releases,下载匹配手机 CPU 架构和本地 Frida 版本的包。
      下载之后解压文件,使用adb push 命令将文件推送到手机端,建议放置在/data/local/tmp 文件夹中,并修改该文件的权限为 755,以便之后进行启动。
      确认环境运行正常
      通过 Frida 提供的一些小工具,对 Frida 的安装运行环境做简单的确认。
      首先准备一个 Android 模拟器或者真机,将上一步中提到的 Frida-server 推送到手机端中,在本示例中将放置在手机的/data/local/tmp 文件夹内,并将文件命名为frida-server 。
      通过adb  如果在安装中卡住,需要在 Frida 的 pypi 页面下载对应系统的 egg 文件,对应页面地址为:https://pypi.org/project/frida/#files ,并将该文件放置到个人文件夹路径下,例如 C:\Users\当前用户名 ,再重新使用命令安装。  安装完毕后可以通过命令frida --version 来查看安装的版本,确认是否安装成功。
      手机端 Frida-server 安装
      本次示例使用 Android App 作为目标程序,所以需要电脑端安装 SDK 环境,以便能够连接手机进行调试操作,还需在手机端准备一个 Frida-server,下载地址为:https://github.com/frida/frida/releases,下载匹配手机 CPU 架构和本地 Frida 版本的包。
      下载之后解压文件,使用adb push 命令将文件推送到手机端,建议放置在/data/local/tmp 文件夹中,并修改该文件的权限为 755,以便之后进行启动。
      确认环境运行正常
      通过 Frida 提供的一些小工具,对 Frida 的安装运行环境做简单的确认。
      首先准备一个 Android 模拟器或者真机,将上一步中提到的 Frida-server 推送到手机端中,在本示例中将放置在手机的/data/local/tmp 文件夹内,并将文件命名为frida-server 。
      通过adb shell 命令连接手机,运行/data/local/tmp/frida-server & ,将 Frida-server 放在系统后台自动运行。
      在本地电脑终端中运行frida-ps -U ,结果如下展示手机中的进程信息,说明环境已经准备完毕。 命令连接手机,运行/data/local/tmp/frida-server & ,将 Frida-server 放在系统后台自动运行。
      在本地电脑终端中运行frida-ps -U ,结果如下展示手机中的进程信息,说明环境已经准备完毕。

    1. <font size="3"> PID  Name

    2. -----  --------------------------------------------------

    3. 1313  adbd

    4. 12621  android.process.acore

    5. 18037  android.process.media

    6. 14455  com.android.defcontainer

    7. 11656  com.android.deskclock

    8. ...</font>
    复制代码
    示例
      目标应用介绍
      因为 Hook 需要通过分析源码中的逻辑来实现,所以先展示一下目标应用的源码部分,方便分析其中的逻辑,找到 Hook 时要修改的方法和变量。
      代码是简单的猜黑白游戏,通过按下黑或白按钮,与电脑结果进行对比,结果相同加 1 分,结果不同分数清零,当满 100 分时打出胜利提示语。具体代码如下:



    1. <font size="3">package com.example.target_frida;

    2. import androidx.appcompat.app.AppCompatActivity;

    3. import android.annotation.SuppressLint;

    4. import android.os.Bundle;

    5. import android.view.View;

    6. import android.widget.Button;

    7. import android.widget.TextView;

    8. public class MainActivity extends AppCompatActivity implements View.OnClickListener {




    9.     TextView winCountView;

    10.     TextView battleInfoTextView;

    11.     int winCount = 0;

    12.     @SuppressLint("SetTextI18n")

    13.     @Override

    14.     public void onClick(View view) {

    15.         if (winCount > 100) {

    16.             return;

    17.         }

    18.         if (view.getId() == R.id.tvButtonBlack) {

    19.             if (!getCPUResult()) {

    20.                 winCount++;

    21.                 winCountView.setText(winCount + "");

    22.                 battleInfoTextView.setText("Right!");

    23.             } else {

    24.                 winCount = 0;

    25.                 winCountView.setText(winCount + "");

    26.                 battleInfoTextView.setText("Wrong! Clean All!");

    27.             }

    28.         } else if (view.getId() == R.id.tvButtonWhite) {

    29.             if (getCPUResult()) {

    30.                 winCount++;

    31.                 winCountView.setText(winCount + "");

    32.                 battleInfoTextView.setText("Right!");

    33.             } else {

    34.                 winCount = 0;

    35.                 winCountView.setText(winCount + "");

    36.                 battleInfoTextView.setText("Wrong! Clean All!");

    37.             }

    38.         }

    39.         if (winCount >= 100) {

    40.             battleInfoTextView.setText("Win 100 times!!!");

    41.         }

    42.     }

    43.     @SuppressLint("SetTextI18n")

    44.     @Override

    45.     protected void onCreate(Bundle savedInstanceState) {

    46.         super.onCreate(savedInstanceState);

    47.         setContentView(R.layout.activity_main);

    48.         winCountView = findViewById(R.id.winCount);

    49.         winCountView.setText(winCount + "");

    50.         battleInfoTextView = findViewById(R.id.battleInfo);

    51.         battleInfoTextView.setText("猜黑白!!!");

    52.         Button buttonBlack = (Button) findViewById(R.id.tvButtonBlack);

    53.         Button buttonWhite = (Button) findViewById(R.id.tvButtonWhite);

    54.         buttonBlack.setOnClickListener(this);

    55.         buttonWhite.setOnClickListener(this);

    56.     }

    57.     public boolean getCPUResult() {

    58.         //true为白,false为黑

    59.         return Math.random() > 0.5;

    60.     }

    61. }</font>
    复制代码


    Hook 需求分析
      由于正常情况下,连赢 100 次的概率几乎为零,如果想要达到胜利条件,Hook 就是一个比较好的方式。
      首先分析一下源码,想要达到连赢 100 次的情况,可以有两种解决办法:一种是通过修改用来记录连赢次数的变量winCount ,将连赢记录改成 99,这样只需要再赢一次就可以获得胜利;还有一种是通过修改getCPUResult 方法的返回值,让其固定返回一种可能性,这样只需要选择对应的颜色就可以连续获胜。接下来通过实现第一个方案,看看使用 Frida 如何达到想要的效果。
      第一种实现:修改结果变量中保存的值
      首先展示修改代码,然后再进行逐步讲解:



    1. <font size="3">import time

    2. import frida, sys

    3. date_str = time.strftime('%m-%d %H:%M:%S')

    4. def on_message(message, data):

    5.     if message['type'] == 'send':

    6.         print(f"[{date_str}] {message['payload']}")

    7.     else:

    8.         print(f"[{date_str}] {message}")

    9. def run_all():

    10.   # Java.perform方法:当 js 附加到目标的进程中时被执行,运行其中定义的函数

    11.   # Java.choose方法:通过完整类名,获取它的实例,从而对实例中的数据进行修改

    12.   # 通过 key:value 结构定义了两个函数:

    13.   # onMatch 对应的函数在命中一个实例的时候被调用,传入函数中的参数 instance 就是被命中的实例

    14.   # onComplete 函数会在所有实例遍历完毕之后被调用,可以做一些后续处理操作

    15.     jscode = """

    16.     Java.perform(function () {

    17.       Java.choose("com.example.target_frida.MainActivity",{

    18.         onMatch:function(instance){

    19.             console.log("winCount value is "+instance.winCount.value);

    20.             instance.winCount.value=99;

    21.             console.log("winCount value is "+instance.winCount.value);

    22.         },

    23.         onComplete:function(){

    24.             console.log("Complete!!!")

    25.         }

    26.       });

    27.     });

    28.     """

    29.     # attach目标App进程

    30.     target_app = 'com.example.target_frida'

    31.     process = frida.get_usb_device().attach(target_app)

    32.     # 将JS代码注入进程,并附加监听方法,用来获取返回的日志信息

    33.     script = process.create_script(jscode)

    34.     script.on('message', on_message)

    35.     # 打印起始日志

    36.     print(f'[{date_str}] Start Frida on {target_app}')

    37.     # 加载注入的JS代码逻辑

    38.     script.load()

    39.     # 使用系统输入语句阻止函数运行完毕自动退出

    40.     sys.stdin.read()

    41. if __name__ == '__main__':

    42.     run_all()</font>
    复制代码


    代码中的 python 语句已经添加了注释,Hook 的核心逻辑,JS 语句作为字符串保存在 jscode 变量中。
      梳理一下整个 JS 语句的流程:通过Java.choose 函数获取com.example.target_frida.MainActivity 类的实例。在获取到实例时,首先使用console.log 语句将当前实例中的 winCount 变量值(使用 winCount.value)打印到日志中,之后直接通过赋值语句把变量值改为 99,再次输出日志确认修改无误,修改逻辑就完成了。
      在手机端启动 Frida-server 和被测 App,电脑端运行脚本,可以看到在命令行中输出如下内容:



    1. <font size="3">[04-15 18:19:57] Start Frida on com.example.target_frida

    2. winCount value is 0

    3. winCount value is 99

    4. Complete!!!</font>
    复制代码


    这时在 App 中选择一个颜色点击,只要选中正确的颜色,就可以成功达到预期的 100 次连胜目标,如果没能选中正确的颜色导致清零,可以再重复运行脚本修改一次数值,在这种情况下要达到预期的场景就很容易。
      总结
      第二个方案以及其他更多的可能性,就留给读者自行探索,在这里送上 Frida 官方 JavaScript API 链接:https://frida.re/docs/javascript-api/ ,可以通过这个链接找到你所需要的 JS 函数。
      通过示例可以看到 Frida 实现 Hook 功能的强大能力,它可以定位到类的实例,并且对实例中的数据进行直接的修改,达到场景构建的目的。


    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-25 14:15 , Processed in 0.064055 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表