[iOS 逆向 7] Hook

概述

前面经过分析与调试,了解了程序的执行逻辑。现在想改变执行逻辑,脑海里也大致有了新逻辑,例如让判断是否是会员的函数始终返回 true。那么怎么把新逻辑应用到程序中呢?这是本文要介绍的。

Hook,钩子,用来改变程序执行流程。iOS 中有以下几种方式:

  • Method Swizzling:利用 OC runtime 特性,对 OC 方法重排。
  • fishhook:利用 Mach-O 格式程序加载原理修改符号表,对 C 函数重排。
  • Cydia Substrate:Cydia 底层框架,提供对 OC 方法、C 函数的重排接口。

Hook 原理

Method Swizzling

原理比较简单,概括就是:OC 方法 = 方法名 + C 函数地址,我们交换两个 OC 方法的实际 C 函数地址,即实现方法重排。细节可以直接看以前写的文章 。

fishhook

fushhook 的核心思想就是修改懒加载和非懒加载符号表,源码地址。
前面提到过懒加载与非懒加载符号绑定过程,实际上就是苹果的位置代码独立技术(PIC)。由于地址空间随机化的存在,不能使用固定的地址,于是调用符号实际去调用函数桩,函数桩内读取符号表上的值并调用。目标功能函数的真实地址由 dyld 获取后写入数据段中的符号表。fishhook 就是利用 PIC 技术的特点,实现 C 语言符号的“动态”特性。给定目标函数名和要替换的函数指针,fishhook 对目标函数在符号表中的值重新绑定,让其指向要替换的函数地址,这样就实现了 C 函数的重排。

Cydia Substrate

Cydia 底层框架,提供:
MSHookMessageEx 函数用来 hook OC 方法,原理就是对上述 Method Swizzling 的封装。
MSHookFuntion 函数可以 hook 任意 C 函数,原理是直接修改了内存中的汇编指令,调用目标函数时,会直接跳转到自定义函数。这种 hook 方式只能在越狱设备上使用,在非越狱设备上如果想使用这种思路,必须修改静态文件中的指令。

Hook 工具

Theos

为了更便捷地进行逆向开发,有人封装了上面的 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 给原函数打断点,观察函数执行逻辑是否已改变。

更多语法

逆向工程中可能有更复杂的需求,下面介绍一些常用语法。

获取类对象 %c

使用原 App 内的类时,必须使用 runtime 函数动态获取:Class objc_getClass(const char *aClassName),Theos 将其封装成了一个简洁的语法 %c(aClassName)

个别文件应用 MRC

如果源文件有两个,MyTomato_FILES = Tweak.x MySwizzle.m,其中 MySwizzle.m 必须用 MRC,则单独为其指定编译参数:MySwizzle.m_CFLAGS = -fno-objc-arc

给类添加方法 %new

在要 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 编译文件集合。

MonkeyDev

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。

注意

第八九章还没写,有机会再补充,不过不重要。
八 其他知识
九 一些工具的源码解读

你可能感兴趣的:(iOS逆向)