我的设备环境:
ios9.3.2(越狱)
OS X EI
这篇文章,详细介绍了在写一个tweak中遇到的问题。同时,提供了解决方案
我跟着他的脚步,效果是这样 的:
~这次让我学到很多,再次感谢这哥们~~~
1. 基本知识概述
这次实践的最终目的,是要实现“自由设定微信定位”的功能,这个功能的操作流程应该是:
- 打开 APP,输入一对经纬度数据
- 进入微信,APP 自动读取输入的经纬度数据,作为使用“附近的人”时的数据来源
1.1 tweak
而要实现这个功能,很显然并不是通过修改微信的工程源码。达成目的索要采取的手段是 tweak
。在 iOS 中,tweak
1.1.1 tweak 工作原理
在 OC 级别的 tweak 的工作原理主要是使用了 OC 语言的特性(那么对于 swift 编写的 APP 是否有效呢,先卖个关子)。OC 中对某个对象的方法的调用并不像 C++ 一样直接取得方法的实现的偏移值来调用,所以 C++ 方法与实现的关系在编译时就可确定。而 OC 中方法和实现的关系是在运行时决定的。在调用某个对象的方法时,实际上是调用了 obj_msgsend
向对象发送一个名称为方法名的消息,而我们可以替换这个响应这个消息的实现内容。OC 中比较有力的动态特性 Method Swizzing
1.1.2 tweak 编写套路
a.确定需求
这是当然的吧,你需要逆向的对象是什么,dylib?bundle?daemon?,想要实现什么样的功能。
b.定位目标函数
这里就需要使用后面会介绍到的 class-dump 了。dump 出所有头文件之后,寻找自己感兴趣的函数。实际上为了更好地分析内部实现以实现我们的功能,可能还会用到一些静态分析的工具。
c.编写 tweak
知道了需要 hook 的对象以及方法之后,我们就可以开始编写 tweak 了。编写 tweak 可以使用 Theos 或者是 iOSOpendev,这两者的关系就像是 git 跟 SourceTree 的关系一样。
1.2 MobieSubstrate
MobieSubstrate 是现有越狱插件运行的基础。它由3部分组成:
MobileHooker
它的作用是替换函数实现,也就是所谓的 hook 技术。比如大名鼎鼎的 Theos 就是基于这个 hooker实现的。
MobileLoader
它的作用是加载第三方动态链接库,也就是我们开发的 tweak。在 iOS 启动时,由 launchd 将 MobileLoader 载入内存,然后 MobileLoader 会 dlopen 所有/Library/MobileSubstrate/DynamicLibraries/
Safe Mode
bug 是不可避免的,如果寄生的 tweak 出现了 bug 导致 APP 崩溃,那么我们就需要一种机制,将 tweak 禁用掉,跟我们一个机会来 debug。而 Safe Mode 会捕获 SIGTRAP, SIGABRT, SIGILL, SIGBUS, SIGSEGV, SIGSYS 这6种信号,然后进入安全模式。
在 iOS9.3 越狱时 MobieSubstrate 已经自动安装上了。目前在 Cydia 中也更名为了 Cydia Substrate。
2. 砸壳
所谓砸壳,就是对 ipa 文件进行解密。因为在上传到 AppStore 之后,AppStore自动给所有的 ipa 进行了加密处理。而对于加密后的文件,直接使用 class-dump 是得不到什么东西的,或者是空文件,或者是一堆加密后的方法/类名。
2.1 使用 Clutch
砸壳可以使用工具Clutch 曾经在 Cydia 中可以直接下载安装 Clutch, 现在已经不行了,需要自己编译然后 scp 到真机上。下面开始来一步一步配置 Clutch:
在安装了 Xcode 之后(都看到这篇文章了没理由没安装吧),安装另外的 Command line tools:
|
接着将 Clutch clone 到本地:
|
Build:
|
./Clutch
目录下生成可执行文件 Clutch
,查看一下文件的权限,其他用户是否可执行:
|
|
将它 scp 到越狱机器上。这里需要越狱机器已经安装了 openSSh,并且建议同时安装 Mobile Terminal,更改你的默认密码:
- 输入命令
su root
- 输入默认密码 alpine
- 输入
passwd root
- 来更换你的 root 密码
拷贝文件到你的机器上:
|
接着 ssh 到你的机器上,然后使用 clutch 列出所有已安装的 APP:
|
根据编号或者 bundleID 来执行 dump
|
结果得到了错误信息,说当前版本的 clutch 不支持 dump watchOS2 的 APP…
com.tencent.xin contains watchOS 2 compatible application. It’s not possible to dump watchOS 2 apps with Clutch 2.0.3 at this moment.
只好尝试用 dumpdecrypted.dylib 来砸壳:
2.2 使用 Dumpdecrypted
XXX/PrivateFrameworks
这个目录。直觉告诉我不解决这几个 warning 会导致以后一些奇奇怪怪的问题。
Google 之后发现,出现这个问题是因为 Xcode 7.3(ios sdk 9.3) 移除了所有 private framework。但是我们可以下载 ios sdk 9.2 然后解压到相应目录,并且将 Makefile 里面的 SDK 路径修改为下载下来的 9.2sdk。
9.2sdk可以在这里下载到,修改 Makefile 中的 SDK 为实际的 9.2sdk 路径:
|
dumpdecrypted.dylib
文件 copy 到 APP 的沙盒目录的 Documents 目录下。我们知道所有 APP 的沙盒路径都在/var/mobile/Container/Data/Application/XXXXX/
里面,但是我们不知道 XXXXX 到底是哪一个 APP。尝试过多种方法失败之后(可惜 Cycript 不支持 iOS9.3.X 啊!),推荐使用 iTools 之类的工具拷贝,比较直观,需要先安装 afc2add 补丁。接着开始砸壳:
|
executalbe 的路径可以用下面的命令来查找:
|
在当前目录下就会生成 WeChat.decrypted, 这个就是砸过壳的微信了。把它 scp 回来本地之后,就可以开始接下来的操作了。
DYLD_INSERT_LIBRARIES另外,其实操作完成之后发现,dumpdecrypted.dylib
3. Class Dump
Class Dump 是为了导出微信的头文件,能够让我们得以浏览感兴趣的类中暴露出来哪些方法,找到需要 hook 的方法名,后面编写 tweak 会需要使用到。
|
命令执行完成之后,会在当前目录下生成一个 output 文件夹,里面有导出来的所有微信头文件,包括使用到的第三方 sdk。可以将所有这些头文件放到一个空工程里面方便查看。一共 7000+ 个头文件,导入需要一定时间…
凭借参考文章中作者的提示,MicroMessengerAppDelegate.h
之所以可以使用 class-dump 来还原类的 @interface 部分,还是多亏了 iOS 采用的文件格式 Mach-O 中包含了足够多的 metadata。
再来看看我们需要实现的功能,是改变我们的位置从而改变附近的人,但是手动猜测类名关键字搜索始终是很低效的行为。其实除了排除法和一个一个推测之外,还可以使用 Reveal 这个工具来帮助我们定位。
4. 反编译静态分析
class-dump 帮助我们列出了所有 header 文件,让我们对项目结构大体有个认识。不过,我们更加好奇的,始终是 .m 文件里面的实现。这里使用了 Hopper Disassembler 这个工具。注意,导入的文件应该是砸壳之后的文件。否则你看到的是一堆乱码,犹如一开始那傻逼的我…
保险起见,可以先确认一下 .decrypted 是否已经解密了。首先查看一下对应的二进制文件包含哪些架构:
|
可以看到,砸壳后的二进制文件只包含砸壳时使用的机器的架构,可能是由于使用了 bitCode?接下来使用 otool 来输出 app 的 load commands,查看 cryptid 这个标志位来判断 app 是否被加密了,1代表加密了,0代表被解密了。otool 是 Xcode tool chain 的一部分,所以并不需要额外安装:
|
输出:
|
可以看到确实是被解密了。丢进去 hopper 之后,可以看到美丽的画面:
由于 IDA 暂时不能支持64位架构的包,而 hopper 试图在生成伪代码的时候也提示不能支持包对应的 cpu,所以这里建议,其实可以在 iTools 或者 pp助手 之类的第三方分发渠道下载微信,一般都是已经脱壳了的。然后就可以直接反编译包里面的 armv7s 架构。
5. tweak 小试
这里暂时先不进行我们的目标 tweak 开始,先来一个小样练练手:实现微信一打开就弹出 Hello World 对话框的功能。
5.1 创建 Theos 工程
创建 Theos 工程在 Terminal 中完成:
1) 更改工作目录到常用的 iOS 工程目录,然后使用下面命令启动 NIC(New Instance Creator):
|
2) 这时候 Theos 需要我们输入工程的名字,以及 deb 包的名字,类似于 bundle identifier:
3) 输入作者名称之后,Theos 要求我们输入 MobileSubstrate Bundle filter,也就是 tweak 作用对象的 bundle identifier,这里我们填上微信的 id,可以在解压后的 ipa 包中的 plist 文件中找到:
4) 最后我们需要输入指定 tweak 安装完成之后需要重启的应用,以 bundle identifier 来表示,这里还是填上微信:
到这里工程便创建完成了,会在当前目录下生成工程文件夹。
5.2 定制工程文件
工程目录中只有4个文件,但是这4个文件是构成我们的 tweak 的4根顶梁柱
1) control
control 文件记录了 deb 包管理系统所需要的基本信息,会被打包到最后生成的 deb 包中。一般无需修改里面的内容。
2) Project_Name.plist
这个跟工程名同名的 plist 文件跟 APP 中的 Info.plist 左右类似,记录一些配置信息,描述了 tweak 的作用范围。
其中 Bundles 字段下的数组指定了若干个 bundle identifier 作为 twewak 的作用对象。默认是 spring board,这里我们替换成微信的 bundle identifier:com.tencent.xin。可以在解压后的 ipa 包中的 plist 文件里找到。
除了使用 Bundle 指定,也支持使用另外两种指定方式:
- Class: 对列表中指定的类起作用
- Executables: 对指定的可执行文件名起作用
这三种配置方式可以同时指定,但是这里有个小问题。在同时指定的时候,一个文件只有满足每种 Filter 中的 array 里的至少一个条件,tweak 才能生效。如果想要满足任意一种 Filter 下的任意一个条件就能生效的话,就必须添加一个键值对:Mode: Any
3) Makefile
Makefile 文件制定编译和链接所涉及的文件、框架、库等信息,将整个过程自动化。需要 Makefile 文件,是因为 theos 就是一个跨铭泰的 Makefile 系统。工程中的 Makefile 内容如下:
自动生成的内容一般无需更改,除非引入了其他第三方库,或者添加了其他的源码文件,则需要增加引入的新文件。
4) Tweak.xm
Theos 创建工程之后,默认生成的模板源文件是 Tweak.xm,源文件中包含了基本的 Logos 语法的说明,具体写法就不展开了,书中很详细。其中有一些预处理命令值得注意:
- %hook: 指定需要 hook 的 class
- %log: 这个指令在 %hook 内部使用,可以将 log 的内容写入 syslog(/var/log/syslog)。这种是将 log 写入文件的方法。在 iOS9.3 这个文件没有自动生成,如果需要以这种方式查看 log,则需要配置一下,配置方法可以参考 wiki 中的 1.3 内容。而我使用的是 1.1 的方法,利用 socat 这个命令行工具,可以在 cydia 中安装,按照 wiki 的方法实时查看 log
- %orig: 在 %hook 内部使用,执行被 hook 的函数的原始代码。还可以利用它以 C++ 函数的调用方式改变原始函数传入的参数
- %group: 用于将 %hook 分组,与 %init 配合使用,在按条件初始化 hook 代码时非常有用
- %ctor: 用于系统自动初始化默认的未分组 hook 代码
- %new: %hook内部使用,用于给现有的 class 添加新的方法
- %c: %hook内部使用,动态获取一个类的定义
关于详细的 logo 语法,可以到这里一探究竟
5.3 编写 tweak 源码
MicroMessengerAppDelegate
,头文件中也暴露了这个方法出来,因此可以推断出 hook point,编写 .xm 文件如下:
%hook
5.4 编辑 MakeFile
- THEOS_DEVICE_IP: 指定了目标机器的 ip 地址,因为我接下来采用命令行的方法将包安装到机器上。这需要用到简单的 ssh 命令,所以需要越狱的机器安装 OpenSSH。
- ARCHS: 指定了开发的架构,也就是这个包需要支持的架构。因为我的机器是 iPad Mini 4,所以只需要指定它使用的架构的就可以了。
- TARGET: 指定 tweak 应该工作在哪个 SDK 版本下。这里需要参照目标机器的系统版本,毕竟不用的系统版本可用的 SDK 也不一样。
- wechat_hook_test_FRAMEWORKS: 指定需要导入的 framework,由于使用到了 AlertView,所以这里需要导入 UIKit。另外值得一提,如果需要导入 private framework 的话可以使用
XXX_PRIVATE_FRAMEWORK
- ,但是 iOS SDK 9.3 已经去除了 private frameworks,最后具有 private frameworks 的 sdk 版本是 9.2,使用 sdk9.2 编译打包的 tweak 不知道能否工作在 iOS 9.3 中,待验证。
5.5 编译
走到这步,就说明我们已经看到摸着逆向之路的门框了。想想都有点小激动。
Theos 采用与 debian 相同的 make 命令来编译。执行 make 命令:
从输出来的信息可以看到,Theos 完成了预处理,编译,链接,签名等一系列动作。编译完成之后会发现当前目录的 .theos 目录中多出了若干个 .dylib 文件,这就是 tweak 的核心部分。
5.6 打包
打包使用的 make package 命令来自 Theos 本身,其实就是先 make 然后再 dpkg-deb,运行时报了一个错误跟一个警告:
- 错误指的是,package name 只能包含小写的字母还有数字…于是只好将所有 wechat_hook_test 都替换成 wechathooktest…包括 control, Makefile 还有 plist 文件名
- 关于警告,在 github 看到解释,大意上是说可以不用管这个 warning,看得不太懂,希望看懂的朋友告诉一声,链接在此
可以看到在 packages 文件夹中生成了我们需要的包。我这里有两个,是因为我打包了两次。
5.7 安装
最后,要将这个 deb 文件安装到 iOS 中去。可以用图形化的方法,将 deb 包丢到机器里面,然后用 iFile 来安装。我这里采用了命令行的安装方法。为了简化步骤,让进程使用 ssh 时无需输入密码,这里先做一些操作,就是将我们的 rsa 公钥拷贝到越狱机器上:
1) 先 ssh 到设备上,以 root 身份登录 iOS 设备,输入 ssh-key gen
,会生成 /var/root/.ssh
目录,并且里面有一对秘钥
2) exit 回本机,将本机的公钥 cp 到用户目录中,并且更名为 authorized_keys,然后将它 scp 到/var/root/.ssh
中
3) 再尝试 ssh 登录到 iOS 设备,应该不用输密码了
接着调用 make package install
命令完成编译打包安装一条龙服务,报了一个错误,说找不到进程:
这个错误是因为我误将 Makefile 中需要 kill 的进程名称填成了 bundle identifier,将它改成 WeChat 就好了:killall -9 WeChat