前言
在 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
.
后续继续分享 .