从获得 APP 的所有类结构,到锁定目标类与函数,现在是时候注入函数了.
所谓 "注入函数",小程的意思是让 APP 执行到小程写的代码中,跟 "钩子" 的概念一致.小程把个叫作 iOS 上的 hook 的技术.
本文介绍 iOS 注入函数的办法.
在借助框架之前,小程先介绍一个简单的注入办法,让读者可以 "感性" 地认识到 "动态绑定" 所带来的注入.
(一)动态绑定的一个示例
(1)锁定注入点
随便找一个 APP,classdump 拿到所有类的结构信息.
比如,"微信" 有一个类是这样声明的:
viewcontroller 的继承
这个类继承于 UIViewController,也就是有 viewDidLoad 这个消息处理函数.
小程在这里演示把 MMUIViewController::viewDidLoad 函数给替换掉,让它执行到新的函数中.
(2)写注入代码
先找一个熟悉的编辑器,创建一个文件,命名为 hookwx.m,然后在里面添加这样的代码:
hookwx 的代码
然后是编译的事.可以直接使用 xcode 来编译出来. o 文件,也可以用 clang 来编译出来. o 文件.比如,小程演示时使用的是 iphone4 手机,也就是 armv7 指令集,所以可以这样编译出 obj 文件:
clang -c hookwx.m -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk
再使用 ld 来链接成动态库(dylib):
ld -dylib -lsystem -lobjc -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/ -o hookwx.dylib hookwx.o -framework Foundation -framework UIKit -ios_version_min 6.0
以上编译链接命令的参数跟小程使用的 sdk 版本有关,读者如果尝试的话应该选择对应的参数.
(3)拷贝 dylib 到 DynamicLibraries
scp hookwx.dylib root@192.168.1.100:/Library/MobileSubstrate/DynamicLibraries/
每个 APP 启动时都会载入 / Library/MobileSubstrate/DynamicLibraries/ 目录下所有的动态库,除非动态库有用 plist 来指定只给哪些应用加载,否则所有应用都会加载.
其实,合理一点的做法是创建一个 plist 文件,指定只让 "微信" 加载这个动态链接库,读者可以参考上篇文章介绍的办法来创建 plist,小程这里省掉这一步了.
(4)验证效果
启动 "微信",使用 socat 观察 log 输出,可以看到:
Dec 2 11:22:05 810 MicroMessenger[974] <Warning>: =======in initialize=================
...
Dec 2 11:24:48 810 MicroMessenger[974] <Warning>: -------------in new_viewDidLoad----------
也就是,"微信" 加载了小程写的 dylib,而且也执行到新的函数中.
以上的例子,只是 "感性地" 知道注入的办法,而在实际使用场景,更应该借助一些成熟的可以做到注入的框架.
小程较常见的两个框架,一个叫 fishhook,一个叫 MobileSubstrate.
fishhook,是 facebook 的一个开源工具,可以在运行时修改目标函数的地址,让控制点执行到自己的代码.因为需要知道目标函数的名字,这对于 c 运行时库的函数来说是适用的,或者对于能定位到函数名的情况也是适用的,但对于连名字都拿不到的情况(比如只能定位到代码地址)就不适用.如果只能拿到函数的地址,那可以考虑用 MobileSubstrate 的 MSHookFunction 来做到注入.
MobileSubstrate(也叫 CydiaSubstrate,以下简称为 MS),最大的一个作用,是可以动态绑定新的执行函数,这个功能已经能满足我们大部分的需求.比如,MS 提供的函数 MSHookMessageEx,可以用来对 oc 代码进行 hook,原理上利用了 oc 的 runtime 特性(运行时替换执行函数).
MS 提供的函数 MSHookFunction,可以用来对 c 代码进行 hook,比如很多 APP 在写文件时都会用到 write 或 fwrite 函数,那通过对这两个函数进行 hook,就能看到写入文件的数据,可以这样写代码:
MSHookFunction 示例
但直接使用 MS 的函数,并不是本文介绍的重点.从 "实用" 的角度,小程要介绍的是 iOSOpenDev 的使用.
(二)iOSOpenDev 的使用
theos 跟 iOSOpenDev,都是对 MS 库进行封装的开发工具包,这样的工具包可以简化开发的操作.这里介绍 iOSOpenDev 的使用.
安装 iOSOpenDev 后,就可以使用 xcode 来完成插件的开发,或者简单地生成 dylib 库.
(1)安装 iOSOpenDev
包装包地址:
http://iosopendev.com/download/
如果你安装成功,则不用参考小程失败的例子.
以下是小程安装失败并动手解决的例子.
安装时失败,/var/log/system.log 里面记录着 "安装器遇到了一个错误,导致安装失败.请联系软件制造商以获得帮助.".
虽然安装失败,但是在 / opt 下面还是创建了三个目录(红框内):
iosopendev 目录
在 iOSOpenDevSetup/bin 里面已经有一个 shell 脚本:iod-setup,这个是安装的脚本.
直接运行 iod-setup 来安装:sudo ./iod-setup base
发现总是在下载某个东西时失败,打开 iod-setup 来定位,发现有三个 downloadGithubTarball 的地方,
直接注释掉,然后手动去下载这三个东西,并拷贝到 iOSOpenDev 目录:
分别下载下面三个地址的zip包:
https://github.com/kokoabim/iOSOpenDev
https://github.com/kokoabim/iOSOpenDev-Xcode-Templates
https://github.com/kokoabim/iOSOpenDev-Framework-Header-Files
解压上面下载的zip包,拷贝:
sudo cp -r iosopendev-master/* /opt/iosopendev/
在iosopendev目录里面,sudo mkdir templates,然后:
sudo cp -r iosopendev-xcode-templates-master/* /opt/iosopendev/templates
在iosopendev目录里面,sudo mkdir frameworks,然后:
sudo cp -r iosopendev-framework-header-files-master/* /opt/iosopendev/frameworks
再次安装:
sudo ./iod-setup base
指定最新 xcode sdk:
sudo ./iod-setup sdk -sdk iphoneos
小程还遇到另一种情况:在一台 imac 上,xcode8.3.2,安装包失败后,直接 sudo ./iod-setup base,成功.
所以,上面不成功的情况,有可能是从 github 下载时网络上失败导致.
最终安装成功,表现为:
1.
~/library/developer/xcode 里面会多出
Templates/iosopendev
2.
打开 ~/.bash_profile
会看到:
export iOSOpenDevPath=/opt/iOSOpenDev
export iOSOpenDevDevice=
export PATH=/opt/iOSOpenDev/bin:$PATH
3.
启动xcode,新建工程,多出一个"iOSOpenDev"的模板.
就算使用 iOSOpenDev,也有必要安装 theos,否则编译时会有提示:
Preparing to run Xcode Build Phase for Logos Processor...
Failed to locate Logos Processor. Is Theos installed? If not, see http://iphonedevwiki.net/index.php/Theos/Getting_Started.
安装 theos 很简单(可以安装完 iOSOpenDev 后,再安装 theos):
brew install dpkg ldid
sudo Git clone --recursive https://github.com/theos/theos.git /opt/theos
(2)使用 iOSOpenDev 的示例
创建项目,iOSOpenDev -> Logos Tweak (安装后会有图标).
在项目中,可以找到一个后缀为 xm 的文件,这个文件就是写代码的地方.
iOSOpenDev 会根据 xm 的内容编译到 mm 中(xm 不是必须要有的,但 xm 的语法比 mm 中的好懂多了).
xm 文件里面的 #error 会提示你拷贝个 libsubstrate.dylib 过来.
到 / opt/iosopendev/lib 里面把 libsubstrate.dylib 拉到项目的 Frameworks 目录.
再拉进一个 UIKit.framework,因为 SpringBoard 在里面声明,而这个例子就是对 SpringBoard 进行 hook.
SpringBoard 是系统的组件,在启动机子时加载.
清空 xm 文件,写代码:
注入 SpringBoard 的一个例子
小程这里用的 UIAlertView 是旧 sdk 支持的,如果是新版本的 sdk,应该使用新的 "提示框" 类.
编译,成功的话,会生成对应的动态链接库,即 xx.dylib.也可以在项目中找到 xx.plist,对它进行修改,指定让哪一个 APP 启动时加载这个 dylib.
然后,把 dylib 与 plist 拷贝到 DynamicLibraries 目录(小程有多次提到了).其实,xcode 可以在编译后自行拷贝到手机,只需要这样配置下项目(当然也要保证电脑可以 ping 到手机):
在build settings页面,查找iOSOpenDevDevice,把内容设置为IP,比如:192.168.1.101 ,让xcode知道往哪台手机安装应用.然后编译并安装到手机:Project->Build For->Profiling.
注意,编译时,目标设备哪一项,应该选择 Generic iOS Device, 否则会遇到一堆错误(选择真机或模拟器都可能一堆编译错误).
安装后可以到 cydia 的 "已安装" 中看有没有你的应用,也可以 ssh 到手机后查看:
dpkg -l
重启机子(killall springboard),启动时会看到弹出的 alertview.
总结一下,本文内容较多,但主体是 iOSOpenDev 的使用,这个是注入 APP 的有效的工具.iOSOpenDev 的安装与使用,难度系数为中.
来源: http://www.jianshu.com/p/43793de23cc4