如何使用 Frida 对桌面和移动应用程序进行动态分析和逆向工程 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

如何使用 Frida 对桌面和移动应用程序进行动态分析和逆向工程

walker 技术 2023-09-22 11:30:00
31894
收藏

导语:在本文中,您将学习如何使用 Frida 动态分析您的应用程序并发现漏洞。

确保软件产品的安全性和可靠性是现代企业面临的最大挑战之一。随着产品的发展,每个新功能都会为潜在的安全漏洞和性能瓶颈打开大门。 

动态分析和逆向工程是允许开发人员在应用程序运行时探索其内部工作原理的两种方法。借助这些见解,开发人员可以识别漏洞并发现潜在的安全问题。

在本文中,您将学习如何使用 Frida 动态分析您的应用程序并发现漏洞。我们将根据我们自己的项目经验向您展示 Frida 工具的实际应用示例。本文对于想要确保其产品受到保护的安全研究人员、CTO 和 CSO 非常有用。 

什么是动态分析以及为什么它很重要?

动态分析和逆向工程是企业增强产品安全性的两种重要网络安全方法。

动态分析允许网络安全专家评估软件运行时的行为。这对于:

  • 漏洞检测——发现潜在漏洞并计划网络安全增强,以保护您的产品免受数据泄露

  • 真实世界模拟 — 了解您的产品在现实条件下的表现,使您能够从用户的角度检查您的产品,并了解它如何处理敏感数据和关键操作

  • 性能优化——除了安全性之外,动态分析还有助于优化应用程序性能,从而帮助您改善产品的用户体验并优化基础设施成本

  • 合规保证——在潜在的违规行为导致合规违规和处罚之前识别它们

动态分析用于逆向工程,即使没有源代码或文档,开发人员也可以剖析软件并了解其内部工作原理。 

在 Apriorit,我们专注于逆向工程,并利用它来帮助企业评估和提高其软件的安全性。这包括保护其免受可能导致重大声誉和金钱损失的违规、恶意软件攻击和数据泄露。

有许多工具可以让企业进行动态分析和逆向工程。在我们的逆向项目中,我们经常使用Frida。

Frida是什么?

Frida是一个动态开源检测工具包,允许开发人员和逆向工程师将 JavaScript 代码注入正在运行的应用程序中。注入使开发人员能够实时跟踪函数调用、修改函数行为和拦截数据,使其成为动态分析的宝贵工具。

Frida 提供了一套全面的 API,可用于与正在运行的应用程序进行交互。 

image.png

Frida 的体系结构基于客户端-服务器模型。Frida 服务器在目标设备或计算机上运行,而客户端用于与服务器交互并将 JavaScript 代码注入正在运行的应用程序中。

让我们看几个示例,了解您可以使用 Frida 执行哪些动态应用程序分析任务。我们将从桌面应用程序开始。

使用 Frida 对桌面应用程序进行动态分析

Frida 支持 Windows、macOS 和 Linux,使其成为分析桌面应用程序的可靠选择,无论它们运行在何种操作系统上。让我们看看您可以使用 Frida 执行的一些任务。

在执行任何动态分析或操作操作之前,我们需要将 Frida 注入到我们的桌面应用程序中。这是一个相对简单的过程:

  • 在目标系统上安装 Frida。

  • 使用命令frida-server -l 0.0.0.0.这将启动 Frida 服务器并使其可供网络上的其他设备访问。 

  • 将 Frida 客户端附加到目标进程并注入 Frida 脚本。

在 Windows 桌面应用程序中挂钩 MessageBox

为了演示如何使用 Frida 挂钩函数,让我们考虑在 Windows 桌面应用程序中挂钩MessageBox函数的示例。该函数用于向用户显示消息框,使其成为动态分析的绝佳目标。

要使用 Frida 挂钩MessageBox函数,我们首先需要编写一个 Frida 脚本。该脚本将附加到目标进程,找到 MessageBox 函数的地址,并使用拦截器挂钩该函数。这是我们的一个项目中的自定义脚本:

image.png

在此脚本中,我们使用Module.findBaseAddress查找 user32.dll 库的基地址,其中包含MessageBox函数。然后,我们使用findExportByName方法获取MessageBox函数的地址(对于私有方法,我们可以使用带有偏移量的add)。最后,我们使用Interceptor.attach来挂钩MessageBox函数,在调用该函数时将消息记录到控制台。

要运行此脚本,请将其保存到文件(例如hook_messagebox.js)并使用以下命令运行 Frida 客户端:

image.png

这会将 Frida 连接到目标进程并注入脚本。当目标进程调用MessageBox函数时,Frida将拦截该函数并将消息记录到控制台。

在 macOS 桌面应用程序中修改 NSURLRequest

Frida 在桌面应用程序中的另一个强大功能是实时修改数据。例如,让我们考虑使用NSURLRequest发出 HTTP 请求的macOS 桌面应用程序的情况。我们可以使用 Frida 在发送 HTTP 请求之前对其进行修改,从而使我们能够绕过安全措施或修改应用程序的行为。

要使用 Frida 修改macOS 桌面应用程序中的NSURLRequest,我们首先需要编写一个 Frida 脚本。该脚本将附加到目标进程,查找NSURLRequest对象的地址,并使用 JavaScript 修改其属性。这是一个例子:


function modify_nsurlrequest() {
  // Find the address of the NSURLRequest object
  var requestPtr = null;
  var objc_msgSend = new NativeFunction(Module.findExportByName("libobjc.A.dylib", "objc_msgSend"), "pointer", ["pointer", "pointer"]);
  Interceptor.attach(objc_msgSend, {
    onEnter: function (args) {
      var sel = ObjC.selectorAsString(args[1]);
      if (sel === "initWithURL:cachePolicy:timeoutInterval:") {
        requestPtr = args[0];
      }
    },
    onLeave: function (retval) {
      if (requestPtr !== null) {
        var request = new ObjC.Object(requestPtr);
        request.setValue_forHTTPHeaderField_("my-custom-header", "X-Custom-Header");
        requestPtr = null;
      }
    }
  });
}
// Attach to the target process and wait for it to start
Process.enumerateApplications({
  onMatch: function (info) {
    if (info.name === "TargetApp") {
      console.log("Attaching to " + info.name + " (" + info.pid + ")");
      // Attach to the process and wait for its main module to be loaded
      Process.attach(info.pid, {
        onModuleLoaded: function (module) {
          if (module.name === "TargetApp") {
            // Wait for the script runtime to be ready
            setTimeout(modify_nsurlrequest, 0);
          }
        }
      });
    }
  },
  onComplete: function () {
    console.log("Failed to find target process");
  }
});

该脚本使用objc_msgSend函数拦截对NSURLRequest类的initWithURL:cachePolicy:timeoutInterval:方法的调用。当调用这个方法时,我们保存初始化的NSURLRequest对象的地址。当该方法返回时,我们创建一个代表NSURLRequest对象的ObjC.Object实例,并根据需要修改其属性。

接下来,我们使用Process.enumerateApplications按名称查找目标进程。找到进程后,我们使用Process.attach附加到它并等待其主模块加载。当主模块加载时,我们调用setTimeout函数来安排在下一次事件循环迭代期间对修改_nsurlrequest 函数的调用,从而为脚本运行时提供完全初始化的机会。

一旦调用了modify_nsurlrequest函数,它将拦截对NSURLRequest初始化方法的调用,并根据需要修改任何初始化的NSURLRequest对象的属性。

这些只是您可以在桌面应用程序中使用 Frida 执行的动态分析任务的几个示例。现在让我们看一下 Frida 在移动应用程序中的用途。

使用 Frida 对移动应用程序进行动态分析

动态分析是识别移动应用程序中漏洞的有效方法,因为它使我们能够实时监控应用程序行为。 

Frida 可用于对 Android 和 iOS 应用程序执行动态分析。借助 Frida,我们可以连接正在运行的应用程序并监视其行为,包括函数调用、网络流量和内存使用。

要使用Frida对应用程序进行动态分析,我们首先需要将应用程序安装在物理或虚拟设备上。然后,我们可以将 Frida 附加到正在运行的进程并开始监视应用程序的行为。

Frida 可用于未 root、已 root 和越狱的设备。Frida 官方文档中详细描述了安装。通常,它涉及在目标设备上运行 run frida-server 命令并在调试模式下使用 USB 连接到目标设备。 

让我们看一些示例,了解如何使用 Frida 在移动应用程序上运行动态分析。 

绕过根检测

许多开发人员实施 root 检测以防止用户在 root 设备上运行其应用程序。然而,使用 Frida 的动态分析可以绕过这个问题。

让我们看一个使用 Frida 绕过 Android 应用程序中的 root 检测的示例。在这种情况下,我们假设应用程序正在使用 SafetyNet API 来检查设备是否已获得 root 权限。

为了绕过 root 检测,我们将使用 Frida 拦截对 SafetyNet API 的调用并修改返回值以指示设备未获得 root 权限。下面是实现此目的的 JavaScript 代码:

image.png

然而,现代移动应用程序和系统通常采用多层安全措施来检测和防止各种形式的篡改和未经授权的访问。因此,绕过当代应用程序和系统中的根检测可能需要更广泛和复杂的挂钩技术。

您可以在CodeShare上找到这些技术和实施示例。

篡改 API 调用

移动应用程序通常使用 API 与后端服务器进行通信。通过篡改 API 调用,攻击者可以修改应用程序行为或窃取敏感数据。

我们将在 Frida 动态仪器中合乎道德地使用这项技术。篡改 API 调用可以帮助您评估应用程序的整体安全性、检查流量和监控行为。 

Frida 允许篡改 iOS 应用程序中的 API 调用。在本例中,我们假设应用程序正在使用 URLSession API 向后端服务器发出请求。

为了篡改 API 调用,我们将使用 Frida 拦截对 URLSession API 的调用并在请求发送到服务器之前修改请求。这是实现此目的的代码: 

image.png

通过篡改API调用,您可以评估应用程序的安全漏洞。在 Apriorit,我们使用这种方法来模拟各种攻击场景,并识别应用程序处理数据、与服务器通信或响应意外输入的方式中的潜在弱点。

与 Frida 进行逆向工程

您可以使用 Frida 进行逆向工程。它提供了一个动态分析环境,可帮助您检查应用程序在运行时的行为方式。Frida 允许我们挂钩应用程序的执行流程,监视和操作函数调用和参数,并拦截应用程序发送或接收的数据。

在 Apriorit,我们使用 Frida 执行各种逆向工程任务,例如:

  • 提取加密密钥

  • 分析网络流量

  • 跟踪系统调用

  • 识别恶意软件行为

  • 研究专有协议

  • 分析二进制代码

还有其他活动,例如使用 Frida 进行恶意软件检测,允许网络安全专家对恶意软件进行逆向工程。 

在本指南中,我们使用 Android 应用程序作为示例演示前三个任务。Frida 特别适合 Android 平台,而其他工具可能更适合桌面和 iOS 平台上的逆向工程任务。

提取加密密钥

Apriorit 安全专家团队使用 Frida 提取应用程序使用的加密密钥来保护敏感数据。通过连接应用程序的加密函数,我们可以拦截应用程序生成或使用的密钥并记录它们以供进一步分析。

以下是使用 Frida 从应用程序中提取加密密钥的脚本示例:

image.png

该脚本挂钩javax.crypto.KeyGenerator和javax.crypto.Cipher类并拦截它们的函数调用。当KeyGenerator初始化新密钥时,脚本会记录密钥大小并生成密钥。当使用密钥初始化Cipher时,脚本会记录有关密钥的信息。

分析网络流量

Frida 可用于通过连接网络功能并拦截应用程序发送或接收的数据来分析应用程序的网络流量。这对于识别通过网络传输的敏感数据以及了解应用程序如何与外部服务进行通信非常有用。

下面是一个使用 Frida 拦截网络流量的示例脚本:


Java.perform(function() {
  var URL = Java.use("java.net.URL");
  var HttpURLConnection = Java.use("java.net.HttpURLConnection");
  var OutputStreamWriter = Java.use("java.io.OutputStreamWriter");
  var BufferedReader = Java.use("java.io.BufferedReader");
 
  // Intercept the request
  HttpURLConnection.getOutputStream.implementation = function() {
var outputStream = this.getOutputStream();
var requestMethod = this.getRequestMethod();
var url = this.getURL().toString();
 
// Print out the request method and URL
console.log("[+] Intercepted " + requestMethod + " request to " + url);
 
// Read the request body
var request = "";
      // Get the InputStream object
      var inputStream = this.getInputStream();
      // Create a new InputStreamReader object
      var inputStreamReader = InputStreamReader.$new.overload('java.io.InputStream', 'java.lang.String')(inputStream, 'UTF-8');
      // Create a new BufferedReader object
      var BufferedReader_instance = BufferedReader.$new.overload('java.io.Reader')(inputStreamReader);
var line = "";
while ((line = BufferedReader_instance.readLine()) != null) {
      request += line;
}
 
// Print out the request body
console.log("[+] Request body: " + request);
 
// Modify the request
if (requestMethod == "POST") {
      var modifiedRequest = "param1=value1&param2=value2";
      console.log("[+] Modifying request body to: " + modifiedRequest);
      // Get the OutputStreamWriter constructor
          var OutputStreamWriter = Java.use('java.io.OutputStreamWriter');
          // Get the Charset and StandardCharsets classes
          var Charset = Java.use('java.nio.charset.Charset');
          var StandardCharsets = Java.use('java.nio.charset.StandardCharsets');
          // Get the OutputStream object
          var outputStream = this.getOutputStream();
          // Get the UTF-8 Charset object
          var utf8Charset = StandardCharsets.UTF_8;
          // Create a new OutputStreamWriter object
          var outputStreamWriter = OutputStreamWriter.$new.overload('java.io.OutputStream', 'java.nio.charset.Charset')(outputStream, utf8Charset);
          outputStreamWriter.write(modifiedRequest);
      outputStreamWriter.flush();
      outputStreamWriter.close();
}
 
return outputStream;
  };
 
  // Intercept the response
  HttpURLConnection.getInputStream.implementation = function() {
var inputStream = this.getInputStream();
 
// Read the response body
var response = "";
// Get the InputStream object
      var inputStream = this.getInputStream();
      // Create a new InputStreamReader object
      var inputStreamReader = InputStreamReader.$new.overload('java.io.InputStream', 'java.lang.String')(inputStream, 'UTF-8');
      // Create a new BufferedReader object
      var BufferedReader_instance = BufferedReader.$new.overload('java.io.Reader')(inputStreamReader);
var line = "";
while ((line = BufferedReader_instance.readLine()) != null) {
  response += line;
}
 
// Print out the response body
console.log("[+] Response body: " + response);
 
return inputStream;
  };
});

该脚本拦截 HTTP 请求和应用程序响应并显示有关它们的信息,包括:

  • 请求方式

  • 网址

  • 请求正文

  • 响应体

它还演示了如何使用 Frida 在请求正文发送到服务器之前对其进行修改。通过以这种方式拦截网络流量,安全研究人员可以分析应用程序如何与其后端服务器通信,识别漏洞和潜在的攻击向量,并开发利用它们的漏洞。 

当与 SSL pinning 旁路机制结合使用时,Frida 还可以允许开发人员除了拦截流量之外还可以操纵流量。

跟踪系统调用

识别恶意软件行为是安全研究中的一项重要任务。开发人员可以使用 Frida 来完成此任务。

识别恶意软件行为的一种方法是使用 Frida 跟踪应用程序发出的系统调用。系统调用是应用程序与操作系统交互的机制。通过跟踪这些调用,安全专家或研究人员可以深入了解应用程序的行为并识别潜在的漏洞。

另一种方法是使用 Frida 来跟踪应用程序与文件系统和其他资源的交互。通过监视文件系统活动,研究人员可以识别应用程序可能创建或访问的任何可疑文件或目录。

以下是我们创建的脚本示例,用于跟踪使用 Frida 的应用程序进行的系统调用:


function WhoCalledMe(instance, amount) {
      var Thread = Java.use('java.lang.Thread')
      var threadinstance = Thread.$new()
      var stack = threadinstance.currentThread().getStackTrace()
      var stackClassNames = stack.map(function(x){return x.getClassName()})
      var index = stackClassNames.indexOf(instance.$className)
      if (index === -1) {
        console.log('failed to find calling class in the stacktrace: ' + stack + '\n class: ' + instance.$className)
        return
      }
      return stackClassNames.slice(index + 1, index + 1 + amount)
}

该脚本使用java.lang.Thread创建一个新线程,并从调用堆栈中打印类名的数量(基于数量值)。

根据您的需要,您可以自定义此脚本来执行与 Java 应用程序中的方法调用跟踪、调试或分析相关的任务。

结论

动态分析在识别应用程序中的漏洞方面发挥着至关重要的作用,因为它允许您了解软件的内部工作原理。


本文翻译自:https://www.apriorit.com/dev-blog/web-frida-dynamic-analysis如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务