逆向工程

一般来说,软件逆向工程可以看作系统分析代码分析两个阶段的有机结合。在系统分析阶段,我们从整体上观察目标程序的行为特征,文件的组织架构,从而找到我们感兴趣的地方。代码分析阶段,利用各种工具对程序本身的二进制文件进行分析,从而了解目标软件的实现,进而借鉴其设计思路、内部算法、反思教训等。

1.设备越狱

通过爱思PP助手同步助手unc0ver等工具,对已经备份过的设备进行越狱;

2.安装插件

2.1.OpenSSH

OpenSSH 会在 iOS 设备上安装 SSH 服务,从而给外界提供了一个通过 SSH 接入 iOS 设备的途径。常用的有两个命令:

ssh,用于远程登录:

1
$ ssh root@192.168.31.242

scp,用于远程拷贝文件:

1
2
3
4
//本地拷贝到手机
$ scp /path/localFile root@192.168.31.242:/path/remoteFile
//手机拷贝到本地
$ scp root@192.168.31.242:/path/remoteFile /path/localFile

使用 Cydia 在线源直接搜索 OpenSSH 即可找到并安装。

注意:iOS设备上有 rootmobile两个用户,在安装完 SSH 后记得修改默认登录密码“alpine”,防止病毒通过 ssh 以 root 用户身份登录设备。安装SSH和修改密码的方法,一般在Cydia的首页都有说明,可按照说明一步步操作即可。

2.2.Cycript

Cycript 是逆向工程中用来进行动态分析的利器,能够让开发人员在命令行下和应用交互,在执行时查看和改动应用,如帮助我们在运行时查看应用视图层级、函数等信息。

  • 安装方法1:

从 Cydia 自带源Cydia/Telesphoreo下载安装。

  • 安装方法2:

官网 中找到cycript_0.9.501_iphoneos-arm.deblibffi_1:3.0.10-5_iphoneos-arm.deb这两个安装包,下载到 MacOS 上。用 sftp 上传上面两个文件到设备上:

1
2
3
$ sftp root@192.168.31.242
sftp> put cycript_0.9.501_iphoneos-arm.deb
sftp> put libffi_1:3.0.10-5_iphoneos-arm.deb

上传进度100%后,用 dpkg -i来安装deb包:

1
2
$ ssh root@192.168.31.242
~ root# dpkg -i cycript_0.9.501_iphoneos-arm.deb
  • 安装方法3:

ssh登录后,输入以下命令:

1
apt-get install cycrypt

执行 cycript,如果出现cy#符号,则安装完毕:

1
2
~ root# cycript
cy#

3.dumpdecrypted.dylib

从 Github 上下载 dumpdecrypted 源码:

1
2
3
4
5
$ git clone https://github.com/stefanesser/dumpdecrypted.git dumpdecrypted
Cloning into 'dumpdecrypted'...
remote: Counting objects: 31, done.
remote: Total 31 (delta 0), reused 0 (delta 0), pack-reused 31
Unpacking objects: 100% (31/31), done.

进入dumpdecrypted目录查看有哪些文件:

1
2
3
$ cd dumpdecrypted/
$ ls
Makefile README dumpdecrypted.c

编译 dumpdecrypted.dylib

1
2
3
4
5
6
7
$ make
`xcrun --sdk iphoneos --find gcc` -Os -Wimplicit -isysroot `xcrun --sdk iphoneos --show-sdk-path` -F`xcrun --sdk iphoneos --show-sdk-path`/System/Library/Frameworks -F`xcrun --sdk iphoneos --show-sdk-path`/System/Library/PrivateFrameworks -arch armv7 -arch armv7s -arch arm64 -c -o dumpdecrypted.o dumpdecrypted.c
2018-07-27 22:09:19.065 xcodebuild[1941:437762] [MT] PluginLoading: Required plug-in compatibility UUID 426A087B-D3AA-431A-AFDF-F135EC00DE1C for plug-in at path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/VVDocumenter-Xcode.xcplugin' not present in DVTPlugInCompatibilityUUIDs
`xcrun --sdk iphoneos --find gcc` -Os -Wimplicit -isysroot `xcrun --sdk iphoneos --show-sdk-path` -F`xcrun --sdk iphoneos --show-sdk-path`/System/Library/Frameworks -F`xcrun --sdk iphoneos --show-sdk-path`/System/Library/PrivateFrameworks -arch armv7 -arch armv7s -arch arm64 -dynamiclib -o dumpdecrypted.dylib dumpdecrypted.o
ld: warning: directory not found for option '-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.4.sdk/System/Library/PrivateFrameworks'
ld: warning: directory not found for option '-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.4.sdk/System/Library/PrivateFrameworks'
ld: warning: directory not found for option '-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.4.sdk/System/Library/PrivateFrameworks'

make命令执行完之后会在当前目录下生成一个dumpdecrypted.dylib文件,这就是后面用来砸壳用的榔头。

1
2
3
$ ls
Makefile dumpdecrypted.c dumpdecrypted.o
README dumpdecrypted.dylib

4.定位进程

关闭设备中所有的应用,打开央视影音(Cbox)。

通过ssh,以 root 身份登录设备:

1
$ ssh root@192.168.31.242

adv-cmds命令行插件自带的ps命令,定位央视影音进程:

1
2
3
4
5
6
7
8
9
10
11
~ root# ps -e | grep /var/
5193 ?? 0:01.90 /var/containers/Bundle/Application/BE3F444E-5002-40A6-AFF3-71DB398DAB18/MobileMail.app/MobileMail
5195 ?? 6:05.27 /var/containers/Bundle/Application/B3248F78-A0C2-4965-8E2F-B3CEE38E71C0/DYZB.app/DYZB
5218 ?? 12:15.94 /var/containers/Bundle/Application/9437BC53-681D-4F41-889C-5DE6DAAD33CD/Cbox.app/Cbox
5317 ?? 0:40.58 /var/containers/Bundle/Application/6EE6BF3A-94DB-4356-811C-5CD35CEAD4CC/XMFilmTelevision.app/XMFilmTelevision
5860 ?? 0:00.58 /private/var/containers/Bundle/Application/C73F08EB-2C9B-4169-9C2C-8E933417465B/BlockerTest.app/PlugIns/Blocker.appex/Blocker
5861 ?? 0:25.78 /private/var/containers/Bundle/Application/EA8607BE-AD62-4179-B060-E939B00FD212/Shadowrocket.app/PlugIns/Today.appex/Today
5862 ?? 0:00.84 /private/var/containers/Bundle/Application/B6364A33-5BEF-4059-8491-EF8DB484176C/Shortcuts.app/PlugIns/ShortcutsWidget.appex/ShortcutsWidget
5863 ?? 0:01.37 /private/var/containers/Bundle/Application/B2D9F8D2-3853-4BE6-8500-8D4FE4140A4E/Weather.app/PlugIns/WeatherAppTodayWidget.appex/WeatherAppTodayWidget
6034 ?? 0:00.16 /private/var/containers/Bundle/Application/C73F08EB-2C9B-4169-9C2C-8E933417465B/BlockerTest.app/PlugIns/Tunnel.appex/Tunnel
6247 ttys000 0:00.02 grep /var/

第一行的数字是进程对应的PID,最后一行就是进程所在的目录。使用ps -e命令时终端里会列出一堆系统进程和应用,它们往往以/usr//System/开头,而我们下载的应用一般以/var/开头,所以这里可以添加一个过滤条件ps -e | grep /var/,这样结果就简化许多。

因为设备上只打开了一个应用,所以含有/var/containers/Bundle/Application/字样的结果就是storeApp可执行文件的全路径,这里我需要记录下来的是央视影音这个进程:

1
/var/containers/Bundle/Application/9437BC53-681D-4F41-889C-5DE6DAAD33CD/Cbox.app/Cbox

5.Documents 目录

通过cycript -p命令监听 Cbox进程:

1
~ root# cycript -p Cbox

也可以通过CboxPID来监听:

1
~ root# cycript -p 5218

接着在cy#后面输入:

1
cy# [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]

这一步是用 Cycript 找出CboxDocuments目录,输出结果:

1
#"/var/mobile/Containers/Data/Application/D02582B3-66D3-4EF1-A6C2-6129D4E1B9CA/Documents/"

这里/var/mobile/Containers/Data/Application/D02582B3-66D3-4EF1-A6C2-6129D4E1B9CA/Documents/就是我们要找的目录,Ctrl + D退出 Cycript 模式。

6.拷贝动态库

将步骤#3 中编译的dumpdecrypted.dylib拷贝到刚才的Documents目录下:

1
$ scp /Users/davidli/Documents/dumpdecrypted/dumpdecrypted.dylib root@192.168.31.242:/var/mobile/Containers/Data/Application/D02582B3-66D3-4EF1-A6C2-6129D4E1B9CA/Documents/

另外,你也可以使用iTools或者iFunBox等工具来完成。

7.砸壳

从应用商店下载的应用是被苹果特殊加密过的,可执行文件被套上了一层保护壳,想 dump 出它的头文件等,需要先解密应用的可执行文件,这个过程就被称为砸壳

dumpdecrypted就是一款有名的砸壳软件,其工作原理是:将应用运行起来(iOS系统会先解密应用可执行文件再启动),然后遍历 Load Command 中所有 LC_ENCRYPTION_INFOLC_ENCRYPTION_INFO_64指令的信息,将对应解密后的数据从内存中 dump 出来,复写到 mach-o 文件中,得到一个新的可执行程序。

1
2
~ root# cd /var/mobile/Containers/Data/Application/D02582B3-66D3-4EF1-A6C2-6129D4E1B9CA/Documents/
root# DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/containers/Bundle/Application/9437BC53-681D-4F41-889C-5DE6DAAD33CD/Cbox.app/Cbox

这里的DYLD_INSERT_LIBRARIES是一个环境变量,我们的dumpdecrypted.dylib就是要通过这个环境变量注入到应用中。

上述命令执行完毕后会在当前目录生成Cbox.decrypted,即砸壳之后 App 的可执行文件。

1
2
root# ls
Cbox.decrypted dumpdecrypted.dylib ...

8.拷贝.decrypted

将 Cbox.decrypted 拷贝到自己的电脑上:

1
$ scp root@192.168.31.242:/var/mobile/Containers/Data/Application/D02582B3-66D3-4EF1-A6C2-6129D4E1B9CA/Documents/Cbox.decrypted /Users/davidli/Documents/decrypted

9.反汇编

接下来就可以对此砸壳后的文件进行静态分析了,如用class-dump导出 App 的头文件、用 Hopper Disassembler查看伪代码。

9.1.class-dump

class-dump 是用来 dump 目标对象的 类、分类、协议信息的工具,这一点与otool类似。它利用 OC 语言的 runtime 特性,将存储在 Mach-O 文件中的头文件信息提取出来,并生成对应的.h文件。在 传送门 下载最新安装包,然后把class-dump文件放到/usr/local/bin目录下, 在终端输入class-dump,显示class-dump的版本后,就可以正常使用 class-dump 命令了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ class-dump
class-dump 3.5 (64 bit)
Usage: class-dump [options] <mach-o-file>

where options are:
-a show instance variable offsets
-A show implementation addresses
--arch <arch> choose a specific architecture from a universal binary (ppc, ppc64, i386, x86_64, armv6, armv7, armv7s, arm64)
-C <regex> only display classes matching regular expression
-f <str> find string in method name
-H generate header files in current directory, or directory specified with -o
-I sort classes, categories, and protocols by inheritance (overrides -s)
-o <dir> output directory used for -H
-r recursively expand frameworks and fixed VM shared libraries
-s sort classes and categories by name
-S sort methods by name
-t suppress header in output, for testing
--list-arches list the arches in the file, then exit
--sdk-ios specify iOS SDK version (will look in /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS<version>.sdk
--sdk-mac specify Mac OS X version (will look in /Developer/SDKs/MacOSX<version>.sdk
--sdk-root specify the full SDK root path (or use --sdk-ios/--sdk-mac for a shortcut)

使用 class-dump 导出二进制文件中的.h

1
class-dump [--arch armv7] -H -s /目标二进制文件路径 -o /导出目录

导出的文件列表如下:

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
$ ls
AAAlertItem.h
AACloseNotifyReq.h
AACloseNotifyRes.h
AACloseReq.h
AACloseRes.h
AALaunchByMoneyReq.h
AALaunchByMoneyRes.h
AALaunchByPersonReq.h
AALaunchByPersonRes.h
AALaunchItem.h
AAListRecord.h
AAOperationReq.h
AAOperationRes.h
AAPayReq.h
AAPayRes.h
AAPaySuccReq.h
AAPaySuccRes.h
AAPayUrgeReq.h
AAPayUrgeRes.h
AAPayer.h
AAQueryDetailReq.h
AAQueryDetailRes.h
AAQueryListReq.h
AAQueryListRes.h
AARealNameItem.h
ABNewPersonViewControllerDelegate-Protocol.h
ABPeoplePickerNavigationControllerDelegate-Protocol.h
...

9.2.Hopper

class-dump 只能查看目标 APP 的头文件,无法查看.m 文件和具体的代码,所以就需要使用到反汇编器。这里介绍的是Hopper Disassembler,它是一款二进制反汇编器,能反编译出二进制文件中的所有函数和实现,包括伪代码以及控制流图(Control Flow Graph),支持 ARM 指令集并针对 OC 做了优化。官网 传送门,下载试用版就够用了。

将目标二进制文件拖到 Hopper Disassembler 面板中:(如果是第三方市场下载的越狱应用,须将.ipa文件后缀改为.zip,解压后在/Payload目录下找到.app文件,右键 显示包含内容 即可看到目标二进制文件)

disassembler1

选择需要反编译的架构之后点OK,接下来 Hopper 就会自动加载二进制文件的各个segment。加载完成后即可看到如下界面:

disassembler3

左边是符号 Label 等区域,中间是 ARM 的汇编代码,右边是相关信息栏。

在 Label 区搜索栏中输入想要的函数名,双击搜索结果列表中对应的行即可自动跳转到该方法的内存地址处。不过这里显示的都是 ARM 汇编指令,其中还包括了很多r0~r8等寄存器,阅读性不高。所以可以使用上面提到的伪代码功能,快捷键 Option + Enter 或者点击右上角面板中的 if(b)f(x)按钮即可自动弹出伪代码视图,这些就是具体的代码逻辑:

disassembler3

ps: Hopper 除了可以查看汇编代码以外,还可以直接对 Mach-O 文件进行修改,然后重新生成二进制文件,替换原二进制文件后重新打包即可实现某些特殊目的,如去广告、微信自动抢红包、VIP加速等。这些内容及ARM汇编指令待后面继续研究。。


相关参考:

#©沙梓社·吴航【iOS应用逆向工程】

#©iosre论坛


逆向工程
https://davidlii.cn/2019/04/26/reverse.html
作者
Davidli
发布于
2019年4月26日
许可协议