前面经过分析与调试,了解了程序的执行逻辑。现在想改变执行逻辑,脑海里也大致有了新逻辑,例如让判断是否是会员的函数始终返回 true。那么怎么把新逻辑应用到程序中呢?这是本文要介绍的。
Hook,钩子,用来改变程序执行流程。iOS 中有以下几种方式:
原理比较简单,概括就是:OC 方法 = 方法名 + C 函数地址,我们交换两个 OC 方法的实际 C 函数地址,即实现方法重排。细节可以直接看以前写的文章 。
fushhook 的核心思想就是修改懒加载和非懒加载符号表,源码地址。
前面提到过懒加载与非懒加载符号绑定过程,实际上就是苹果的位置代码独立技术(PIC)。由于地址空间随机化的存在,不能使用固定的地址,于是调用符号实际去调用函数桩,函数桩内读取符号表上的值并调用。目标功能函数的真实地址由 dyld 获取后写入数据段中的符号表。fishhook 就是利用 PIC 技术的特点,实现 C 语言符号的“动态”特性。给定目标函数名和要替换的函数指针,fishhook 对目标函数在符号表中的值重新绑定,让其指向要替换的函数地址,这样就实现了 C 函数的重排。
Cydia 底层框架,提供:
MSHookMessageEx 函数用来 hook OC 方法,原理就是对上述 Method Swizzling 的封装。
MSHookFuntion 函数可以 hook 任意 C 函数,原理是直接修改了内存中的汇编指令,调用目标函数时,会直接跳转到自定义函数。这种 hook 方式只能在越狱设备上使用,在非越狱设备上如果想使用这种思路,必须修改静态文件中的指令。
为了更便捷地进行逆向开发,有人封装了上面的 Hook 原理,编写了注入、拦截工具 Theos。
先用 brew 安装 ldid、fakeroot、dpkg,其中 dpkg 需要指定版本 1.18.10,可以通过执行 brew install --from-bottle https://raw.githubusercontent.com/Homebrew/homebrew-core/7a4dabfc1a2acd9f01a1670fde4f0094c4fb6ffa/Formula/dpkg.rb
安装。最近 raw.githubusercontent.com 的 DNS 被污染,可能会出现 443 Connection Refused 的错误,可以修改系统中的 hosts 文件,添加该网站的 IP 地址 199.232.28.133 来恢复访问。api.github.com 同理。
然后执行 git clone --recursive https://github.com/theos/theos.git
下载 Theos,下载后将 Theos 根目录和 bin 目录添加到环境变量,后面会用到。
export THEOS=/path/of/theos
export PATH=$THEOS/bin:$PATH
export THEOS_MAKE_PATH=$THEOS/makefiles
至此安装完成。
执行 nic.pl 开始创建项目,模版选择 iphone/tweak,按提示输入项目名,项目 ID,创建者,目标应用的 Bundle ID 等信息,创建完成后,自动生成四个文件:Makefile,编写代码的 Tweak.x,指定注入目标 Bundle ID 的 plist 文件,指定 deb 包的一些信息的 control。
Theos 为了让逆向开发更简便,对前面的 hook 原理进行封装,然后提供了一种新的语法。即使是没有读过 runtime 源码、不了解程序加载等底层问题,使用 Theos 仍然可以写 hook 代码做逆向开发。例如,我要 hook 番茄Todo 应用中 SystemUtil 类中的 +shouldSH 函数,在 Tweak.x 中编辑如下:
%hook SystemUtil
+ (_Bool)shouldSH {
return 1;
}
%end
编译时,Theos 语法会被转换为真正的代码。其中 %hook 类名
用于指定要 hook 的类,然后在里面写需要 hook 的方法,如果参数或返回值涉及到闭包,则使用 id 类型替代。%log 打印日志,%orig 调用原来的方法。
然后编辑 Makefile,Theos 已自动生成了部分内容,包括目标进程名,项目名,源码文件,编译参数。
INSTALL_TARGET_PROCESSES = TomatoTime
include $(THEOS)/makefiles/common.mk
TWEAK_NAME = MyTomato
MyTomato_FILES = Tweak.x
MyTomato_CFLAGS = -fobjc-arc
include $(THEOS_MAKE_PATH)/tweak.mk
一般还需要手动添加目标架构和目标系统版本:
ARCHS = arm64
TARGET = iphone:latest:8.0
在命令行执行 make,没有错误的话会生成一个 .theos 文件夹,里面包含一个 MyTomato.dylib。可以手动把这个 dylib 注入到目标 App,当然 Theos 肯定也封装了这个步骤,执行 make package 打包成 deb,成功后会生成 packages 文件夹,里面包含一个 deb 文件。现在要把该文件安装到手机中,如果用 ssh 传输则用手机的 IP 以及端口号 22,如果用 USB 连接,先用 iproxy 转发iproxy 3333 3333
,端口号是 3333,IP 用 localhost。然后编辑 Makefile,添加对应方式的值:
export THEOS_DEVICE_IP=localhost
export THEOS_DEVICE_PORT=3333
执行 make install 即可安装。安装成功后在手机的 /Library/MobileSubstrate/DynamicLibraries 目录下可以看到 MyTomato.dylib 和 plist 文件。如果想查看动态库有没有被加载,除了直接观察 App 运行外,电脑打开 Console.app,USB 连接手机后打开 App 就可以看到 App 的日志。编译前在代码文件里写一个 __attribute__((constructor))
函数,函数中打印自定义的信息,即可在日志中观察到。或者用 lldb 给原函数打断点,观察函数执行逻辑是否已改变。
逆向工程中可能有更复杂的需求,下面介绍一些常用语法。
使用原 App 内的类时,必须使用 runtime 函数动态获取:Class objc_getClass(const char *aClassName)
,Theos 将其封装成了一个简洁的语法 %c(aClassName)
。
如果源文件有两个,MyTomato_FILES = Tweak.x MySwizzle.m
,其中 MySwizzle.m 必须用 MRC,则单独为其指定编译参数:MySwizzle.m_CFLAGS = -fno-objc-arc
。
在要 hook 的类内,声明 %new,下面写方法即可。添加的是实例方法,熟悉 runtime 的话可以猜出来实际是封装的 class_addMethod 函数。如果想给某个类添加静态方法呢?根据 runtime 原理,需要依次用 objc_getClass 和 object_getClass 获取元类对象,然后 class_addMethod 给元类添加方法。由此可见,Theos 并不是万能的,只有熟悉 runtime 才能灵活地编写代码。
如果有添加图片等文件需求,则需要在项目根目录新建 layout 文件夹,这个文件夹将来实际映射到手机根目录。因此一般都会在 layout 里建多层目录使用,例如 layout/Library/Application Support/TomatoTime/。
和命令行编译差不多,将库文件复制到项目中自定义的目录下,指定 CFLAGS、LDFLAGS、LIBRARIES、FRAMEWORKS,在代码中直接可以导入头文件即可使用。
如果要对某个类中的所有方法进行 hook,监控该类所有的函数调用,则对 class-dump 得到的头文件使用 logify.pl 命令,输出所有方法的 hook 函数。如果涉及到未知的类,手动添加 @class 声明。输出结果保存到一个 .xm 文件或其他位置,将该文件添加到 Makefile 编译文件集合。
Theos 是一个命令行工具,对于习惯使用 Xcode 的 iOS 开发者并不友好。于是《iOS 应用逆向与安全》的作者开发了 MonkeyDev,相当于对 Theos 再次封装,让越狱开发更人性化,是一款插件开发集成神器。
在完成 Theos 安装后,下载 MonkeyDev,然后执行 sudo ./MonkeyDev/bin/md-install
。该文件夹下也有卸载、更新脚本。
打开 Xcode 创建项目,在 iOS 一栏最下面选择 Logos Tweak,创建完成后即可看到 Theos 中熟悉的几个文件,按照 Theos 项目的规则编辑即可。其中的 Makefile 被 Xcode 工程代替,让不熟悉 make 的开发者也可以开发越狱插件。因此前面修改 Makefile 的内容现在改为修改项目文件,在项目 Build Settings 最后一块,主要是设置设备 IP 和端口号。需要注意的是,项目默认 theos 和 MonkeyDev 的路径保存在 /opt/theos 和 /opt/MonkeyDev,修改项目文件或者把对应的文件放到 /opt 目录下。项目编写完成后,只需要 Build 就完成了 Theos 的三步操作:make,make package,make install。
第八九章还没写,有机会再补充,不过不重要。
八 其他知识
九 一些工具的源码解读