看了下看雪的帖子分布,Android 帖子比 iOS 的帖子多了五六倍啊。本人之前也是在 Android 平台下进行分析比较多,因此这也算面向 Android 安全工程师的一个 iOS 入门介绍吧。本文以微信扔骰子小游戏为例,详细记录了一次完整 iOS 逆向分析的过程,希望能对大家有所帮助。
本文主要讨论的是应用安全,因此关于越狱实现的部分不做深入介绍。关于XNU内核漏洞的分析和利用网上有很多相关的文章或书籍,比如:
看一些公开的漏洞利用代码对加深印象也很有帮助,比如oob_timestamp的利用。对于本文而言只需要站在巨人的肩膀上使用这些封装好的利用即可。iOS越狱和Android root的的一个很大不同是前者系统封闭性高,碎片化较低,因此提权的方法也相对单一,不支持刷机,大部分都是通过漏洞去获取更高的权限(tfp0)。既然是专注应用层安全,就抓大放小,使用现有的越狱工具,站在前人肩膀上即可。
越狱之后总算可以通过 ssh 进入到系统中,也就是相当于安卓世界的 adb shell
而已。既然是逆向分析,下一步就是获取应用的安装包,这在安卓中是一条adb pull
命令,但苹果里要复杂一些。
iOS 中大部分应用都从 Apple Store 即应用商店下载,而从应用商店下载的 app 是通过苹果签名和加密保护的,这是苹果 FairPlay DRM 数字版权保护的重要部分。为了能使用逆向工具进行分析,需要先对其进行解密,即俗称的砸壳。网上关于砸壳的资料和工具都很多,比如:
这里面有些工具已经年代久远年久失修了,比如我就注意到 dumpdecrypted 在文件沙盒的处理上有点问题导致无法保存文件,需要经过简单 patch 。
上面的这些工具大部分都是基于内存 dump,也就是需要通过 exec 执行目标程序才实现砸壳,这可能会让一些 App 通过在初始化函数中自我检测来对抗砸壳。如果出现这种情况,可以通过基于 mremap_encrypted 的方式进行解密,直接调用内核接口,无需启动目标程序实现静态砸壳。
狱也越了,壳也砸了,接下来呢?直接丢到 IDA 里分析?不是不可以,但 iOS 的应用主体是一个巨大的 mach-o 文件,直接分析是很难的。还是以微信为例,主程序解密后单 arm64 架构的可执行文件就有 218 MB,即便在逆向工具里分析也会让人无从下手。这时候就需要一个入手点,用互联网的黑话来说,就是需要一个抓手。
一个最常见的入手点就是 UI 界面。在安卓应用分析中一般通过 Android Studio 提供的uiautomatorviewer
可以进行目标应用的 UI 分析,并通过 UI 的 ID 在反编译的代码中查找引用。不过这个方法我一般不用,而是直接获取顶层 Activity 然后找对应的类去分析。
在 iOS 中,UI 分析却是一个有效的入手点,因为 iOS 应用都是基于 MVC 结构,View 中触发的事件由对应的 Controller 去实现。不管是基于 Springboard 的拖拖拽拽还是通过代码布局,MVC 的基调是不变的。在 Xcode 中有视图层级调试功能(Debug View Hierarchy
),但需要目标开启调试。除此之外,更为常用的是一些视图调试框架,比如 Reveal 或者开源的 FLEX。
用 FLEX 直接注入到进程中可实现 UI 的分析:
这里用到了 cycript,后文再细说。或者可以用 Reveal 在电脑端分析,当然还是需要将 RevealServer.dylib
注入到目标进程中并调用[IBARevealLoader startServer]
,参考官方的 lldb 脚本,启动后 PC 端界面如下:
点击发送骰子的区域是一个 UIImageView,其本身是没有响应点击事件的,因此要找对应的 ViewController 或者 Gesture Recognizer。
根据 UI 信息能知道的是当前界面的 Controller 类是 BaseMsgContentViewController
,所以我们可以通过脚本去跟踪发送骰子时该类所有的函数调用,如下所示:
其中SendEmoticonMesssageToolView
是个值得关注的函数,可以直接用 frida 打印函数堆栈:
输出结果如下:
在 WeChat 部分的回溯没有符号,因为已经去掉了。
在 lldb 中也可以看到类似的结果:
有符号更好,没有也无所谓,我们还是可以通过地址在逆向工具中查看代码。不过考虑到 ASLR 的存在,需要确认一下镜像的加载地址:
vmmap
是我自己定义的一个 lldb 命令,参考了 gef 和 pwndbg 在 gdb 中的实现
这里加载地址是 0x000102790000
,而在 IDA 中 text 段的加载地址是 0x100004000
,可以在后者进行 rebase,但是因为函数不多,所以先手动查找。另外用 lldb 也可以直接查看对应函数地址的偏移:
根据 lldb 中每个函数地址在 TEXT 段中的偏移,可以在 IDA 中直接跳转到对应函数(返回值),整理对应的调用函数分别是:
既然 IDA 可以识别出对应 OC 函数的符号,那么理论上这些符号也是可以还原的,如果经常需要进行动态分析,那么可以通过一些方法自动化恢复对应的符号,可以参考 iOS符号表恢复 以及 restore-symbol 等项目。
通过动态分析找到了着手点,以及通过回溯调用栈找到了一些相关函数,其中并没有一目了然的设置骰子点数的函数 setDiceValue
,所以要想改点数还需要对上面的函数进行进一步分析。到这里,逆向工程是不可避免了,一般而言我是能躺着绝不坐着,能坐着绝不站着,不到万不得已是不去逆向的。
花开两朵,各表一枝,在前面砸完壳的第一时间我就预感到需要逆向,所以早早地把 200MB 的 MachO 丢进了 IDA,在文章写到这里时已经过去了四个小时,期间看了两集动漫,但是 IDA 还是没有全部分析完。所以在等待的时间里,先来简单介绍些 ObjectiveC 的底层实现。
在学习 OC 的时候,总会看到说 OC 语言实现面向对象是通过发送消息,具体是怎么发送呢?因此在实际逆向之前,我们先来自己写一个程序来进行分析,以便了解 OC 的调用原理。
以下面简单的 helloworld 为例:
主要调用了两个方法,一个类方法和一个成员方法,编译后查看其汇编代码如下:
且不管初始化部分,后面两个函数调用最终都进入了 objc_msgSend 函数,在苹果官网可以看到其函数原型:
其中各个参数为:
回到上面的例子,第一次类方法调用的参数为:
第二次成员方法的调用参数为:
程序的输出如下:
所以,ObjectiveC 在实现上还是挺接近 C 语言的,这对于我们逆向而言方便很多。
了解了简单的 OC 逆向之后,IDA 也跑的差不多了。根据栈回溯对应 OC 函数的名称,逐级往上看。首先是 didSelectorSelfDefinedEmotcion
这个函数,表示选中了某个自定义表情,里面只是一些发送操作,所以并不是我们想要的;
然后是 -[EmoticonBoardView onTapEmoticonWrap:atIndex:maxCountPerLine:fromSection:]
,该函数中主要是判断所选择的表情是否为自拍表情或者自定义表情,如果是的话就进行异步上传。其调用didSelectorSelfDefinedEmotcion
的参数为 a3,即一个对象指针,虽然还不知道是哪个对象,但知道其包含这些属性(方法):
在 class-dump 导出的头文件中搜索,可以发现是CEmoticonWrap
类:
该函数主要是执行上传并记录一些信息,更新一些 emojiInfo 的字段,所以还是要继续往上走;
接着是-[EmoticonBoardView onTapEmoticonWrap:atIndex:maxCountPerLine:fromSection:]
,该函数主要是作为 delegate 进行转发,没有什么实际功能。但是从名字可以猜测自定义表情栏是通过不同的行实现,而每行中每个表情又对应一个 Cell。
按照回溯的堆栈都看完了,也没有发现和骰子相关的代码。不管怎样,骰子的点数总归是在设置骰子图片之前确定的,而根据上面的逆向可以知道,自定义表情的图片应该是定义在 [CEmoticonWrap m_emojiInfo]
中,该类的属性如下:
接下来还是要通过结合动态分析来确定骰子图片加载的时机。
还是回到SendEmoticonMesssageToolView
函数,通过逆向可以得知其参数为CEmoticonWrap
类型,修改前面打印栈 callback 的脚本,令其打印参数的各个字段,如下:
也就是说进入函数之时这些参数都是空的,m_emojiInfo 的 md5 为 “dice_emoticon_md5”,此时骰子图像并没有真正生成,所以印证了我们之前的猜想,即实际的操作还在后面。那么栈回溯是不是白看了?也不尽然,至少至少了参数格式和调用流程(逆向嘛,总是要学会安慰自己)。
回过头来继续看这个函数:
又是一个 delegate 模式,直接打印出实例:
结果如下:
是个 WeixinContentLogicController,那就继续找它的SendEmoticonMessage:
方法,没有的话就在父类或者 Category 里找。因为程序太大,我的破电脑在 IDA 里搜索比较卡,所以有个小技巧是直接通过函数名跳转,对应的 IDA 函数为名为 -[WeixinContentLogicController SendEmoticonMessage:]
,当然实际并没有这个函数,而是在父类中定义的 -[BaseMsgContentLogicController SendEmoticonMessage:]
。
在上面高亮的一行,IDA 反编译的结果有点问题,显示 V7 是未初始化的,遇到这种情况直接看汇编即可,汇编是不会骗人的:
即第一个参数实际上是 PluginUtil 类的地址,这里调用的则是类方法+[PluginUtil isPluginUserName:]
。其中 m_uiGameType
前面已经通过 frida 打印出来了,其值是 2,继续动态分析得知进入 else 分支。按照这种动静结合的策略,最终分析得到向前的调用过程:
最终设置内容如下:
看到这逻辑应该很明显了,如果游戏类型为 1,则有:
如果游戏类型为 2,则有:
由于我们选的是扔骰子,游戏类型为 2,骰子的 content 取值范围为 4 ~ 9
,正好对应 6 个点数;有理由猜测前面一个 3 个点数的应该就对应剪刀石头布游戏。
这里有两个值得注意的点,一是 random 函数调用了两次,但是后面一次的结果并没有使用,因此目的可能通过减少随机数连续性增加破解伪随机序列的难度;另外一个点是随机后的内容经过了 md5,所以修改点数的时候可能要一并修改哈希,否则会导致校验失败,或者直接修改 random 函数的返回值也是可以的。
实现效果如下:
至此,修改骰子点数的核心功能就分析完毕了。
说实话最近听 hook 这个词已经听到腻了,不管是 plt hook 还是 inline hook,其本质都是在运行时修改现有的代码,进而达到修改控制流的目的。对于二进制汇编代码的修改大同小异,不过对于 iOS 程序而言其实也有其他选择,下面逐一介绍:
这是最为通用的 hook 方式,即在指定地址前增加跳转指令,保存现场并跳转到用户指令,执行完后还原现场并返回执行。inline hook 可以实现汇编指令级别的 trace,也是大部分动态跟踪工具实现的基础。下图为 frida-gum 实现 inline hook 的的大致流程:
当然原理归原理,要基于公式实现核弹在工程上还要做很多工作,比如代码段通常是不可修改的,因此需要 mmap 拷贝一份到自己的地盘去进行修改;另外保存和恢复现场的操作大多只能用汇编写,因此要实现跨平台还要针对不同指令集进行测试等等。这一切繁重的工作别人都已经为我们做好了,我们需要做的就只是调用一下 Interceptor.attach
,不得不感慨生活真幸福。
前面我们说过,Objective-C 实现面向对象是基于消息的发送,那么针对 Objc 的调用,我们其实可以动态的修改某个 selector 对应的实现,这个过程就称之为 method swizzling。苹果官方也提供了对应的 API:
该方法相当于下面指令的原子实现:
还是那句老话,原理是简单的,实现是复杂的。现实中很少这样直接交换两个方法实现,而是在之前进行一些必要的检查,如下所示:
直接交换方法是危险的,因为如果类中没有实现该方法,那么 class_getInstanceMethod 会返回某个父类中的 Method 对象,实际交换的则是父类的方法,这样其他父类和子类的调用就会出现意想不到的问题。因此上面的代码会判断 selector 方法不存在的情况。
另外还要注意的是 selector 命名冲突问题、重复 swizzling 问题以及 _cmd
被修改的问题等等,因此也就有了 RSSwizzle 或者 jrswizzle 这些完善的实现方案。对于开发人员来说这是个需要仔细考虑的问题,毕竟这影响了程序的稳定性。
值得一提的是,frida 中对于 Objc 也提供了基于 method swizzling 的 hook 方案:
在 Java 中,我们可以通过 java.lang.reflect.Proxy 基于反射机制来实现方法的动态代理,这种方式在很多地方被应用于 AOP 即切面编程。而 Objective-C 也有类似的方式,即 NSProxy,该类的定义如下:
可以看到其遵守 NSObject protocol,并且第一个 ivar 是 isa 指针,因此可以当成是一个 NSObject 或者其派生类使用,但是它并不是一个 NSObject 的子类。使用上只需要实现两个方法:
NSInvocation 封装了一个方法调用的全部信息,包括调用参数和返回值。一个示例实现如下:
输出:
直接用我们的 MyProxy 对象替换了原始的 NSURL 对象,这样 NSURL 对象中所调用的所有方法都由我们的 Proxy 对象来进行代理,因此可以在执行原始函数之前以及返回之前执行我们自己的操作,从而实现 hook 的功能。
补充一些前面没有提到的东西。
Mach-O程序是支持多架构的,例如同一个程序既能运行在arm32位也能运行在64位机器上。多架构(multiarch)的应用通常比原程序体积要大,但是比两个单架构的程序要小,因为多架构应用会共享资源。
iOS应用开发者为了保证兼容性通常会选择同时支持armv7和arm64,但对于逆向分析却不必要。因此,我们可以先对其进行瘦身(thin),只保留arm64架构即可。首先查看应用是否为多架构:
只保留arm64,可以使用lipo或者ditto,二者都是苹果自带的:
输出的文件是一样的。
前文中有用到 cycript 来注入动态库进行 UI 界面分析,这里再补充一下。cycript 由 saurik 大神开发,在 JavaScript REPL 中支持 ObjectiveC 的混合语法,使用起来比较直观。比如:
支持直接调用 c 函数,并且直接使用 enum 常量:
前面使用 FLEX 进行 UI 分析就是直接通过 dlopen 注入动态库实现的(其实注入 Reveal 也是)。cycript 的功能很强大,但是文档不多,主要是参考官方的 manual。一些参考网站如下:
在越狱 iOS 中有类似于 Andoid Xposed 的 patch 框架 Cydia Substrate,其提供了 C API 给越狱 APP 的开发者来实现对特定方法的动态修改,还提供了基于 plist 的声明文件,让开发者可以选择指定的 APP 进行 hook,同时也很好地处理了同一个方法被 Hook 多次的问题。基于 Substrate 开发的 dylib 称为 Tweak,由 Substrate 根据 plist 在应用启动时选择进行注入。
一个 hook 示例如下:
如果觉得这样写还是太麻烦,可以使用 Logos 来简化代码,这是个基于预处理库的组件,包含一系列以%
开头的宏方便编写 hook 代码,示例如下:
Logos 现在是 Theos 组件的一部分,可以通过 NIC 工程模板系统快速地创建一个基于 Makefile 的 Tweak 项目。初学者可以通过参考一些 开源的 Tweaks 来熟悉开发流程。
LLDB是个功能十分强大的调试器,在有些场景下比GDB还要好用。关于GDB命令可以参考GDB调试笔记,而GDB命令对应的LLDB命令可以参考GDB and LLDB Command Examples,这里只介绍一些常用的例子:
在一个ObjectiveC类中的所有方法下设置断点(正则表达式):
打印 Objective-C 对象:
查看各个模块的映射地址(target module list):
设置zero flag
,常用于修改跳转指令:
更详细的功能以及 lldb 脚本的编写可以参考下面的链接:
https://lldb.llvm.org/use/tutorial.html
https://lldb.llvm.org/use/python-reference.html
本文以微信扔骰子小游戏为例,介绍了一次完整的 iOS 逆向分析过程。以最初的越狱开始,分别介绍了砸壳、UI 分析、动态分析和静态分析的具体操作,最后还介绍了 iOS 中常见的 hook 方案以及一些越狱开发相关的工具。之前分析过 Android 中微信的扔骰子,里面大部分代码都经过了混淆,相比之下 iOS 中逆向代码就直观很多。现今很多应用都是同时支持两个平台,在一个平台中受阻可以切换到另一个平台说不定有意想不到的收获,比如对于 Unity3D
中 C# 代码的逆向,Android 平台就比 iOS 要方便点 (因为 IL2CPP)。因此,对于有志深造的逆向工程师而言,保持灵活和开放的心态也是至关重要的。
[
-
]
13
:
04
:
24.188
Hooked:
-
[BaseMsgContentViewController didRotateFromInterfaceOrientation:]
[
-
]
13
:
04
:
24.188
Hooked:
-
[BaseMsgContentViewController previewingContext:viewControllerForLocation:]
[
-
]
13
:
04
:
24.188
Hooked:
-
[BaseMsgContentViewController previewingContext:commitViewController:]
[
-
]
13
:
04
:
24.188
Hooked:
-
[BaseMsgContentViewController previewActionItems]
[
-
]
13
:
04
:
24.189
Hooked:
-
[BaseMsgContentViewController m_delegate]
[!]
13
:
04
:
24.291
Failed to hook:
-
[BaseMsgContentViewController willShow]
[
-
]
13
:
04
:
24.291
Hooked:
-
[BaseMsgContentViewController canPasteImage]
[
-
]
13
:
04
:
24.292
Hooked:
-
[BaseMsgContentViewController setTableFooterView:]
[
-
]
13
:
04
:
24.292
Hooked:
-
[BaseMsgContentViewController documentInteractionControllerViewControllerForPreview:]
[
-
]
13
:
04
:
24.292
Hooked:
-
[BaseMsgContentViewController willAppear]
[
-
]
13
:
04
:
24.292
Hooked:
-
[BaseMsgContentViewController showLoadingView]
[
-
]
13
:
04
:
24.293
Hooked:
-
[BaseMsgContentViewController initTableView]
[
-
]
13
:
04
:
24.293
Hooked:
614
methods
for
class
BaseMsgContentViewController
[
-
]
13
:
04
:
35.075
ENTER
-
[BaseMsgContentViewController useTransparentNavibar]
[
-
]
13
:
04
:
35.076
ENTER
-
[BaseMsgContentViewController useTransparentNavibar]
[
-
]
13
:
04
:
35.078
ENTER
-
[BaseMsgContentViewController shouldInteractivePop]
[
-
]
13
:
04
:
35.078
ENTER
-
[BaseMsgContentViewController toolView]
[
-
]
13
:
04
:
35.132
ENTER
-
[BaseMsgContentViewController SendEmoticonMesssageToolView:]
[
-
]
13
:
04
:
35.148
ENTER
-
[BaseMsgContentViewController findNodeDataByLocalId:]
[
-
]
13
:
04
:
35.148
ENTER
-
[BaseMsgContentViewController addMessageNode:layout:addMoreMsg:]
[
-
]
13
:
04
:
35.148
ENTER
-
[BaseMsgContentViewController findNodeDataByLocalId:]
[
-
]
13
:
04
:
35.148
ENTER
-
[BaseMsgContentViewController getCurContentSizeHeight]
...
[
-
]
13
:
04
:
24.188
Hooked:
-
[BaseMsgContentViewController didRotateFromInterfaceOrientation:]
[
-
]
13
:
04
:
24.188
Hooked:
-
[BaseMsgContentViewController previewingContext:viewControllerForLocation:]
[
-
]
13
:
04
:
24.188
Hooked:
-
[BaseMsgContentViewController previewingContext:commitViewController:]
[
-
]
13
:
04
:
24.188
Hooked:
-
[BaseMsgContentViewController previewActionItems]
[
-
]
13
:
04
:
24.189
Hooked:
-
[BaseMsgContentViewController m_delegate]
[!]
13
:
04
:
24.291
Failed to hook:
-
[BaseMsgContentViewController willShow]
[
-
]
13
:
04
:
24.291
Hooked:
-
[BaseMsgContentViewController canPasteImage]
[
-
]
13
:
04
:
24.292
Hooked:
-
[BaseMsgContentViewController setTableFooterView:]
[
-
]
13
:
04
:
24.292
Hooked:
-
[BaseMsgContentViewController documentInteractionControllerViewControllerForPreview:]
[
-
]
13
:
04
:
24.292
Hooked:
-
[BaseMsgContentViewController willAppear]
[
-
]
13
:
04
:
24.292
Hooked:
-
[BaseMsgContentViewController showLoadingView]
[
-
]
13
:
04
:
24.293
Hooked:
-
[BaseMsgContentViewController initTableView]
[
-
]
13
:
04
:
24.293
Hooked:
614
methods
for
class
BaseMsgContentViewController
[
-
]
13
:
04
:
35.075
ENTER
-
[BaseMsgContentViewController useTransparentNavibar]
[
-
]
13
:
04
:
35.076
ENTER
-
[BaseMsgContentViewController useTransparentNavibar]
[
-
]
13
:
04
:
35.078
ENTER
-
[BaseMsgContentViewController shouldInteractivePop]
[
-
]
13
:
04
:
35.078
ENTER
-
[BaseMsgContentViewController toolView]
[
-
]
13
:
04
:
35.132
ENTER
-
[BaseMsgContentViewController SendEmoticonMesssageToolView:]
[
-
]
13
:
04
:
35.148
ENTER
-
[BaseMsgContentViewController findNodeDataByLocalId:]
[
-
]
13
:
04
:
35.148
ENTER
-
[BaseMsgContentViewController addMessageNode:layout:addMoreMsg:]
[
-
]
13
:
04
:
35.148
ENTER
-
[BaseMsgContentViewController findNodeDataByLocalId:]
[
-
]
13
:
04
:
35.148
ENTER
-
[BaseMsgContentViewController getCurContentSizeHeight]
...
import
{ kObjC }
from
"../../agent/objc"
;
import
{ kNative }
from
"../../agent/native"
;
/
/
kObjC.traceClass(
"BaseMsgContentViewController"
);
kObjC.traceMethod(
"BaseMsgContentViewController"
,
"- SendEmoticonMesssageToolView:"
, {
onEnter: function(args) {
log.i(
"SendEmoticonMesssageToolView"
, args[
0
], args[
1
]);
kNative.printStackTrace(this.context);
}
});
import
{ kObjC }
from
"../../agent/objc"
;
import
{ kNative }
from
"../../agent/native"
;
/
/
kObjC.traceClass(
"BaseMsgContentViewController"
);
kObjC.traceMethod(
"BaseMsgContentViewController"
,
"- SendEmoticonMesssageToolView:"
, {
onEnter: function(args) {
log.i(
"SendEmoticonMesssageToolView"
, args[
0
], args[
1
]);
kNative.printStackTrace(this.context);
}
});
[
-
]
13
:
10
:
32.079
Hooked:
-
[BaseMsgContentViewController SendEmoticonMesssageToolView:]
[
+
]
13
:
10
:
37.212
SendEmoticonMesssageToolView
0x11f8a1800
0x10bf6e8ba
[
-
]
13
:
10
:
37.636
backtrace:
0x104e06de4
WeChat!
0x2676de4
0x10294cde8
WeChat!
0x1bcde8
0x1041bb930
WeChat!
0x1a2b930
0x1b8331e34
UIKitCore!
-
[UICollectionView _selectItemAtIndexPath:animated:scrollPosition:notifyDelegate:deselectPrevious:]
0x1b83464b0
UIKitCore!
-
[UICollectionView _cellForItemAtIndexPath:includePrefetchedCells:]
0x1b8359e10
UIKitCore!
-
[UICollectionView touchesEnded:withEvent:]
0x1b46ef730
libsystem_malloc.dylib!nanov2_calloc
0x1b46f4100
libsystem_malloc.dylib!calloc
0x1b48ddae4
CoreFoundation!
-
[__NSSetM addObject:]
0x1b473926c
libobjc.A.dylib!objc_autoreleasePoolPop
0x1b4a14320
CoreFoundation!_CFAutoreleasePoolPop
0x1b48dd594
CoreFoundation!
-
[__NSSetM enumerateObjectsWithOptions:usingBlock:]
0x1b498bc7c
CoreFoundation!__NSSetM_new
0x1bb64e208
QuartzCore!CA::Layer::retain_parent(CA::Transaction
*
) const
0x1b8b5d9e8
UIKitCore!forwardTouchMethod
0x1b8b5dae4
UIKitCore!
-
[UIResponder touchesEnded:withEvent:]
[
-
]
13
:
10
:
32.079
Hooked:
-
[BaseMsgContentViewController SendEmoticonMesssageToolView:]
[
+
]
13
:
10
:
37.212
SendEmoticonMesssageToolView
0x11f8a1800
0x10bf6e8ba
[
-
]
13
:
10
:
37.636
backtrace:
0x104e06de4
WeChat!
0x2676de4
0x10294cde8
WeChat!
0x1bcde8
0x1041bb930
WeChat!
0x1a2b930
0x1b8331e34
UIKitCore!
-
[UICollectionView _selectItemAtIndexPath:animated:scrollPosition:notifyDelegate:deselectPrevious:]
0x1b83464b0
UIKitCore!
-
[UICollectionView _cellForItemAtIndexPath:includePrefetchedCells:]
0x1b8359e10
UIKitCore!
-
[UICollectionView touchesEnded:withEvent:]
0x1b46ef730
libsystem_malloc.dylib!nanov2_calloc
0x1b46f4100
libsystem_malloc.dylib!calloc
0x1b48ddae4
CoreFoundation!
-
[__NSSetM addObject:]
0x1b473926c
libobjc.A.dylib!objc_autoreleasePoolPop
0x1b4a14320
CoreFoundation!_CFAutoreleasePoolPop
0x1b48dd594
CoreFoundation!
-
[__NSSetM enumerateObjectsWithOptions:usingBlock:]
0x1b498bc7c
CoreFoundation!__NSSetM_new
0x1bb64e208
QuartzCore!CA::Layer::retain_parent(CA::Transaction
*
) const
0x1b8b5d9e8
UIKitCore!forwardTouchMethod
0x1b8b5dae4
UIKitCore!
-
[UIResponder touchesEnded:withEvent:]
(lldbinit) bt
*
thread
*
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
(lldbinit) bt
*
thread
*
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
frame
(lldbinit) vmmap
/
WeChat
000102790000
-
00010d6d4000
af44000 R
-
X
/
private
/
var
/
containers
/
Bundle
/
Application
/
2A5D623F
-
5F8E
-
4A99
-
96C8
-
8CBD00D8B6BE
/
WeChat.app
/
WeChat
0
__TEXT
00010d6d4000
-
00010fc40000
256c000
RW
-
/
private
/
var
/
containers
/
Bundle
/
Application
/
2A5D623F
-
5F8E
-
4A99
-
96C8
-
8CBD00D8B6BE
/
WeChat.app
/
WeChat af44000 __DATA
00010fc40000
-
000110ac4000
e84000 R
-
-
/
private
/
var
/
containers
/
Bundle
/
Application
/
2A5D623F
-
5F8E
-
4A99
-
96C8
-
8CBD00D8B6BE
/
WeChat.app
/
WeChat cb68000 __LINKEDIT
(lldbinit) vmmap
/
WeChat
000102790000
-
00010d6d4000
af44000 R
-
X
/
private
/
var
/
containers
/
Bundle
/
Application
/
2A5D623F
-
5F8E
-
4A99
-
96C8
-
8CBD00D8B6BE
/
WeChat.app
/
WeChat
0
__TEXT
00010d6d4000
-
00010fc40000
256c000
RW
-
/
private
/
var
/
containers
/
Bundle
/
Application
/
2A5D623F
-
5F8E
-
4A99
-
96C8
-
8CBD00D8B6BE
/
WeChat.app
/
WeChat af44000 __DATA
00010fc40000
-
000110ac4000
e84000 R
-
-
/
private
/
var
/
containers
/
Bundle
/
Application
/
2A5D623F
-
5F8E
-
4A99
-
96C8
-
8CBD00D8B6BE
/
WeChat.app
/
WeChat cb68000 __LINKEDIT
(lldbinit) image lookup
-
a
0x0000000105334f04
Address: WeChat[
0x0000000102ba4f04
] (WeChat.__TEXT.__text
+
45747972
)
Summary: WeChat`___lldb_unnamed_symbol201841$$WeChat
(lldbinit) image lookup
-
a
0x0000000104b32f80
Address: WeChat[
0x00000001023a2f80
] (WeChat.__TEXT.__text
+
37351296
)
Summary: WeChat`___lldb_unnamed_symbol170264$$WeChat
+
160
(lldbinit) image lookup
-
a
0x0000000104e06de4
Address: WeChat[
0x0000000102676de4
] (WeChat.__TEXT.__text
+
40316388
)
Summary: WeChat`___lldb_unnamed_symbol182196$$WeChat
+
2600
(lldbinit) image lookup
-
a
0x000000010294cde8
Address: WeChat[
0x00000001001bcde8
] (WeChat.__TEXT.__text
+
1805800
)
Summary: WeChat`___lldb_unnamed_symbol8796$$WeChat
+
248
(lldbinit) image lookup
-
a
0x00000001041bb930
Address: WeChat[
0x0000000101a2b930
] (WeChat.__TEXT.__text
+
27425072
)
Summary: WeChat`___lldb_unnamed_symbol129751$$WeChat
+
624
(lldbinit) image lookup
-
a
0x0000000105334f04
Address: WeChat[
0x0000000102ba4f04
] (WeChat.__TEXT.__text
+
45747972
)
Summary: WeChat`___lldb_unnamed_symbol201841$$WeChat
(lldbinit) image lookup
-
a
0x0000000104b32f80
Address: WeChat[
0x00000001023a2f80
] (WeChat.__TEXT.__text
+
37351296
)
Summary: WeChat`___lldb_unnamed_symbol170264$$WeChat
+
160
(lldbinit) image lookup
-
a
0x0000000104e06de4
Address: WeChat[
0x0000000102676de4
] (WeChat.__TEXT.__text
+
40316388
)
Summary: WeChat`___lldb_unnamed_symbol182196$$WeChat
+
2600
(lldbinit) image lookup
-
a
0x000000010294cde8
Address: WeChat[
0x00000001001bcde8
] (WeChat.__TEXT.__text
+
1805800
)
Summary: WeChat`___lldb_unnamed_symbol8796$$WeChat
+
248
(lldbinit) image lookup
-
a
0x00000001041bb930
Address: WeChat[
0x0000000101a2b930
] (WeChat.__TEXT.__text
+
27425072
)
Summary: WeChat`___lldb_unnamed_symbol129751$$WeChat
+
624
@interface
MyClass: NSObject
-
(void)insMethod:(const char
*
)a1 arg2:(
int
)a2;
+
(void)clsMethod:(const char
*
)a1 arg2:(
int
)a2;
@end
@implementation
MyClass
-
(void)insMethod:(const char
*
)a1 arg2:(
int
)a2 {
NSLog(@
"insMethod called, self=%p, a1=%s"
,
self
, a1);
}
+
(void)clsMethod:(const char
*
)a1 arg2:(
int
)a2 {
NSLog(@
"clsMethod called, self=%p, a1=%s"
,
self
, a1);
}
@end
int
main() {
MyClass
*
c
=
[[MyClass alloc] init];
[MyClass clsMethod:
"hello"
arg2:
1
];
[c insMethod:
"world"
arg2:
2
];
return
0
;
}
@interface
MyClass: NSObject
-
(void)insMethod:(const char
*
)a1 arg2:(
int
)a2;
+
(void)clsMethod:(const char
*
)a1 arg2:(
int
)a2;
@end
@implementation
MyClass
-
(void)insMethod:(const char
*
)a1 arg2:(
int
)a2 {
NSLog(@
"insMethod called, self=%p, a1=%s"
,
self
, a1);
}
+
(void)clsMethod:(const char
*
)a1 arg2:(
int
)a2 {
NSLog(@
"clsMethod called, self=%p, a1=%s"
,
self
, a1);
}
@end
int
main() {
MyClass
*
c
=
[[MyClass alloc] init];
[MyClass clsMethod:
"hello"
arg2:
1
];
[c insMethod:
"world"
arg2:
2
];
return
0
;
}
(lldbinit) disassemble
main @ test_hello:
0x100003e90
: push rbp
0x100003e91
: mov rbp, rsp
0x100003e94
: sub rsp,
0x10
0x100003e98
: mov dword ptr [rbp
-
0x4
],
0x0
-
>
0x100003e9f
: mov rax, qword ptr [rip
+
0x424a
] ; (void
*
)
0x0000000100008120
: MyClass
0x100003ea6
: mov rdi, rax
0x100003ea9
: call
0x100003f04
; symbol stub
for
: objc_alloc_init
0x100003eae
: mov qword ptr [rbp
-
0x10
], rax
0x100003eb2
: mov rax, qword ptr [rip
+
0x4237
] ; (void
*
)
0x0000000100008120
: MyClass
0x100003eb9
: mov rsi, qword ptr [rip
+
0x4220
] ;
"clsMethod:arg2:"
0x100003ec0
: mov rdi, rax
0x100003ec3
: lea rdx, [rip
+
0xa8
] ;
"hello"
0x100003eca
: mov ecx,
0x1
0x100003ecf
: call qword ptr [rip
+
0x12b
] ; (void
*
)
0x00007fff20439d00
: objc_msgSend
0x100003ed5
: mov rax, qword ptr [rbp
-
0x10
]
0x100003ed9
: mov rsi, qword ptr [rip
+
0x4208
] ;
"insMethod:arg2:"
0x100003ee0
: mov rdi, rax
0x100003ee3
: lea rdx, [rip
+
0x8e
] ;
"world"
0x100003eea
: mov ecx,
0x2
0x100003eef
: call qword ptr [rip
+
0x10b
] ; (void
*
)
0x00007fff20439d00
: objc_msgSend
0x100003ef5
: xor eax, eax
0x100003ef7
: add rsp,
0x10
0x100003efb
: pop rbp
0x100003efc
: ret
(lldbinit) disassemble
main @ test_hello:
0x100003e90
: push rbp
0x100003e91
: mov rbp, rsp
0x100003e94
: sub rsp,
0x10
0x100003e98
: mov dword ptr [rbp
-
0x4
],
0x0
-
>
0x100003e9f
: mov rax, qword ptr [rip
+
0x424a
] ; (void
*
)
0x0000000100008120
: MyClass
0x100003ea6
: mov rdi, rax
0x100003ea9
: call
0x100003f04
; symbol stub
for
: objc_alloc_init
0x100003eae
: mov qword ptr [rbp
-
0x10
], rax
0x100003eb2
: mov rax, qword ptr [rip
+
0x4237
] ; (void
*
)
0x0000000100008120
: MyClass
0x100003eb9
: mov rsi, qword ptr [rip
+
0x4220
] ;
"clsMethod:arg2:"
0x100003ec0
: mov rdi, rax
0x100003ec3
: lea rdx, [rip
+
0xa8
] ;
"hello"
0x100003eca
: mov ecx,
0x1
0x100003ecf
: call qword ptr [rip
+
0x12b
] ; (void
*
)
0x00007fff20439d00
: objc_msgSend
0x100003ed5
: mov rax, qword ptr [rbp
-
0x10
]
0x100003ed9
: mov rsi, qword ptr [rip
+
0x4208
] ;
"insMethod:arg2:"
0x100003ee0
: mov rdi, rax
0x100003ee3
: lea rdx, [rip
+
0x8e
] ;
"world"
0x100003eea
: mov ecx,
0x2
0x100003eef
: call qword ptr [rip
+
0x10b
] ; (void
*
)
0x00007fff20439d00
: objc_msgSend
0x100003ef5
: xor eax, eax
0x100003ef7
: add rsp,
0x10
0x100003efb
: pop rbp
0x100003efc
: ret
id
objc_msgSend(
self
, op, ...);
id
objc_msgSend(
self
, op, ...);
(lldbinit) p
/
x $rdi
(unsigned
long
) $
29
=
0x0000000100008120
(lldbinit) x
/
1s
$rsi
0x100003f86
:
"clsMethod:arg2:"
(lldbinit) x
/
1s
$rdx
0x100003f72
:
"hello"
(lldbinit) p
/
x $rcx
(unsigned
long
) $
32
=
0x0000000000000001
(lldbinit) p
/
x $rdi
(unsigned
long
) $
29
=
0x0000000100008120
(lldbinit) x
/
1s
$rsi
0x100003f86
:
"clsMethod:arg2:"
(lldbinit) x
/
1s
$rdx
0x100003f72
:
"hello"
(lldbinit) p
/
x $rcx
(unsigned
long
) $
32
=
0x0000000000000001
(lldbinit) p
/
x $rdi
(unsigned
long
) $
54
=
0x0000000100208340
(lldbinit) x
/
1s
$rsi
0x100003f96
:
"insMethod:arg2:"
(lldbinit) x
/
1s
$rdx
0x100003f78
:
"world"
(lldbinit) p
/
x $rcx
(unsigned
long
) $
57
=
0x0000000000000002
(lldbinit) p
/
x $rdi
(unsigned
long
) $
54
=
0x0000000100208340
(lldbinit) x
/
1s
$rsi
0x100003f96
:
"insMethod:arg2:"
(lldbinit) x
/
1s
$rdx
0x100003f78
:
"world"
(lldbinit) p
/
x $rcx
(unsigned
long
) $
57
=
0x0000000000000002
2021
-
04
-
10
22
:
54
:
19.487148
+
0800
test_hello[
99332
:
5120374
] clsMethod called,
self
=
0x100008120
, a1
=
hello
2021
-
04
-
10
22
:
54
:
54.565229
+
0800
test_hello[
99332
:
5120374
] insMethod called,
self
=
0x100208340
, a1
=
world
2021
-
04
-
10
22
:
54
:
19.487148
+
0800
test_hello[
99332
:
5120374
] clsMethod called,
self
=
0x100008120
, a1
=
hello
2021
-
04
-
10
22
:
54
:
54.565229
+
0800
test_hello[
99332
:
5120374
] insMethod called,
self
=
0x100208340
, a1
=
world
@class
EmojiInfoObj, NSData, NSString;
@interface
CEmoticonWrap : NSObject <PBCoding>
{
_Bool m_bCanDelete;
_Bool m_isAsyncUpload;
_Bool m_isRemoteRecommed;
_Bool m_isLastSended;
unsigned
int
m_uiType;
unsigned
int
m_uiGameType;
unsigned
int
m_lastUsedTime;
unsigned
int
m_extFlag;
NSString
*
m_nsAppID;
NSString
*
m_nsThumbImgPath;
NSString
*
m_query;
EmojiInfoObj
*
m_emojiInfo;
NSData
*
_m_imageData;
}
@class
EmojiInfoObj, NSData, NSString;
@interface
CEmoticonWrap : NSObject <PBCoding>
{
_Bool m_bCanDelete;
_Bool m_isAsyncUpload;
_Bool m_isRemoteRecommed;
_Bool m_isLastSended;
unsigned
int
m_uiType;
unsigned
int
m_uiGameType;
unsigned
int
m_lastUsedTime;
unsigned
int
m_extFlag;
NSString
*
m_nsAppID;
NSString
*
m_nsThumbImgPath;
NSString
*
m_query;
EmojiInfoObj
*
m_emojiInfo;
NSData
*
_m_imageData;
}
@interface
EmojiInfoObj : MMObject <PBCoding, NSCopying>
{
_Bool _disableExtern;
NSString
*
md5;
NSString
*
url;
NSString
*
thumbUrl;
NSString
*
designerId;
NSString
*
encryptUrl;
NSString
*
aesKey;
NSString
*
productId;
NSString
*
externUrl;
NSString
*
attachedText;
NSString
*
externMd5;
NSString
*
activityId;
NSString
*
attachedTextColor;
NSString
*
lensId;
NSString
*
linkId;
NSString
*
_tpUrlString;
NSString
*
_authkey;
}
@interface
EmojiInfoObj : MMObject <PBCoding, NSCopying>
{
_Bool _disableExtern;
NSString
*
md5;
NSString
*
url;
NSString
*
thumbUrl;
NSString
*
designerId;
NSString
*
encryptUrl;
NSString
*
aesKey;
NSString
*
productId;
NSString
*
externUrl;
NSString
*
attachedText;
NSString
*
externMd5;
NSString
*
activityId;
NSString
*
attachedTextColor;
NSString
*
lensId;
NSString
*
linkId;
NSString
*
_tpUrlString;
NSString
*
_authkey;
}
[
+
]
21
:
50
:
18.958
Called SendEmoticonMesssageToolView:
[
+
]
21
:
50
:
18.958
emotion: <CEmoticonWrap:
self
.m_uiType
=
1
,
self
.m_bCanDelete
=
1
,
self
.m_uiGameType
=
2
,
self
.m_nsAppID
=
(null),
self
.m_extFlag
=
0
,
self
.m_nsThumbImgPath
=
(null),
self
.m_lastUsedTime
=
0
,
self
.m_isAsyncUpload
=
0
,
self
.m_isRemoteRecommed
=
0
,
self
.m_isLastSended
=
0
,
self
.m_query
=
(null),
self
.m_emojiInfo
=
<EmojiInfoObj:
0x2813c77b0
> {
activityId
=
<nil>;
aesKey
=
<nil>;
attachedText
=
<nil>;
attachedTextColor
=
<nil>;
authkey
=
<nil>;
designerId
=
<nil>;
disableExtern
=
0
;
encryptUrl
=
<nil>;
externMd5
=
<nil>;
externUrl
=
<nil>;
lensId
=
<nil>;
linkId
=
<nil>;
md5
=
"dice_emoticon_md5"
;
productId
=
"custom_emoticon_pid"
;
thumbUrl
=
<nil>;
tpUrlString
=
<nil>;
url
=
<nil>
}>
[
+
]
21
:
50
:
18.958
Called SendEmoticonMesssageToolView:
[
+
]
21
:
50
:
18.958
emotion: <CEmoticonWrap:
self
.m_uiType
=
1
,
self
.m_bCanDelete
=
1
,
self
.m_uiGameType
=
2
,
self
.m_nsAppID
=
(null),
self
.m_extFlag
=
0
,
self
.m_nsThumbImgPath
=
(null),
self
.m_lastUsedTime
=
0
,
self
.m_isAsyncUpload
=
0
,
self
.m_isRemoteRecommed
=
0
,
self
.m_isLastSended
=
0
,
self
.m_query
=
(null),
self
.m_emojiInfo
=
<EmojiInfoObj:
0x2813c77b0
> {
activityId
=
<nil>;
aesKey
=
<nil>;
attachedText
=
<nil>;
attachedTextColor
=
<nil>;
authkey
=
<nil>;
designerId
=
<nil>;
disableExtern
=
0
;
encryptUrl
=
<nil>;
externMd5
=
<nil>;
externUrl
=
<nil>;
lensId
=
<nil>;
linkId
=
<nil>;
md5
=
"dice_emoticon_md5"
;
productId
=
"custom_emoticon_pid"
;
thumbUrl
=
<nil>;
tpUrlString
=
<nil>;
url
=
<nil>
}>
void __cdecl
-
[BaseMsgContentViewController SendEmoticonMesssageToolView:](BaseMsgContentViewController
*
self
, SEL a2,
id
emotion)
{
id
v4;
/
/
x19
id
v5;
/
/
x20
id
*
v6;
/
/
x21
id
v7;
/
/
x24
unsigned __int8 v8;
/
/
w23
id
*
v9;
/
/
x21
id
v10;
/
/
x0
id
*
v11;
/
/
x21
void
*
v12;
/
/
x20
id
v13;
/
/
x24
unsigned
int
v14;
/
/
w22
id
*
v15;
/
/
x21
v4
=
objc_retain(emotion);
v5
=
objc_loadWeakRetained((
id
*
)&
self
-
>m_delegate);
if
( ((unsigned
int
)objc_msgSend(v5,
"respondsToSelector:"
,
"CanSendEmoticonMessage"
) &
1
)
=
=
0
)
goto LABEL_6;
v7
=
objc_loadWeakRetained(v6);
v8
=
(unsigned __int8)objc_msgSend(v7,
"CanSendEmoticonMessage"
);
objc_release(v7);
objc_release(v5);
if
( (v8 &
1
) !
=
0
)
{
v10
=
objc_loadWeakRetained(v9);
if
( v10 )
{
v12
=
v10;
v13
=
objc_loadWeakRetained(v11);
v14
=
(unsigned
int
)objc_msgSend(v13,
"respondsToSelector:"
,
"SendEmoticonMessage:"
);
objc_release(v13);
objc_release(v12);
if
( v14 )
{
v5
=
objc_loadWeakRetained(v15);
objc_msgSend(v5,
"SendEmoticonMessage:"
, v4);
LABEL_6:
objc_release(v5);
goto LABEL_7;
}
}
}
LABEL_7:
objc_release(v4);
}
void __cdecl
-
[BaseMsgContentViewController SendEmoticonMesssageToolView:](BaseMsgContentViewController
*
self
, SEL a2,
id
emotion)
{
id
v4;
/
/
x19
id
v5;
/
/
x20
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!