初学者如何对iOS应用程序进行逆向工程和修补:第1部分

原文链接:https://www.inversecos.com/2022/06/how-to-reverse-engineer-and-patch-ios.html

你想逆转和修补iOS应用程序吗?我给你>_<

这篇博客的重点是反转我构建的iOS应用程序,目的是向初学者展示如何反转和修补iOS应用程序。不需要花哨的工具(IDA O.o),只有你,我和debugger<3

该应用程序是一个简单的,未加密的Objective-C应用程序,它只接受密码,其目标是绕过密码机制并获得成功代码。这篇博客文章将重点介绍应用程序的反转/调试,并且不会涉及静态分析的各个方面。我想写这篇文章的原因是因为我意识到这个话题对很多人来说都是令人困惑的,我想尝试写一个博客,试图以一种对初学者更友好的方式解释它。

下面的屏幕截图显示了我可爱的应用程序的外观 – 它被称为“breakmedaddy”,左侧显示您尝试输入密码 – 右侧显示所需的旁路屏幕。

为什么我是用Objective-C而不是Swift构建的?因为你的女孩是一个qt狂:)!但也因为Objective-C允许你在运行时修改方法,这意味着它比在Swift中构建应用程序更容易挂接到函数中。

如果你想关注这篇博客文章,这个应用程序可以在我的GitHub上找到(这是我公开上传的第一件事,因为我害羞>_<):

为了使这尽可能容易理解,我将博客分为三个部分,以便您可以跳到您感兴趣的任何部分:

  • 我们将采取的高级步骤(这是为了演示如何执行此类操作背后的逻辑)
  • 我们将用于分析的工具
  • 对应用程序进行逆向工程和修补


高级步骤

这就是我们将在本教程中遵循的逻辑:

  1. 越狱iOS设备(我正在使用运行iOS 14.1的旧手机)。
  2. 通过将 IPA 文件解压缩到 /Applications 目录中,通过 SSH 和 SFTP 将应用程序上传到越狱设备。
  3. 重新启动跳板(您可以通过CLI通过SSH或通过可以在Cydia中找到的Respring来执行此操作)。
  4. 打开应用程序并将其保留在前台,并解锁手机
  5. 使用 SSH 查找应用程序的 PID。
  6. 使用 Cycript 挂接到正在运行的应用程序进程中,以允许您从运行时查看实例。
  7. 使用 otool 查看应用程序中存在的库。
  8. 使用 pagestuff 查看 Mach-o 文件的结构(这将显示段、标头、代码签名、符号表等)。
  9. 找到任何有趣的方法名称和变量名称。
  10. 通过在 iOS 设备上运行 debugserver 并通过 LLDB 连接到正在运行的应用程序进程来反转和调试 Mach-O 文件。

我们将使用的工具

您可以使用许多工具来执行此操作,但是,为了保持本演练的精益性,以下是我们将要使用的分析工具:

逆向工程 IOS 指南

步骤1:越狱iOS设备并设置SSH,SFTP
我不打算介绍如何越狱设备,因为有很多关于它的文章和视频。我测试并为其构建应用程序的手机运行iOS 14.1,我在这里使用了unc0ver越狱:https://unc0ver.dev/。unc0ver的网站详细解释了如何执行越狱:)

越狱完成后,进入Cydia并设置SSH。这就像执行搜索“openssh”并安装它一样简单。


步骤 2:通过 SFTP 将应用程序上传到越狱设备,并将其解压缩到应用程序目录中

您可以通过将文件SFTPing到iOS设备/应用程序目录中,然后将其解压缩来执行此操作。该文件的格式是“.ipa”文件,该文件已在XCode中构建。.ipa文件是一个压缩文件,您可以直接将其解压缩到手机上。只需从我的Github下载IPA文件,不要偷看源代码,因为你不是骗子!全文: https://github.com/inversecos/ios-breakmedaddy

结果如下:

步骤3:重新 Springboard 以显示应用程序

你可以通过两种方式之一来做到这一点 – 要么通过CLI杀死SpringBoard,要么你可以在Cydia中下载ReSpring并以这种方式运行它。对于某些上下文,应用程序不会显示在主屏幕上,因为 SpringBoard 尚未刷新它。SpringBoard是一个应用程序,用于管理iPhone主屏幕上显示的内容:)

您可以在终端中运行以下线路:


步骤 3:检查应用程序是否已加载并正常运行
此时,您应该看到主屏幕上加载了“breakmedaddy”应用程序。您还应该能够与它进行交互并输入各种密码。这是应用程序应如下所示:

步骤 4:使用 Cycript 挂接应用程序要执行此步骤,请通过SSH连接到iOS设备并查找“breakmedaddy”应用程序的PID。此应用程序在iOS设备的前台打开非常重要。此应用程序不能“关闭”或背景。

从屏幕截图中可以看出 – 我的应用程序的PID是“1795”。下一步是使用Cycript挂接到这个,这就像运行以下命令一样简单:

我运行的命令“UIApp”显示了应用程序对象的主入口点。如果您想知道这意味着什么 – 请查看此处的iOS文档“UIApplicationMain”:https://developer.apple.com/documentation/uikit/1622933-uiapplicationmain。可以像主函数一样考虑这一点。

如果我们查看我的应用程序的源代码(如下) – 您可以在此处看到main函数返回UIApplicationMain传入四个参数 – 要注意的主要参数是delegateClassName参数,它是“appDelegateClassName”变量,指向AppDelegate类。此类定义了如何“响应”应用程序中触发的某些事件,即iOS将如何处理与应用程序相关的重要“触发器”。

运行Cycript的目的是更好地了解应用程序的内部结构及其运行方式,以便我们知道如何“bypass(绕过)”密码检查。我们在这里要做的是专注于理解应用程序代码的“层次结构”或“布局”。我们将以黑匣子的形式处理这个问题,而不看源代码。

例如,为了弄清楚委托类的名称 – 您可以运行“UIApp.delegate”,如下图所示,它返回委托类“AppDelegate”的名称,这是我的应用程序中提到的名称。

下一步是找出管理当前在应用程序窗口屏幕上呈现的内容的类。这存储在变量“rootViewController”中。下面的输出显示该类的名称是“ViewController”。

如果您查看应用程序文件 – 您可以看到这种情况,因为rootViewController的名称确实是“ViewController”。

这很重要,因为现在我们知道在当前窗口中呈现的内容是在 ViewController 类和头文件中管理和定义的!

步骤5:使用otools和pagestuff分析应用程序

现在我们知道当前的keyWindow是在“ViewController”类中定义的 – 我们的下一步是查看该类并找出关键内容,例如:

  • 我们可以操作任何有趣的方法/函数吗?
  • 有什么有趣的变量我们应该注意吗?

所有这些信息将定义我们如何继续调试应用程序。

我们将运行的第一个命令行工具是pagestuff – 这使我们能够一窥Mach-O文件的内部。如上所述,pagestuff是一个很好的工具,可以让我们深入了解以下事情:

  • File headers
  • Sections
  • Segments
  • Symbol tables
  • Code signatures

运行下面的命令,我们可以直接看到我们感兴趣的“ViewController”类中的变量和方法名称。Apple鼓励iOS开发人员为方法,类和变量提供“meaningful(有意义的)”名称,以便您了解它们是什么。我相信大多数开发人员都被告知,无论语言如何:P

但是,我们可以立即在名为 ViewController 的类中看到变量定义:

  • Label
  • setLabel
  • secret

也许更有趣的是一个名为“isValidPin”的方法。

为了进行一些分析,让我们来看看otools输出。要运行 otools,您可以编写以下命令:

下面的输出还显示了对这些变量和方法的引用,包括由Objective-C定义的“isEqualToString”实例方法。这可能是比较输入密码的地方!有关此“isEqualToString”方法的更多信息,请参阅iOS文档:https://developer.apple.com/documentation/foundation/nsstring/1407803-isequaltostring

步骤 6:设置调试服务器和 LLDB 准备进行调试

我们将使用免费工具进行此调试。为此,我们将使用调试服务器和LLDB。您可以通过SSH或Cydia安装“debugserver”来设置它。其工作方式是,我们将通过LLDB远程连接到breakmedaddy应用程序进程,以执行应用程序的远程调试。

下面的屏幕截图显示了如何执行此设置。左边的屏幕是我的iOS设备,右边的屏幕是我的Mac终端,我正在远程连接到该过程。

步骤7:在我们确定的有趣方法“isValidPin”上设置断点之前从 otools 和 pagestuff 输出中,我们发现了一个名为 “isValidPin” 的有趣函数。让我们通过在此函数上设置断点来更深入地研究这个问题。如何在LLDB中执行此操作的方法如下面的屏幕截图所示。基本上,您传入类(ViewController)的名称,后跟方法“isValidPin”的名称。

步骤 8:在应用程序中键入密码以触发断点

现在,我们需要手动与应用程序交互,以触发发生检查的函数处的断点。

步骤 9:遍历汇编代码并确定感兴趣的函数调用现在我们命中断点,让我们看一下程序集输出并找出发生了什么!

上面的输出显示,我们已在黄色箭头所在的地址处命中了断点。我用紫色突出显示了一行,这对我们来说非常有趣。如您所见 – 即将到来,有一个名为“objc_msgSend”的函数的调用 https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend。此函数在 Objective C 中用于将消息发送到类的实例。

这条线之所以有趣,是因为当你在反转某件事时,重要的是理解程序中的数据流。这个函数即将出现,它与我们刚刚输入到应用程序中的当前密码进行交互,然后才在“isValidPin”上命中断点。数据流很重要,因为在这种情况下,反转的全部意义在于修改我们输入的数据如何通过应用程序中的各种方法和函数“看到”或“响应”。在我们的例子中,我们并不真正关心密码是什么,我们只想修补并绕过密码检查。因此,重要的是我们要在此行上设置一个断点,以便我们可以了解正在对输入执行的操作。因此,让我们在此函数调用上放置一个断点并继续该过程,直到我们命中该函数调用“objc_msgSend”。

步骤 10:查看寄存器中存储的内容

当我们在iOS文档中查看“objc_msgSend”函数时,我们可以看到它需要三个函数参数:

  • 指向接收消息的类的实例的指针
  • 处理消息的方法的选择器
  • 包含方法参数的参数列表

这三个参数按相应的顺序存储在 x0、x1、x2 中。

为了进一步深入研究这一点,让我们仔细看看每个寄存器中存储的确切内容。

如上所示,这些是传递到“objc_msgSend”函数中的三个参数:

  • $x0 – 我们放入应用程序中的密码“:)密码”
  • $x1 – 对方法“isEqualToString”的调用,其中将在x0和x2中存储的字符串之间进行比较
  • $x2 – 程序期望“婴儿撒儿”的实际密码

此时,您可以停止LLDB和调试服务器,只需使用真实密码即可。但是,让我们更进一步,修补程序!

步骤11:修补应用程序并检查其是否正常工作

跳回到程序组装中,我们可以看到我们仍然处于“objc_msgSend”的断点。

如上图所示,您可以从“objc_msgSend”函数中推断出,如果字符串不匹配,则方法“isEqualToString”将返回“0”,如果字符串匹配,则返回“1”。在我们的立场中,它将设置为“0”,因为我们的密码不是真正的密码。但是,如果我们输入了正确的密码“babysareevil”,这将设置为1。

让我们将断点设置在上面的TBZ指令屏幕截图中突出显示的下一行。在这里,我们应该期望看到寄存器设置为0,因为我们的密码不正确。

继续该过程后,让我们再次查看寄存器,因为我们应该已到达此断点。如下图所示,x0 的值为“0”,正如预期的那样。

如果您不熟悉 ARM 说明,只需使用此处的 ARM 开发人员参考指南查找它们:https://developer.arm.com/documentation/dui0802/a/A64-General-Instructions/TBZ

步骤12:修补应用程序!瞧!本垒打时间。现在我们确切地知道在哪里修补此应用程序。我们知道,如果$x0是“0”,密码将失败,因为它失败了“isEqualToString”方法调用,我们知道如果$x1是“1”,密码将成功。因此,让我们修补$x0以保存值 1:

最后,让我们看一下我们的应用程序,现在它呈现为SUCCELL,无论我们输入了什么密码,哈哈!:)

进一步学习链接

此博客旨在作为反转iOS应用程序并对其进行修补的介绍性演练。如果这有帮助,请告诉我,这样我就可以弄清楚我是否应该花更多的时间写这样的内容!

以下是一些有用的链接,供您进一步学习:

使用Cycript入侵iOS:https://docs.huihoo.com/rsaconference/usa-2014/hta-r04a-hacking-ios-on-the-run-using-cycript.pdf

Cycript Tricks: https://iphonedev.wiki/index.php/Cycript_Tricks

ARM 组装:https://developer.arm.com/documentation/dui0802/a/A64-General-Instructions/TBZ

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部