最近,关于 @Steipete 在Radar发布的帖子,笔者看到不少人在问「你是怎么理解那个伪代码的」。笔者想写博客已经有一段时间了,如今正好就此发表第一篇博文。笔者在一个叫 Hopper 的工具上花了不少时间(这是笔者的必备工具之一),虽然它很神奇,可是刚接触的时候可能会让人感受不知所措。本篇博文的目的是帮助那些回避或不熟悉逆向工程的人填补知识空白。html
你是否曾经疑惑,别人是怎么获取下图所示的私有 API伪代码的?这实际上很简单,并且是找出 UIkit中那些烦人错误的好方法。使用 Hopper 这样的工具后,只须要点几下鼠标,就能获得伪代码。更酷的还在后头。有了 Obj-C runtime 和 lldb,即便不能提升伪代码的语法正确性,也必定能提升伪代码的可读性!让咱们深刻探讨一下吧!编程
Decompilation of a method in UIKit.
在 UIKit 中反编译一个方法。缓存
摘自 Hopper 主页的定义:“Hopper 是一种适用于 OS X 和 Linux 的逆向工程工具,能够用于反汇编、反编译和调试 32位/64位英特尔处理器的 Mac、Linux、Windows 和 iOS 可执行程序!”用更简洁的话来讲,这表明咱们能够用一个编译二进制(你的 iOS app,UIKit 二进制等等)生成你以前看到的伪代码!sass
反汇编和反编译有什么区别?很简单,反汇编(经过反汇编程序来实现)是指将 opcode (二进制原始字节)转化成对应的汇编指令(也叫 mnemonics)的过程。下图展现了一个被反汇编的文件。反编译(经过反编译程序来实现)是指将该汇编指令转化成伪代码的过程。下图分别为同一个文件的反汇编结果与反编译结果。性能优化
在本文发表时,Hopper的售价只有90美圆,这简直就是白送。对那些了解这个工具的威力的人来讲,这个工具彻底能够卖到几百美圆。所以,若是你以为这个价格贵,再好好考虑一下吧!他们也提供功能受限的免费试用版本,不过用来了解本文内容应该够用了。网络
在下载并安装 Hopper 以后,打开并按顺序点击 “File -> Read Executable to Disassemble...”app
点击 Read Exectuble to Disassemble 来开始反汇编。框架
在这里,你须要点击用于试验的二进制文件并点击“Open”。在本文中,咱们用的是如下路径的 UIKit 二进制文件:工具
</Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.1.sdk/System/Library/Frameworks/UIKit.framework性能
你可能须要更新一些目录名称,不过以上路径已经提示了大体的方向。这是模拟器使用的 x86 二进制文件,也是本文要关注的文件。ARM 二进制文件储存在设备上,而且在运行时加载。点击打开以后,你将会看到下图所示画面。UIKit 是个很肥的二进制文件,也就是说它包含了多个二进制文件。在示范的 UIKit 中,包含 x86 32位和64位。咱们将要反汇编32位的文件。确保选中该文件并点击“Next”。
注意 x86(32位) 和 x86(64位)两种文件的存在。
在接下来的页面中点击“OK”,你就会来到 Hopper 的主界面。Hopper 会开始分析 Mach-O 二进制文件,在这个过程当中,你会看到界面右下角的“Working...”状态提示。由于 UIKit 是个大文件,可能须要一段时间才能完成分析。若是是小文件,分析很快就能完成。如今,若是你以为这个界面太费解,不要担忧,咱们只须要关注整个界面中的三个按钮。
首先是左侧操做面板中的“Labels”和“Strings”。“Labels”会向你展现二进制文件中包含的全部类和方法(你还会看到其余文件,不过本教程中只须要关注方法签名)。
下拉查看全部的方法!
如今点击左侧操做面板中的“String”,你会看到 app 中全部的字符串。这就是为何你绝对不能对 app 中的重要字符串进行硬编码。
检查你本身的 app,确保没有对任何你不想让别人看到的东西硬编码!
回到“Labels”界面,注意搜索框。在这里你能够搜索任何类和方法的名称。输入“UIPopoverPresentationController dimmingView”,查看搜索结果。点击“-[UIPopoverPresentationController dimmingViewWasTapped:]”签名,注意主界面就会跳转到“-[UIPopoverPresentationController dimmingViewWasTapped:]”的反汇编界面。
搜索方法名称和类。
若是你不了解汇编指令,这个界面就没有太大帮助,请查看界面右上角,注意那个带有代码的按钮。这个按钮能够开启反编译过程,并产生伪代码。点下去吧!
此处输入图片的描述
没那么难用,对吧??
在调试第三方 SDK 或者查看本身的 app 时,这个工具效果很是好。
通常笔者在查找程序错误时会采用如下步骤:
在代码中找出自认为致使问题的那个文件,而后打开 Hopper
搜索那个类和方法名称,而后进行反编译
经过反编译,我能够很容易就能收集方法签名,并在 lldb 中设置它们的断点(参见 lldb 部分)。
以上就是所有操做,彻底没有一点删减。若是你不想再看,能够到此为止,不过笔者在工做流程中添加了 lldb 操做来对伪代码进行更进一步的清理!
首先,下载 @Steipete 最近发布在 Radar 中的示范代码。打开项目以后,笔者通常喜欢把调试构架设成“$(ARCHS_STANDARD_32_BIT) ”,由于这样会让编译过程更加友好(前提是已经从给定的二进制文件中反编译了32位文件)。
好了,通常来讲,Hopper 产生的伪代码就已经能知足你的需求了,不过有时候它会比较难懂,须要进行一些清理工做。这就到了 Obj-C runtime 和 lldb 大显身手的时候了。
首先,在模拟器中打开示范代码,经过调试暂停程序执行。暂停后,代码会转存到 lldb中,在这里,你能够给选定的任何方法签名设置断点。输入“b -[UIPopoverPresentationController dimmingViewWasTapped:]”,给“-[UIPopoverPresentationController dimmingViewWasTapped:]”设置断点,而后按回车键。调试控制台界面与下图相似:
b 是设置断点的简称。
如今继续程序执行,一旦启动“-[UIPopoverPresentationController dimmingViewWasTapped:]”,就会启动你所设置的断点。遵循示例项目中的操做指令(双击黄色区域)。
若是进展顺利,你就会看到断点启动,而后看到方法签名与对应的汇编指令。为了好玩,你能够比较一下 Xcode 的反编汇结果和 Hopper 产生的结果。它们应该基本一致。若是存在不一样,多是由于它们使用的汇编语法不一样,一个是 Intel,一个是 AT&T。若是你遇到其余状况,请看文末的“其余”部分。
就是感受挺累的,这段时间,总之,想请假,以为坐在这里,也没很大的意义。
到了这一步,你可能会由于不了解汇编而有些担忧,可是笔者要告诉你,你真的不须要懂。只要有一点儿直觉,加上反复尝试,你就能够完成任务了。换句话说,咱们如今所要作的就是让反编译过程稍微简单一些(替换寄存器等等)。这样产生的伪代码会好懂一些,不过若是你遇到伪代码难懂的状况,能够采用一样的理念对伪代码进行清理。
好了,如今该左右对照反汇编和反编译了。笔者经常使用的作法是寻找反编译的关键点,好比说调用 if 语句或者方法的时候。这些关键点对应的编译指令很容易猜到,只须要逐行浏览编译。若是你的关键点是一个 if 语句,就去找测试或 cmp(compare)指令。
在本文中,笔者选的是反编译中的第一个 if 语句,并在 Xcode 的反编译结果中搜寻测试或 cmp(compare)指令。以下图所示,笔者找到了一个测试指令。
可能须要尝试几回,要有耐心!
如今在那个内存地址(你的地址可能不一样)用“b 0x148b95c”设置一个断点。
继续程序执行,期待你的断点被启动。
下一步就会见证 lldb 和 Obj-C runtime 的神奇之处。咱们要清理反编译结果中大部分艰涩难懂的部分。若是你不熟悉反编译过程当中的 eax、edi、和 esiare,它们就是 x86 CPU 寄存器,咱们能够把它们转存到 lldb 中。若是你看到 r0、r一、r16等等,那些是 ARM 框架。若是这些你全都看不懂,别担忧,只要把它们和伪代码匹配就行了。
在 lldb 提示框中输入“register read”,按回车键。
CPU 寄存器。根据你所用的不一样框架,显示不一样名字。
显示出来的是 CPU 寄存器及其内容值。如今你能够用 lldb 中显示的值替代反编译结果中的寄存器。
不要盲目地更换反编译的 esi、edi 和其余寄存器,由于在执行不一样代码时,它们可能表明不一样的值。这就回到了明智选择关键点的重要性。笔者的操做步骤以下:
在一个关键点设置断点,继续程序执行
转储寄存器
用 lldb 生成的内容值替换反编译结果中关键点以上的缓存器
举个例子,咱们的关键点是 dimmingViewWasTapped 方法中的第一个 if 语句,一旦断点被启动,转储寄存器,替换伪代码中 if 语句以上的缓存器。若是你跟踪伪代码,发现关键点以后的缓存器未重置,那就更新这些内容值。
若是你进行这个操做,就会发现 edi 包含委托选择器,可是 esi 寄存器包含一个 hex 值。这就更加费解了,不过幸亏咱们能够利用 Obj-C runtime来搞清楚 esi 究竟是什么。
复制 esi 的内存地址,输入“po [0x78657dd0 class]”,而后按回车键。
Very nice!
太棒了!
啦啦啦,如今咱们知道 esi 是什么了,而且能够利用这个值来提高反编译效果。
笔者发现反编译说“esi = self”,大家可能已经推断出esi = UIPopoverPresentationController,可是这个推断不必定老是成立。并且,若是还不明显,你能够尝试“po [0x78657dd0 anyMethodThatThisClassImplements]”。若是你对某个对象的内部很感兴趣,这个操做效果超好。
到了这一步,再说下去就会变成选个新要点的重复说教了,因此笔者打算见好就收了!若有任何问题或反馈,请在推特上联系笔者 @bartcone。
有 Hopper 的替代工具吗?
有的,就是 IDA Pro(HexRays 是他们的反编译器),不过除非你想一掷千金,否则 Hopper 就是你最好的选择。他们还提供免费版本,不过只有反汇编器。
个人反编译和反汇编结果的缓存器显示的是 r,不是 e
你反编译或者反汇编了 x86 64位文件。
OneAPM Mobile Insight 以真实用户体验为度量标准进行 Crash 分析,监控网络请求及网络错误,提高用户留存。访问 OneAPM 官方网站感觉更多应用性能优化体验,想阅读更多技术文章,请访问 OneAPM 官方技术博客。
本文转自 OneAPM 官方博客