原文链接:https://www.inversecos.com/2022/06/how-to-reverse-engineer-and-patch-ios.html
所以你想反转和修补一个 iOS 应用程序?我得到你>_<
这个博客的重点是逆向我为向初学者展示如何逆向和修补 iOS 应用程序而构建的 iOS 应用程序。不需要花哨的工具(IDA Oo),只有你、我和调试器 <3
该应用程序是一个简单的、未加密的 Objective-C 应用程序,它只接受密码,其目的是绕过密码机制并获取成功代码。这篇博文将专注于逆向/调试应用程序,不会涉及静态分析的各个方面。我想写这篇文章的原因是因为我意识到这个话题让很多人感到困惑,我想尝试写一个博客,试图以一种对初学者更友好的方式来解释它。
最初,我将此内容计划为 TikTok 视频,但我厌倦了 TikTok 的社区准则和针对任何“攻击性”安全内容的规则。所以……因此,我现在可能会写更多的博客。
下面的屏幕截图显示了我可爱的应用程序的外观——它被称为“breakmedaddy”,左边显示你试图输入密码——右边显示你想要的绕过屏幕。
为什么我用 Objective-C 而不是 Swift 来构建它?因为你的女孩是一个 qt 和一个受虐狂:)!但也因为 Objective-C 允许您在运行时修改方法,这意味着它比使用 Swift 构建的应用程序更容易挂钩函数。
如果您想关注这篇博文,可以在我的GitHub 上找到该应用程序(我第一次公开上传,因为我很害羞>_<):
为了使这尽可能容易理解,我将博客分为三个部分,以便您可以跳到您感兴趣的任何部分:
- 我们将采取的高级步骤(这是为了演示如何执行此类操作背后的逻辑)
- 我们将用于分析的工具
- 逆向工程和修补应用程序
高级步骤
这是我们将在本教程中遵循的逻辑:
- 越狱 iOS 设备(我使用的是运行 iOS 14.1 的旧手机)。
- 通过将 IPA 文件解压缩到 /Applications 目录,通过 SSH 和 SFTP 将应用程序上传到越狱设备。
- 重新启动 Springboard(您可以通过 CLI 通过 SSH 或通过在 Cydia 中找到的 Respring 执行此操作)。
- 打开应用程序并在手机解锁的情况下将其保持在前台
- 使用 SSH 查找应用程序的 PID。
- 通过使用 Cycript 连接到正在运行的应用程序进程,以允许您从运行时查看实例。
- 使用 otool 查看应用程序中存在的库。
- 使用 pagestuff 查看 Mach-o 文件的结构(这将显示段、标题、代码签名、符号表等)。
- 找到任何有趣的方法名称和变量名称。
- 通过在 iOS 设备上运行 debugserver 并通过 LLDB 连接到正在运行的应用程序进程来反向和调试 Mach-O 文件。
我们将使用的工具
您可以使用很多工具来执行此操作,但是,为了保持本演练精益求精,以下是我们将使用的分析工具:
- Otool 命令 – https://www.unix.com/man-page/osx/1/otool/
- Pagestuff 命令 – https://www.unix.com/man-page/osx/1/pagestuff/
- Cycript – http://www.cycript.org/
- LLDB – https://lldb.llvm.org/
逆向工程IOS指南
第 1 步:越狱 iOS 设备并设置 SSH、SFTP
我不打算介绍如何越狱设备,因为有很多关于它的文章和视频。我测试并构建应用程序的手机运行的是 iOS 14.1,我在这里使用了 unc0ver 越狱: https ://unc0ver.dev/ 。unc0ver 的网站详细解释了如何执行越狱:)
越狱完成后,进入 Cydia 并设置 SSH。这就像搜索“openssh”并安装它一样简单。
步骤 2:通过 SFTP 将应用程序上传到越狱设备并解压缩到 Applications 目录 您可以通过将文件 SFTP 到您的 iOS 设备 /Applications 目录然后解压缩来执行此操作。该文件的格式是在 XCode 中构建的“.ipa”文件。.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 参数,它是指向 AppDelegate 类的“appDelegateClassName”变量。此类定义如何对应用程序中触发的某些事件做出“反应”,即 iOS 如何处理与应用程序相关的重要“触发器”。
运行 Cycript 的目的是更深入地了解应用程序的内部结构及其运行方式,以便我们知道如何“绕过”密码检查。我们在这里要做的是专注于理解应用程序代码的“层次结构”或“布局”。我们将在不查看源代码的情况下将其视为黑盒。 例如,要找出委托类的名称——您可以运行“UIApp.delegate”,如下所示,它返回了委托类的名称“AppDelegate”,这就是它在我的应用程序中所调用的名称。
下一步是找出管理当前在应用程序窗口屏幕上呈现的内容的类。这存储在变量“rootViewController”中。下面的输出显示类的名称是“ViewController”。
如果您查看应用程序文件 – 您可以看到情况就是这样,因为 rootViewController 的名称确实是“ViewController”。
这很重要,因为现在我们知道在当前窗口中呈现的内容是在 ViewController 类和头文件中管理和定义的!
第 5 步:使用 otools 和 pagestuff 分析应用程序 现在我们知道当前的 keyWindow 是在“ViewController”类中定义的——下一步是查看该类并找出关键内容,例如:
- 我们可以操作任何有趣的方法/功能吗
- 是否有任何有趣的变量我们应该注意
所有这些信息将定义我们如何进行应用程序的调试。 我们将运行的第一个命令行工具是 pagestuff——这让我们可以窥探 Mach-O 文件的内部结构。如上所述,pagestuff 是一个很好的工具,可以让我们深入了解以下内容:
- 文件头
- 部分
- 细分市场
- 符号表
- 代码签名
运行下面的命令,我们可以直接看到我们感兴趣的“ViewController”类中的变量和方法名称。Apple 鼓励 iOS 开发人员为方法、类和变量赋予“有意义”的名称,以便您了解他们是。我敢肯定大多数开发人员都被教导,无论语言如何:P但是,我们可以立即在 ViewController 类中看到变量定义,名为:
- 标签
- 设置标签
- 秘密
也许更有趣的是一个名为“isValidPin”的方法。
为了进行一些分析,让我们看一下 otools 的输出。要运行 otools,可以编写以下命令:
下面的输出还显示了对这些变量和方法的引用,包括由 Objective-C 定义的“isEqualToString”实例方法。这很可能是对输入密码进行比较的地方!有关此“isEqualToString”方法的更多信息,请参阅 iOS 文档:
https ://developer.apple.com/documentation/foundation/nsstring/1407803-isequaltostring
第 6 步:设置调试服务器和 LLDB 以进行调试我们将使用免费工具来进行此调试。为此,我们将使用 debugserver 和 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 – 程序期望的实际密码“babiesareevil”
此时您可以停止 LLDB 和调试服务器并使用真实密码。但是,让我们更进一步,为程序打补丁!
第 11 步:修补应用程序并检查它是否有效跳回程序集,我们可以看到我们仍然在“objc_msgSend”的断点处。
如上图所示,您可以从“objc_msgSend”函数中推断,如果字符串不匹配,“isEqualToString”方法将返回“0”,如果匹配则返回“1”。在我们的立场,它将被设置为“0”,因为我们的密码不是真正的密码。但是,如果我们输入正确的密码“babiesareevil”,它将被设置为 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 的值:
最后,让我们看看我们的应用程序,无论我们输入什么密码,它现在都呈现为 SUCCESSFULLL 哈哈!:)
更多学习链接本博客旨在介绍逆向 iOS 应用程序并对其进行修补的演练。请让我知道这是否有帮助,以便我确定是否应该花更多时间编写这样的内容! 以下是一些有助于您进一步学习的有用链接: 使用 Cycript 破解 iOS:
https ://docs.huihoo.com/rsaconference/usa-2014/hta-r04a-hacking-ios-on-the-run-using-cycript.pdfCycript 技巧:
https ://iphonedev.wiki/index.php/Cycript_TricksARM 组装:
https ://developer.arm.com/documentation/dui0802/a/A64-General-Instructions/TBZ