前言
在 Hook / fishHook 原理与符号表 这篇文章中我们提到过 基本的 hook 的使用和原理分析 , 但是在逆向中 , 由于我们需要大量的去 hook 来调试业务逻辑等等 , 我们这么一个个的去 hook 别人的方法也太麻烦 ( ben ) 了些 .
在实际逆向开发过程中 , 我们最常用到的 代码注入 / 调试 使用的 hook 方式是 Cydia Substrate , 它能帮助我们非常简单的去 hook 某个类的某个方法 , 获取某个类的属性 , 再 hook 之后继续调用原本的函数 等等 , 它使用的就是 Logos 语法.
因此 , 对于 iOS 逆向开发人员这个技能是必不可少的 .
Cydia Substrate
Cydia Substrate 是绝大部分 tweak ( 本质是 dylib ) 正常工作的基础,主要分为三部分:Mobile Hooker , Mobile Loader,Safe mode
Mobile Hooker
也就是本篇文章重点讲述的部分 .
顾名思义用于 HOOK。它定义一系列的宏和函数,底层调用 objc 的 runtime 和 fishhook 来替换系统或者目标应用的函数. 其中有两个函数:
-
MSHookMessageEx主要作用于Objective-C方法void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result) -
MSHookFunction主要作用于C和C++函数void MSHookFunction(voidfunction,void* replacement,void** p_original)Logos语法的%hook就是对此函数做了一层封装 .
Mobile Loader
Mobile Loader 用于加载第三方 dylib 在运行的应用程序中。启动时 Mobile Loader 会根据规则把指定目录的第三方的动态库加载进去,第三方的动态库也就是我们写的破解程序 .
safe mode
破解程序本质是 dylib,寄生在别人进程里。 系统进程一旦出错,可能导致整个进程崩溃,崩溃后就会造成 iOS 瘫痪。所以 Cydia Substrate 引入了安全模式,在安全模 式下所有基于 Cydia Substrate 的三方 dylib 都会被禁用,便于查错与修复。
在安全模式里,所有基于
Cydia Substrate的第三方dylib会被禁用,便于查找于修复;如果设备因为dylib的原因无法进入系统,比如,开机一直卡在白苹果上,或者进度圈不停地转—>home+lock+然后音量上键禁用Cydia Substrate,系统重启后再查错与排修,修复后重启iOS,Cydia Substrate会自动重启)
说了很多前导知识 , 主要是为了大家清楚 Cydia Substrate 与 Logos 的关系 .
Logos
概述
Logos : iphonedevwiki.net/index.php/L…
Logos 语法其实是 Cydia Substruct 框架提供的一组宏定义。便于开发者使用宏进行 HOOK 操作。语法简单,功能强大且稳定。
Logos 语法分为三大类:
-
Block level- 这一类型的指令会开辟一个代码块,以
%end结束。%group、%hook、%subclass、%end
- 这一类型的指令会开辟一个代码块,以
-
Top level- 这个
Top Level指令不放在Block Level中。%config、%hookf、%ctor、%dtor
- 这个
-
Function level- 这一块的指令就放在方法中。
%init、%class、%c、%orig、%log.
- 这一块的指令就放在方法中。
简单语法解释
HOOK 方法
%hook ClassName
//对象方法
- (void)instanceMethod{
//输出方法调用的详细信息
%log;
//继续执行原本方法
%orig;
}
//类方法
+ (void)classMethod{
//输出方法调用的详细信息
%log;
//继续执行原本方法
%orig;
}
%end
这样就完成了一个方法的 hook , 不管从简便性 , 可读性方面都得到了大幅度的提高 .
新增方法
//新增方法
%hook ClassName
//新增对象方法
%new
- (void)newInstanceMethod{
}
%new
//新增类方法
+ (void)newClassMethod{
}
%end
这样就可以为类添加实例方法 / 类方法 .
注意 : 一个 %new 添加一个方法 , 无须 %end .
group 分组
请看一下以下代码
//分组
%group group1
%hook ClassName
- (void)instanceMethod{
NSLog(@"第一组 hook到");
}
%end
%end
%group group2
%hook ClassName
- (void)instanceMethod{
NSLog(@"第二组 hook到");
}
%end
%end
%ctor{
%init(group1)%init(group2);
}
打印结果证明是第二组 hook 到了 .
先解释下 %ctor 为 constructor 的简写 , 即构造函数 ,
同理 %dtor 就是析构函数 .
那么为什么是最后 hook 的是第二组呢 ?
构造函数中 先后去执行了第一组和第二组 , 那么第二组的
hook就会覆盖掉第一组hook的效果 , 因此显然打印会是第二组加载到了 .
类方法调用
Objective-C 中调用类方法我们一般是 [ClassName ClassMethod] 的方式 , 在 Logos 中这么写会报错 , 因此需要用到 %c , 其写法为 :
[%c(ClassName) ClassMethod] ;
可简单理解为 getClassFromString .
案例
( 模拟逆向过程中 使用 Logos 进行 hook )
- 先准备一个工程 , 新建工程 , 添加一个方法
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)testFunc{
[[[UIAlertView alloc] initWithTitle:@"提示" message:@"源方法" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:@"取消", nil] show];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self testFunc];
}
@end
- 选择真机 , 开发者 ,运行工程 , 找到工程
Mach-O class-dump -H LBLogosOriginalDemo -o ./headers/不熟悉class-dump的同学可以阅读一下 重签应用调试与代码修改 (Hook) 这篇文章 .
- 得到头文件查看类与方法 .

- 新建
monkey工程 , 选择开发者 , 选择真机 . 先运行一下空monkey工程 ( 安装描述文件 ) . - 把我们工程的
app包放到target app中
- 编写
logos
#import <UIKit/UIKit.h>
%hook ViewController
- (void)testFunc{
%log;
[[[UIAlertView alloc] initWithTitle:@"提示" message:@"已hook方法" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:@"取消", nil] show];
}
%end
-
运行
monkey工程 . -
点击屏幕

-
同时 , 控制台 (
%log) 打印如下 :
至此 , 一个逆向过程中实际的 hook 写法我们就完成了 . 实际逆向开发中 , 我们往往还会结合 动态调试 ( lldb , cycript) , 静态分析 ( 汇编代码 ) , View-Debug 等方式来分析业务逻辑等等 . 但往往实际代码注入 , 都是采用如上方式进行的 hook .
后续继续分享 .