复习iOS逆向知识,以微信消息防撤回为例,一步一步分析调试,到完成插件注入。
环境配置
越狱iPhone 5s (iOS 10.1.1)
并安装了以下软件:
OpenSSH
: 实现在越狱手机上远程进行 ssh 服务,通过 ssh,即可以通过终端连接 iPhone 进行控制。dumpdecrypted
: 砸壳工具。Cycript
: 脚本语言工具,用于 hook 正在运行的进程,并实时注入代码。debugserver
: 用于连接手机进行 lldb 调试的工具。用 Xcode 在手机上进行 app 调试即可在iPhone目录的 /Developer/usr/bin/ 中生成。
苹果电脑 (macOS High Sierra 10.13.3)
并安装了以下软件:
frida-ios-dump
: 砸壳利器。class_dump
: dump 目标对象的 class 信息的工具。Hopper_Disassembler
: 静态分析工具。usbmuxd
: 端口转发,可以让我们通过 usb 连接手机进行 ssh、lldb 调试等。lldb
: 调试神器,用过的都说好 (/Applications/Xcode.app/Contents/Developer/usr/bin/lldb)。theos
: 插件编写IDE。
流程概述
- 砸壳:
frida-ios-dump
、dumpdecrypted
、Clutch
- 分析调试:
- 静态分析:
class-dump
、Hopper Disassembler
- 动态分析:
Cycript
、Logify
、lldb+debugserver
- 静态分析:
- 编写插件并注入:
theos
砸壳
推荐使用frida-ios-dump
,下面会分别以frida-ios-dump
和dumpdecrypted
为例进行砸壳,实际情况任选一个使用即可。
frida-ios-dump
1.获取app信息 frida-ios-dump/dump.py -l
# frida-ios-dump/dump.py -l
PID Name Identifier
----- -------------- -------------------------------
92311 微信 com.tencent.xin
- App Store com.apple.AppStore
- Cydia com.saurik.Cydia
- FaceTime com.apple.facetime
- Safari com.apple.mobilesafari
...
2.根据app信息进行砸壳 frida-ios-dump/dump.py [bundle id|name]
# frida-ios-dump/dump.py com.tencent.xin
Start the target app com.tencent.xin
Dumping 微信 to /var/folders/7b/c3cyxy3j0t7_tgnt0dh5wc240000gn/T
start dump /var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/WeChat
WeChat.fid: 100%|█████████████████████████████████████████████████| 71.8M/71.8M [00:06<00:00, 11.1MB/s]
start dump /private/var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/Frameworks/WCDB.framework/WCDB
WCDB.fid: 100%|███████████████████████████████████████████████████| 2.49M/2.49M [00:00<00:00, 9.10MB/s]
start dump /private/var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/Frameworks/MMCommon.framework/MMCommon
MMCommon.fid: 100%|█████████████████████████████████████████████████| 979k/979k [00:00<00:00, 7.56MB/s]
start dump /private/var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/Frameworks/MultiMedia.framework/MultiMedia
MultiMedia.fid: 100%|█████████████████████████████████████████████| 6.61M/6.61M [00:00<00:00, 10.8MB/s]
start dump /private/var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/Frameworks/mars.framework/mars
mars.fid: 100%|███████████████████████████████████████████████████| 8.49M/8.49M [00:00<00:00, 11.2MB/s]
network_setting.html: 139MB [00:25, 5.62MB/s]
0.00B [00:00, ?B/s]
Generating "微信.ipa"
砸壳成功后会在当前目录下生成去壳后的安装包微信.ipa
。
dumpdecrypted
1.获取app安装路径 ps -e | grep /var/
# ps -e | grep /var/
92817 ?? 0:16.99 /var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/WeChat
92867 ?? 0:00.20 /private/var/containers/Bundle/Application/FD3685C9-80D5-419E-B106-DF466545003E/News.app/PlugIns/NewsNotificationServiceExtension.appex/NewsNotificationServiceExtension
93182 ttys000 0:00.02 grep /var/
2.根据app安装路径进行砸壳 DYLD_INSERT_LIBRARIES=/path/to/dumpdecrypted.dylib /path/to/app/executablename
# DYLD_INSERT_LIBRARIES=/path/to/dumpdecrypted.dylib /var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/WeChat
mach-o decryption dumper
DISCLAIMER: This tool is only meant for security research purposes, not for application crackers.
[+] detected 64bit ARM binary in memory.
[+] offset to cryptid found: @0x1000b4cf8(from 0x1000b4000) = cf8
[+] Found encrypted data at address 00004000 of length 59457536 bytes - type 1.
[+] Opening /private/var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/WeChat for reading.
[+] Reading header
[+] Detecting header type
[+] Executable is a plain MACH-O image
[+] Opening WeChat.decrypted for writing.
[+] Copying the not encrypted start of the file
[+] Dumping the decrypted data into the file
[+] Copying the not encrypted remainder of the file
[+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset cf8
[+] Closing original file
[+] Closing dump file
成功后会在当前目录下生成WeChat.decrypted,也就是砸壳后的执行文件。
分析调试
Cycript (动态分析工具)
Cycript允许开发人员调试和修改iOS和Mac OS X上运行的应用程序。
Cycript是一个理解Objective-C语法的javascript解释器,它能够挂钩正在运行的进程,能够在运行时修改应用的很多东西。
调试命令 cycript [-p <pid|name>]
可以先通过 ps -e | grep /var
命令找到对应pid
# cycript -p WeChat
cy# [[[UIWindow keyWindow] rootViewController] _printHierarchy].toString()
`<MMTabBarController 0x14606ae00>, state: appeared, view: <UILayoutContainerView 0x145da6420>
| <MMUINavigationController 0x146179800>, state: appeared, view: <UILayoutContainerView 0x145d99460>
| | <NewMainFrameViewController 0x1460ff600>, state: disappeared, view: <MMUIHookView 0x145d8efa0> not in the window
| | <BaseMsgContentViewController 0x146983e00>, state: appeared, view: <UIView 0x145d7d440>
| <MMUINavigationController 0x14618b000>, state: disappeared, view: <UILayoutContainerView 0x145d9dda0> not in the window
| | <ContactsViewController 0x146191000>, state: disappeared, view: (view not loaded)
| <MMUINavigationController 0x14613a800>, state: disappeared, view: <UILayoutContainerView 0x145da0ec0> not in the window
| | <FindFriendEntryViewController 0x146859200>, state: disappeared, view: (view not loaded)
| <MMUINavigationController 0x1461a4e00>, state: disappeared, view: <UILayoutContainerView 0x145da3950> not in the window
| | <MoreViewController 0x146172c00>, state: disappeared, view: (view not loaded)`
Logify (静态分析工具)
介绍: http://iphonedevwiki.net/index.php/Logify
Logify是一种实用工具,可以将类的头文件(.h文件)作为输入,并生成MobileSubstrate文件(.xm文件)。
作用:能自动Hook该类所有的方法,并生成打印日志信息代码,方便开发人员看到在使用过程中调用了某些方法。
# logify.pl BaseMsgContentViewController.h > Tweak.xm
Logos 语法
介绍: http://iphonedevwiki.net/index.php/Logos
可以在MobileSubstrate文件(.xm文件)中使用的语法
Theos-NIC (动态分析工具)
介绍: http://iphonedevwiki.net/index.php/NIC
Theos NIC templates内置了多种种Theos工程类型的模板。
用于编写iOS越狱设备的插件。
创建一个追踪logify的插件工程:
# nic.pl
NIC 2.0 - New Instance Creator
------------------------------
[1.] iphone/activator_event
[2.] iphone/application_modern
[3.] iphone/cydget
[4.] iphone/flipswitch_switch
[5.] iphone/framework
[6.] iphone/ios7_notification_center_widget
[7.] iphone/library
[8.] iphone/notification_center_widget
[9.] iphone/preference_bundle_modern
[10.] iphone/tool
[11.] iphone/tweak
[12.] iphone/xpc_service
Choose a Template (required): 11
Project Name (required): TWeak-Logify-WX
Package Name [com.yourcompany.tweak-logify-wx]: cn.theos.tweak.wx.logify
Author/Maintainer Name [dabing]: author name
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.tencent.xin
[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]:
Instantiating iphone/tweak in tweaklogifywx/...
Done.
工程文件结构如下:
# tree
.
└── tweaklogifywx
├── Makefile
├── TWeakLogifyWX.plist
├── Tweak.xm
└── control
将Logify
生成的Tweak.xm
覆盖掉工程中的Tweak.xm
用文本编辑器打开Makefile
文件,在文件的开头增加iOS设备的ip地址和ssh端口等信息:
THEOS_DEVICE_IP = localhost
THEOS_DEVICE_PORT = 2333
ARCHS = arm64
TRAGET = iphone:latest:9.0
include $(THEOS)/makefiles/common.mk
TWEAK_NAME = TWeakLogifyWX
TWeakLogifyWX_FILES = Tweak.xm
logifyWX_FRAMEWORKS = UIKit Foundation CoreGraphics
logifyWX_CFLAGS = -fobjc-arc
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
install.exec "killall -9 SpringBoard"
编译打包安装:
# make package install
> Making all for tweak TWeakLogifyWX…
==> Preprocessing Tweak.xm…
==> Compiling Tweak.xm (arm64)…
==> Linking tweak TWeakLogifyWX (arm64)…
clang: warning: libstdc++ is deprecated; move to libc++ with a minimum deployment target of iOS 7 [-Wdeprecated]
==> Generating debug symbols for TWeakLogifyWX (arm64)…
warning: no debug symbols in executable (-arch arm64)
==> Merging tweak TWeakLogifyWX…
==> Signing TWeakLogifyWX…
> Making stage for tweak TWeakLogifyWX…
dm.pl: building package `cn.theos.tweak.wx.logify:iphoneos-arm' in `./packages/cn.theos.tweak.wx.logify_0.0.1-1+debug_iphoneos-arm.deb'
==> Installing…
Selecting previously unselected package cn.theos.tweak.wx.logify.
(Reading database ... 2279 files and directories currently installed.)
Preparing to unpack /tmp/_theos_install.deb ...
Unpacking cn.theos.tweak.wx.logify (0.0.1-1+debug) ...
Setting up cn.theos.tweak.wx.logify (0.0.1-1+debug) ...
install.exec "killall -9 SpringBoard"
注意
:一般该Tweak.xm仍然无法执行,需要进行修改:
去掉 .cxx_destruct 方法
将 HBLogDebug 改为 NSLog
去掉所有的 weak 属性
将头文件(.h文件)中的@class和@protocol声明都拷贝至Tweak.xm (或去掉所有delegate并将所有参数对象类型改为id)。
安装成功后会在设备中新增以下两个文件:
/Library/MobileSubstrate/DynamicLibraries/TWeakLogifyWX.dylib
/Library/MobileSubstrate/DynamicLibraries/TWeakLogifyWX.plist
打开Cydia-已安装
可以看到插件已经安装成功:
idevicesyslog (iOS日志查看工具)
介绍: https://github.com/libimobiledevice/libimobiledevice
是
libimobiledevice
下的一个子工具,可以实时追踪iOS设备日志。
# idevicesyslog | grep "BaseMsgContentViewController"
重新运行app,执行收到消息和撤回消息的case,可以分别获取两份log:
接收消息log | 撤回消息log |
---|---|
![]() |
![]() |
整理后log对比可以发现可疑方法调用-[BaseMsgContentViewController OnMsgRevoked:n64MsgId:]
class-dump (静态分析工具)
介绍: http://stevenygard.com/projects/class-dump/
这是一个检查存储在Mach-O文件Objective-C运行时信息的命令行实用工具。
能生成classes、categories、protocols定义的头文件(.h文件)。(与otool -ov
命令获取的信息类似,但可读性更高)
# class-dump -HA WeChat -o ./Headers
获得头文件以及方法的IMP address
例如:-[BaseMsgContentViewController OnMsgRevoked:n64MsgId:] // IMP=0x0000000102129454
Hopper Disassembler (分析工具)
是一款32位和64位的二进制反汇编器,反编译和调试。可以使用此工具拆开你想要的任何二进制。
使用方法很简单:将二进制文件拖入软件中,等待处理完成即可。
(完整解析微信app需要很长一段时间,可以前置该流程进行解析处理)
lldb+debugserver (动态分析)
1.打开微信后,在连接至设备的控制台中键入debugserver *:1234 -a "WeChat"
启动debugserver。
2.从控制台打开新窗口,键入lldb
进入调试,再键入process connect connect://192.168.1.19:1234
连接1234端口。
(此处连接上需要一点时间,可以上个厕所,连接上后键入c
(continue)后app就可以正常运行了)
连接成功后lldb
窗口会出现以下内容:
(lldb) process connect connect://192.168.1.19:1234
Process 73244 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x00000001900f5164 libsystem_kernel.dylib`__fcntl + 8
libsystem_kernel.dylib`__fcntl:
-> 0x1900f5164 <+8>: b.lo 0x1900f517c ; <+32>
0x1900f5168 <+12>: stp x29, x30, [sp, #-0x10]!
0x1900f516c <+16>: mov x29, sp
0x1900f5170 <+20>: bl 0x1900d9e8c ; cerror
Target 0: (WeChat) stopped.
(lldb) c
Process 73244 resuming
获取aslr
的offset
。(每次启动都不同)
其中第一列[X]是image的序号,不用管;第二列是aslr
的offset
(也就是对应image的虚拟内存slide);第三列是image的全路径和slide之后的基地址,也不用管~所以第二列就是我们需要的信息。
(lldb) image list -o -f
[ 0] 0x0000000000048000 /var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/WeChat(0x0000000100048000)
[ 1] 0x0000000104a20000 /Users/passerbycrk/Library/Developer/Xcode/iOS DeviceSupport/10.1.1 (14B100)/Symbols/usr/lib/dyld
[ 2] 0x0000000104970000 /Library/MobileSubstrate/MobileSubstrate.dylib(0x0000000104970000)
[ 3] 0x000000000fab4000 /Users/passerbycrk/Library/Developer/Xcode/iOS DeviceSupport/10.1.1 (14B100)/Symbols/System/Library/Frameworks/CallKit.framework/CallKit
[ 4] 0x000000000fab4000 /Users/passerbycrk/Library/Developer/Xcode/iOS DeviceSupport/10.1.1 (14B100)/Symbols/System/Library/Frameworks/Accelerate.framework/Accelerate
[ 5] 0x000000000fab4000 /Users/passerbycrk/Library/Developer/Xcode/iOS DeviceSupport/10.1.1 (14B100)/Symbols/System/Library/Frameworks/Intents.framework/Intents
[ 6] 0x000000000fab4000 /Users/passerbycrk/Library/Developer/Xcode/iOS DeviceSupport/10.1.1 (14B100)/Symbols/usr/lib/libbz2.1.0.dylib
[ 7] 0x0000000104aa4000 /private/var/containers/Bundle/Application/FC7574FD-C99D-49DE-8130-AF824051424A/WeChat.app/Frameworks/WCDB.framework/WCDB(0x0000000104aa4000)
[ 8] 0x000000000fab4000 /Users/passerbycrk/Library/Developer/Xcode/iOS DeviceSupport/10.1.1 (14B100)/Symbols/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore
...
得到aslr
的offset
:0x0000000000048000
(不同的发布版本偏移量不一定相同)
再根据class-dump
获得的头文件,找到对应的方法,在后面可以看到IMP
的offset
。-[BaseMsgContentViewController OnMsgRevoked:n64MsgId:] // IMP=0x0000000102129454
通过br
命令设置断点(aslr_offset + IMP_offset)
(lldb) br s -a '0x0000000000048000+0x0000000102129454'
Breakpoint 1: where = WeChat`ClearDataItem::compareTime(std::__1::shared_ptr<ClearDataItem> const&, std::__1::shared_ptr<ClearDataItem> const&) + 4373492, address = 0x0000000102171454
进入断点
Process 73244 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000102171454 WeChat`ClearDataItem::compareTime(std::__1::shared_ptr<ClearDataItem> const&, std::__1::shared_ptr<ClearDataItem> const&) + 4373492
WeChat`ClearDataItem::compareTime:
-> 0x102171454 <+4373492>: stp x24, x23, [sp, #-0x40]!
0x102171458 <+4373496>: stp x22, x21, [sp, #0x10]
0x10217145c <+4373500>: stp x20, x19, [sp, #0x20]
0x102171460 <+4373504>: stp x29, x30, [sp, #0x30]
Target 0: (WeChat) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000102171454 WeChat`ClearDataItem::compareTime(std::__1::shared_ptr<ClearDataItem> const&, std::__1::shared_ptr<ClearDataItem> const&) + 4373492
frame #1: 0x0000000104d5b980 MMCommon`_callExtension + 480
frame #2: 0x0000000102ccfaac WeChat`ClearDataItem::compareTime(std::__1::shared_ptr<ClearDataItem> const&, std::__1::shared_ptr<ClearDataItem> const&) + 16294476
frame #3: 0x0000000102cda958 WeChat`ClearDataItem::compareTime(std::__1::shared_ptr<ClearDataItem> const&, std::__1::shared_ptr<ClearDataItem> const&) + 16339192
frame #4: 0x0000000102cdb60c WeChat`ClearDataItem::compareTime(std::__1::shared_ptr<ClearDataItem> const&, std::__1::shared_ptr<ClearDataItem> const&) + 16342444
frame #5: 0x0000000104d5b980 MMCommon`_callExtension + 480
frame #6: 0x0000000102f89414 WeChat`ClearSessionItem::compareVideo(std::__1::shared_ptr<ClearSessionItem> const&, std::__1::shared_ptr<ClearSessionItem> const&) + 1516048
...
python+Hopper Disassembler 获取调用栈
>>> hex(0x0000000102171454-0x0000000000048000)
'0x102129454' // -[BaseMsgContentViewController OnMsgRevoked:n64MsgId:]
>>> hex(0x0000000102ccfaac-0x0000000000048000)
'0x102c87aac' // -[CMessageMgr onRevokeMsg:]
使用Hopper Disassembler可以定位调用方法名,此处我们发现可疑函数调用-[CMessageMgr onRevokeMsg:]
编写插件并注入
创建插件工程
# nic.pl
NIC 2.0 - New Instance Creator
------------------------------
[1.] iphone/activator_event
[2.] iphone/application_modern
[3.] iphone/cydget
[4.] iphone/flipswitch_switch
[5.] iphone/framework
[6.] iphone/ios7_notification_center_widget
[7.] iphone/library
[8.] iphone/notification_center_widget
[9.] iphone/preference_bundle_modern
[10.] iphone/tool
[11.] iphone/tweak
[12.] iphone/xpc_service
Choose a Template (required): 11
Project Name (required): Tweak-crack-WX
Package Name [com.yourcompany.tweak-crack-wx]:
Author/Maintainer Name [dabing]:
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.tencent.xin
[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]:
Instantiating iphone/tweak in tweakcrackwx/...
Done.
Tweak.xm
%hook CMessageMgr
- (void)onRevokeMsg:(id)arg1 {
// do nothing
}
%end
安装
# make package install
> Making all for tweak TweakcrackWX…
make[2]: Nothing to be done for `internal-library-compile'.
> Making stage for tweak TweakcrackWX…
dm.pl: building package `com.yourcompany.tweak-crack-wx:iphoneos-arm' in `./packages/com.yourcompany.tweak-crack-wx_0.0.1-2+debug_iphoneos-arm.deb'
==> Installing…
Selecting previously unselected package com.yourcompany.tweak-crack-wx.
(Reading database ... 2277 files and directories currently installed.)
Preparing to unpack /tmp/_theos_install.deb ...
Unpacking com.yourcompany.tweak-crack-wx (0.0.1-2+debug) ...
Setting up com.yourcompany.tweak-crack-wx (0.0.1-2+debug) ...
install.exec "killall -9 SpringBoard"
打开Cydia-已安装
可以看到插件已经安装成功:
接下来重新运行微信,试试消息撤回的case,发现消息撤回已经被阻止了,任务完成~
(文章特意选了个软柿子案例,实际情况可能不会这么容易找到关键函数,需要反复调试验证)
源码
GitHub/passerbycrk/PRK_iOSRE/Demo/WeChat_MsgRevoke_theos
参考
iOSRE
frida
frida-ios-dump
iphonedevwiki
iOS Security
iOS Reverse Debug Cheatsheet
一步一步用debugserver + lldb代替gdb进行动态调试
移动App入侵与逆向破解技术-iOS篇