首页
社区
课程
招聘
从微信扔骰子看iOS应用安全与逆向分析
发表于: 2021-4-11 14:22 93384

从微信扔骰子看iOS应用安全与逆向分析

2021-4-11 14:22
93384

看了下看雪的帖子分布,Android 帖子比 iOS 的帖子多了五六倍啊。本人之前也是在 Android 平台下进行分析比较多,因此这也算面向 Android 安全工程师的一个 iOS 入门介绍吧。本文以微信扔骰子小游戏为例,详细记录了一次完整 iOS 逆向分析的过程,希望能对大家有所帮助。

Jailbreak

本文主要讨论的是应用安全,因此关于越狱实现的部分不做深入介绍。关于XNU内核漏洞的分析和利用网上有很多相关的文章或书籍,比如:

看一些公开的漏洞利用代码对加深印象也很有帮助,比如oob_timestamp的利用。对于本文而言只需要站在巨人的肩膀上使用这些封装好的利用即可。iOS越狱和Android root的的一个很大不同是前者系统封闭性高,碎片化较低,因此提权的方法也相对单一,不支持刷机,大部分都是通过漏洞去获取更高的权限(tfp0)。既然是专注应用层安全,就抓大放小,使用现有的越狱工具,站在前人肩膀上即可。

砸壳

越狱之后总算可以通过 ssh 进入到系统中,也就是相当于安卓世界的 adb shell 而已。既然是逆向分析,下一步就是获取应用的安装包,这在安卓中是一条adb pull命令,但苹果里要复杂一些。

 

iOS 中大部分应用都从 Apple Store 即应用商店下载,而从应用商店下载的 app 是通过苹果签名和加密保护的,这是苹果 FairPlay DRM 数字版权保护的重要部分。为了能使用逆向工具进行分析,需要先对其进行解密,即俗称的砸壳。网上关于砸壳的资料和工具都很多,比如:

  • stefanesser/dumpdecrypted:手动将动态库注入进程获取解密后的文件,不包括动态库
  • conradev/dumpdecrypted:更新版,支持对每个模块进行解密
  • Clutch:通过posix_spawnp创建进程并解密文件,支持重新打包为ipa (iOS12之后这类静态砸壳方法基本上都不行了)
  • frida-ios-dump:使用frida进行动态解密,并支持重新打包为ipa
  • bagbak:和前者类似,不过是基于 nodejs 的 frida binding

这里面有些工具已经年代久远年久失修了,比如我就注意到 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 的分析:

 

flex.gif

 

这里用到了 cycript,后文再细说。或者可以用 Reveal 在电脑端分析,当然还是需要将 RevealServer.dylib 注入到目标进程中并调用[IBARevealLoader startServer],参考官方的 lldb 脚本,启动后 PC 端界面如下:

 

Reveal

 

点击发送骰子的区域是一个 UIImageView,其本身是没有响应点击事件的,因此要找对应的 ViewController 或者 Gesture Recognizer。

动态分析

根据 UI 信息能知道的是当前界面的 Controller 类是 BaseMsgContentViewController,所以我们可以通过脚本去跟踪发送骰子时该类所有的函数调用,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[-] 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]
...

frida

其中SendEmoticonMesssageToolView是个值得关注的函数,可以直接用 frida 打印函数堆栈:

1
2
3
4
5
6
7
8
9
10
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);
  }
});

输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[-] 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:]

在 WeChat 部分的回溯没有符号,因为已经去掉了。

lldb

在 lldb 中也可以看到类似的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(lldbinit) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000105334f04 WeChat`___lldb_unnamed_symbol201841$$WeChat
    frame #1: 0x0000000104b32f80 WeChat`___lldb_unnamed_symbol170264$$WeChat + 160
    frame #2: 0x0000000104e06de4 WeChat`___lldb_unnamed_symbol182196$$WeChat + 2600
    frame #3: 0x000000010294cde8 WeChat`___lldb_unnamed_symbol8796$$WeChat + 248
    frame #4: 0x00000001041bb930 WeChat`___lldb_unnamed_symbol129751$$WeChat + 624
    frame #5: 0x00000001b8331e34 UIKitCore`-[UICollectionView _selectItemAtIndexPath:animated:scrollPosition:notifyDelegate:deselectPrevious:] + 952
    frame #6: 0x00000001b8359e10 UIKitCore`-[UICollectionView touchesEnded:withEvent:] + 572
    frame #7: 0x00000001b8b5d9e8 UIKitCore`forwardTouchMethod + 332
    frame #8: 0x00000001b8b5dae4 UIKitCore`-[UIResponder touchesEnded:withEvent:] + 64
    frame #9: 0x00000001b8b5d9e8 UIKitCore`forwardTouchMethod + 332
    frame #10: 0x00000001b8b5dae4 UIKitCore`-[UIResponder touchesEnded:withEvent:] + 64
    frame #11: 0x00000001b8b5d9e8 UIKitCore`forwardTouchMethod + 332
    frame #12: 0x00000001b8b5dae4 UIKitCore`-[UIResponder touchesEnded:withEvent:] + 64
    frame #13: 0x00000001b86e6478 UIKitCore`_UIGestureEnvironmentUpdate + 6992
    frame #14: 0x00000001b86e48dc UIKitCore`-[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 380
    frame #15: 0x00000001b86e4698 UIKitCore`-[UIGestureEnvironment _updateForEvent:window:] + 248
    frame #16: 0x00000001b8b6d654 UIKitCore`-[UIWindow sendEvent:] + 3512
    frame #17: 0x00000001b8b48840 UIKitCore`-[UIApplication sendEvent:] + 348
    frame #18: 0x0000000103de4920 WeChat`___lldb_unnamed_symbol111457$$WeChat + 404
    frame #19: 0x00000001ebaaa87c UIKit`-[UIApplicationAccessibility sendEvent:] + 100

有符号更好,没有也无所谓,我们还是可以通过地址在逆向工具中查看代码。不过考虑到 ASLR 的存在,需要确认一下镜像的加载地址:

1
2
3
4
(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

vmmap 是我自己定义的一个 lldb 命令,参考了 gef 和 pwndbg 在 gdb 中的实现

 

这里加载地址是 0x000102790000,而在 IDA 中 text 段的加载地址是 0x100004000,可以在后者进行 rebase,但是因为函数不多,所以先手动查找。另外用 lldb 也可以直接查看对应函数地址的偏移:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(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

符号恢复

根据 lldb 中每个函数地址在 TEXT 段中的偏移,可以在 IDA 中直接跳转到对应函数(返回值),整理对应的调用函数分别是:

  • -[BaseMsgContentViewController SendEmoticonMesssageToolView:] (45747972)
  • -[MMInputToolView didSelectorSelfDefinedEmotcion:] (37351296)
  • -[EmoticonBoardView onTapEmoticonWrap:atIndex:maxCountPerLine:fromSection:] (40316388)
  • -[EmoticonBoardCrossCollectionController onEmoticonPageCellTapEmoticonWrap:atIndex:pid:maxCountPerLine:] (1805800)
  • -[EmoticonBoardCrossCollectionEmoticonPageCell collectionView:didSelectItemAtIndexPath:] (27425072)

既然 IDA 可以识别出对应 OC 函数的符号,那么理论上这些符号也是可以还原的,如果经常需要进行动态分析,那么可以通过一些方法自动化恢复对应的符号,可以参考 iOS符号表恢复 以及 restore-symbol 等项目。

静态分析

通过动态分析找到了着手点,以及通过回溯调用栈找到了一些相关函数,其中并没有一目了然的设置骰子点数的函数 setDiceValue,所以要想改点数还需要对上面的函数进行进一步分析。到这里,逆向工程是不可避免了,一般而言我是能躺着绝不坐着,能坐着绝不站着,不到万不得已是不去逆向的。

 

花开两朵,各表一枝,在前面砸完壳的第一时间我就预感到需要逆向,所以早早地把 200MB 的 MachO 丢进了 IDA,在文章写到这里时已经过去了四个小时,期间看了两集动漫,但是 IDA 还是没有全部分析完。所以在等待的时间里,先来简单介绍些 ObjectiveC 的底层实现。

ObjectiveC 101

在学习 OC 的时候,总会看到说 OC 语言实现面向对象是通过发送消息,具体是怎么发送呢?因此在实际逆向之前,我们先来自己写一个程序来进行分析,以便了解 OC 的调用原理。

 

以下面简单的 helloworld 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import
 
@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;
}

主要调用了两个方法,一个类方法和一个成员方法,编译后查看其汇编代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(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

且不管初始化部分,后面两个函数调用最终都进入了 objc_msgSend 函数,在苹果官网可以看到其函数原型:

1
id objc_msgSend(self, op, ...);

其中各个参数为:

  1. self: 消息的接收方,对于类方法为指向类的指针,对于对象方法而言为对象指针;
  2. op: 为消息的标识符,也称为 selector,实际上是一个表示对应方法名称的字符串;
  3. ... 其他方法参数;

回到上面的例子,第一次类方法调用的参数为:

1
2
3
4
5
6
7
8
(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

第二次成员方法的调用参数为:

1
2
3
4
5
6
7
8
(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

程序的输出如下:

1
2
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

所以,ObjectiveC 在实现上还是挺接近 C 语言的,这对于我们逆向而言方便很多。

头铁逆向

了解了简单的 OC 逆向之后,IDA 也跑的差不多了。根据栈回溯对应 OC 函数的名称,逐级往上看。首先是 didSelectorSelfDefinedEmotcion这个函数,表示选中了某个自定义表情,里面只是一些发送操作,所以并不是我们想要的;

 

然后是 -[EmoticonBoardView onTapEmoticonWrap:atIndex:maxCountPerLine:fromSection:] ,该函数中主要是判断所选择的表情是否为自拍表情或者自定义表情,如果是的话就进行异步上传。其调用didSelectorSelfDefinedEmotcion 的参数为 a3,即一个对象指针,虽然还不知道是哪个对象,但知道其包含这些属性(方法):

  • m_emojiInfo
  • m_isAsyncUpload
  • attachObject:forKey:

在 class-dump 导出的头文件中搜索,可以发现是CEmoticonWrap 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import
 
#import "PBCoding-Protocol.h"
 
@class EmojiInfoObj, NSData, NSString;
 
@interface CEmoticonWrap : NSObject
{
    _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;
}

该函数主要是执行上传并记录一些信息,更新一些 emojiInfo 的字段,所以还是要继续往上走;

 

接着是-[EmoticonBoardView onTapEmoticonWrap:atIndex:maxCountPerLine:fromSection:],该函数主要是作为 delegate 进行转发,没有什么实际功能。但是从名字可以猜测自定义表情栏是通过不同的行实现,而每行中每个表情又对应一个 Cell。

 

按照回溯的堆栈都看完了,也没有发现和骰子相关的代码。不管怎样,骰子的点数总归是在设置骰子图片之前确定的,而根据上面的逆向可以知道,自定义表情的图片应该是定义在 [CEmoticonWrap m_emojiInfo]中,该类的属性如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface EmojiInfoObj : MMObject
{
    _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;
}

接下来还是要通过结合动态分析来确定骰子图片加载的时机。

动静结合

还是回到SendEmoticonMesssageToolView函数,通过逆向可以得知其参数为CEmoticonWrap类型,修改前面打印栈 callback 的脚本,令其打印参数的各个字段,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[+] 21:50:18.958 Called SendEmoticonMesssageToolView:
[+] 21:50:18.958 emotion: 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=0x2813c77b0> {
    activityId = ;
    aesKey = ;
    attachedText = ;
    attachedTextColor = ;
    authkey = ;
    designerId = ;
    disableExtern = 0;
    encryptUrl = ;
    externMd5 = ;
    externUrl = ;
    lensId = ;
    linkId = ;
    md5 = "dice_emoticon_md5";
    productId = "custom_emoticon_pid";
    thumbUrl = ;
    tpUrlString = ;
    url =
}>

也就是说进入函数之时这些参数都是空的,m_emojiInfo 的 md5 为 “dice_emoticon_md5”,此时骰子图像并没有真正生成,所以印证了我们之前的猜想,即实际的操作还在后面。那么栈回溯是不是白看了?也不尽然,至少至少了参数格式和调用流程(逆向嘛,总是要学会安慰自己)。

 

回过头来继续看这个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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);
}

又是一个 delegate 模式,直接打印出实例:

1
2
3
4
5
6
7
8
9
kObjC.traceMethod("BaseMsgContentViewController", "- SendEmoticonMesssageToolView:", {
  onEnter(args) {
    log.i("Called", args[1].readCString());
    const vc = new ObjC.Object(args[0]);
    const emotion = new ObjC.Object(args[2]);
    log.i("vc:", vc);
    log.i("m_delegate:", vc.valueForKey_("m_delegate"));
  }
});

结果如下:

1
2
3
[+] 19:15:24.203 Called SendEmoticonMesssageToolView:
[+] 19:15:24.204 vc: 0x1200b8e00>
[+] 19:15:24.206 m_delegate: 0x11f340a90>

是个 WeixinContentLogicController,那就继续找它的SendEmoticonMessage:方法,没有的话就在父类或者 Category 里找。因为程序太大,我的破电脑在 IDA 里搜索比较卡,所以有个小技巧是直接通过函数名跳转,对应的 IDA 函数为名为 -[WeixinContentLogicController SendEmoticonMessage:],当然实际并没有这个函数,而是在父类中定义的 -[BaseMsgContentLogicController SendEmoticonMessage:]


[注意]看雪招聘,专注安全领域的专业人才平台!

收藏
免费 10
支持
分享
最新回复 (6)
雪    币: 8511
活跃值: (5131)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
2
很清楚,学习了
2021-4-11 15:47
0
雪    币: 5492
活跃值: (3317)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
3
写的很好 mark
2021-4-11 21:08
0
雪    币: 707
活跃值: (1301)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
4
谢谢大佬。学习了,系统学习的话还是建议大家找专业的书 比如:《iOS应用逆向与安全之道》a33K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6A6N6r3g2E0i4K6u0W2K9X3c8Q4x3X3g2U0L8$3#2Q4x3V1j5I4x3U0R3H3x3o6b7J5y4W2)9J5k6h3S2@1L8h3I4Q4x3U0k6F1j5Y4y4H3i4K6y4n7
2021-4-26 11:15
0
雪    币: 762
活跃值: (1430)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
5
对照Android做分析,写的真棒
2021-4-28 10:02
0
游客
登录 | 注册 方可回帖
返回