最近,关于 @SteipeteRadar发布的帖子,笔者看到很多人在问「你是怎么理解那个伪代码的」。笔者想写博客已经有一段时间了,现在正好就此发表第一篇博文。笔者在一个叫 Hopper 的工具上花了很多时间(这是笔者的必备工具之一),虽然它很神奇,但是刚接触的时候可能会让人感觉不知所措。本篇博文的目的是帮助那些回避或不熟悉逆向工程的人填补知识空白。

你是否曾经疑惑,别人是怎么获取下图所示的私有 API伪代码的?这实际上很简单,而且是找出 UIkit中那些烦人错误的好方法。使用 Hopper 这样的工具后,只需要点几下鼠标,就能得到伪代码。更酷的还在后头。有了 Obj-C runtime 和 lldb,即使不能提高伪代码的语法正确性,也一定能提高伪代码的可读性!让我们深入探讨一下吧!

Decompilation of a method in UIKit.
在 UIKit 中反编译一个方法。

什么是 Hopper?

摘自 Hopper 主页的定义:“Hopper 是一种适用于 OS X 和 Linux 的逆向工程工具,可以用于反汇编、反编译和调试 32位/64位英特尔处理器的 Mac、Linux、Windows 和 iOS 可执行程序!”用更简洁的话来说,这代表我们可以用一个编译二进制(你的 iOS app,UIKit 二进制等等)生成你之前看到的伪代码!

反汇编 vs 反编译

反汇编和反编译有什么区别?很简单,反汇编(通过反汇编程序来实现)是指将 opcode (二进制原始字节)转化成对应的汇编指令(也叫 mnemonics)的过程。下图展示了一个被反汇编的文件。反编译(通过反编译程序来实现)是指将该汇编指令转化成伪代码的过程。下图分别为同一个文件的反汇编结果与反编译结果。

在本文发表时,Hopper的售价只有90美元,这简直就是白送。对那些了解这个工具的威力的人来说,这个工具完全可以卖到几百美元。因此,如果你觉得这个价格贵,再好好考虑一下吧!他们也提供功能受限的免费试用版本,不过用来了解本文内容应该够用了。

开篇

在下载并安装 Hopper 之后,打开并按顺序点击 “File -> Read Executable to Disassemble...

点击 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:]”的反汇编界面。

搜索方法名称和类。

如果你不了解汇编指令,这个界面就没有太大帮助,请查看界面右上角,注意那个带有代码的按钮。这个按钮可以开启反编译过程,并产生伪代码。点下去吧

[此处输入图片的描述][1]

没那么难用,对吧??

在调试第三方 SDK 或者查看自己的 app 时,这个工具效果非常好。

一般笔者在查找程序错误时会采用以下步骤:

  1. 在代码中找出自认为导致问题的那个文件,然后打开 Hopper
  2. 搜索那个类和方法名称,然后进行反编译
  3. 通过反编译,我可以很容易就能收集方法签名,并在 lldb 中设置它们的断点(参见 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、r1、r16等等,那些是 ARM 框架。如果这些你全都看不懂,别担心,只要把它们和伪代码匹配就好了。

在 lldb 提示框中输入“register read”,按回车键。

CPU 寄存器。根据你所用的不同框架,显示不同名字。

显示出来的是 CPU 寄存器及其内容值。现在你可以用 lldb 中显示的值替代反编译结果中的寄存器。

不要盲目地更换反编译的 esi、edi 和其他寄存器,因为在执行不同代码时,它们可能代表不同的值。这就回到了明智选择关键点的重要性。笔者的操作步骤如下:

  1. 在一个关键点设置断点,继续程序执行
  2. 转储寄存器
  3. 用 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 官方博客

写给 iOS 开发者的 Hopper + lldb 简介的更多相关文章

  1. iOS 写给iOS开发者的React Native学习路线(转)

    我是一名iOS开发者,断断续续一年前开始接触React Native,最近由于工作需要,专职学习React Native也有一个多月了.网络上知识资源非常的多,但能让人豁然开朗.迅速学习的还是少数,我 ...

  2. 写给iOS开发者的React Native学习路线(转)

    我是一名iOS开发者,断断续续一年前开始接触React Native,最近由于工作需要,专职学习React Native也有一个多月了.网络上知识资源非常的多,但能让人豁然开朗.迅速学习的还是少数,我 ...

  3. 写给Java开发者的Node.JS简介

    前言 今天上推特看见这篇文章,点进去发现是新货. 正好最近想入Node的坑,又有一些Java基础,所以希望翻译出来给大家,同时也让自己加深理解. 才疏学浅,如有不妥之处请指正. 原文链接:Node f ...

  4. iOS 开发者旅途中的指南针 - LLDB 调试技术

    文章转载于:iOS 开发者旅途中的指南针 - LLDB 调试技术 今天给大家介绍的内容,无关乎任何功能性开发技术,但又对开发的效率影响至深,这就是调试技术. 何为调试呢,比如我们用 print 函数在 ...

  5. iOS逆向工程之Hopper+LLDB调试第三方App

    LLDB是Low Level Debugger的简称,在iOS开发的调试中LLDB是经常使用的,LLDB是Xcode内置的动态调试工具.使用LLDB可以动态的调试你的应用程序,如果你不做其他的额外处理 ...

  6. 一个iOS开发者的修真之路

    在微信上有童鞋问我iOS开发者的入门标准是神马?这个问题难到我了,而且贸然给一个答案出来的话,必定会有万千高手来喷. 凡人修仙,仙人修道,道人修真.当我们还是一个在青石板上蹲马步汗水涔涔的废柴时,或许 ...

  7. 一个资深iOS开发者对于React Native的看法

    一个资深iOS开发者对于React Native的看法 当我第一次尝试ReactNative的时候,我觉得这只是网页开发者涉足原生移动应用领域的歪门邪道.   我认为一个js开发者可以使用javasc ...

  8. iOS 开发者必知的 75 个工具(译文)

    原文地址:http://benscheirman.com/2013/08/the-ios-developers-toolbelt (需FQ)   如果你去到一位熟练的木匠的工作室,你总是能发现他/她有 ...

  9. 申请iOS开发者证书

    来源:http://blog.csdn.net/htttw/article/details/7939405 申请iOS开发者证书 今天我们介绍如何申请iOS开发者证书(99刀): 1. 打开 http ...

随机推荐

  1. 一个简单的Redis结合Spring MVC架构以及实现过程

    为了加快开发人员对公司项目的理解.更加容易入手和对公司项目的整体把控. 整体框架 首先介绍公司项目的整体框架,闲话少说,直接上图 整体性能分析 这就是公司的一个整体的架构,为了开发人员对架构的侧重点的 ...

  2. 第二篇、微信程序尺寸rpx

    微信小程序尺寸单位rpx以及样式相关介绍rpx单位是微信小程序中css的尺寸单位,rpx可以根据屏幕宽度进行自适应.规定屏幕宽为750rpx.如在 iPhone6 上,屏幕宽度为375px,共有750 ...

  3. Caching和Purgeable Memory (译)

    Caching和Purgeable Memory对于开发者来说是一个至关重要的资源,尤其是当我们需要处理那些需要超大内存以及计算时间的对象或者是当计算机向磁盘写入数据时导致应用程序陷入停滞时特别有用处 ...

  4. ios开发入门篇(三):UITableView简介

    最近做项目又开始用到了uitableview,温习之余,在这里把uitableview的用法分享一下,有不对的地方欢迎大家提出来. 废话不多说,先创建一个工程,由于Xcode6,去除了创建工程时的空项 ...

  5. css Hack 以及css的一些兼容问题小结

    坚持每天做总结.今天下班还算早.写个跟css兼容有关的知识点.便于后期查看与学习.一.先说说各种主流浏览器的内核 浏览器最重要或者说核心的部分是“Rendering Engine”,可大概译为“渲染引 ...

  6. 从0开始学习react(三)

    这次我们来讲解第三节知识,考虑了下,先不去讲什么理论了,毕竟网上一搜一大堆,而且理论真心看不太懂啊!!! 今天我们就直接上实例喽! 大家HIGH起来!!!(想了好久,还是没舍得删这句话) 1.根据下图 ...

  7. 笔试面试题-小米Git

    题目描述: git是一种分布式代码管理工具,git通过树的形式记录文件的更改历史,比如: base'<--base<--A<--A' ^ | --- B<--B' 小米工程师常 ...

  8. IE浏览器打开 「兼容性视图」

    有些IE上的网页控件需要打开兼容性视图才能使用,不知道是Javascript的原因,还是CSS的原因. 使用环境是用C语言配合boa服务器实现的CGI程序.

  9. Excel 数据分析技巧

    分享一个小技巧,Excel中,统计数据后,根据数据点之间的趋势,描绘出大致的曲线图,并且得到对于的公式. 1. 给出示例数据 2. 插入->散点图,右键点,选择添加趋势线,可以根据点数的走向,来 ...

  10. Qt5中使用lambda表达式

    c11新特性中加入了lambda表达式,所以Qt 也支持 需在.pro文件中加入 CONFIG += c++11 例子: QString program = "C:/Windows/Syst ...