- 一、iOS逆向--加密算法
- 二、iOS逆向--通过Xcode安装ipa包
- 三、iOS逆向 - 脚本自动重签名ipa和代码注入
- 四、iOS逆向--方法交换的几种方式以及破坏微信注册
- 五、iOS逆向--MachoO文件
- 六、iOS逆向--dyld加载过程
- 七、iOS逆向-- fishhook原理分析
- 八、iOS逆向--fishHook源码分析
- 九、iOS逆向--LLDB调试
- 十、iOS逆向--chisel、LLDB、Cycript、MonkeyDev
- 十一、iOS逆向--Logos语法学习
上一篇文章我们学习了LLDB调试和Monkey的使用,现在我们来学习monkey的另外一种功能,编写Logos代码,对程序的动态调试。
一、hook目标文件的方法
首先我们创建一个Demo,在Demo中写上下面代码,然后编译运行:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self zjj_ClickTap:@"点击了屏幕"];
}
-(void)zjj_ClickTap:(NSString *)str{
NSLog(@"%@",str);
}
然后我们分别将.app文件和MachO文件拿出来
下面我们要对这个可执行文件进行Hook
我们创建一个Monkey工程,我们把刚才拿到的.app文件放到Monkey工程下的TargetApp文件夹里
然后在通过class-dump工具从刚才的machO文件里导出头文件
class-dump -H ZJJTestLogos -o ./headers/
我们发现我们的monkey工程有两个targets,上面那个就是我们的注入工程,下面那个就是我们注入的库,所以我们的代码卸载下面库里,下面库里我们发现有很多工具文件,例如fishHook等。现在我们来熟悉一下Logos:
我们发现Logos里面有两个文件,一个xm,一个mm文件,其中xm文件是给我写Logos代码的,mm文件是编译器将xm文件编译出来的代码,所以我们要把Logos代码写在xm文件里面。
我们在xm文件中写上下面代码
然后编译运行,点击屏幕,我们发现打印出来的是
说明已经hook了点击方法,我们来分析一下语法
%hook 和 %end之间算一个模块,%hook后面跟上类名,模块之间重写要hook的方法,方法我们通过class-dump工具将.h文件导出来获取,这样我们就能简单的通过monkey工程把方法给hook了
关于Logos我们可以看下介绍Logos介绍
二、Logos语法
hook方法组:%group
刚才我们了解了%hook,下面我们来了解下%group。
开发中,假如我们需要系统版本啦判断hook不同的方法,这个时候就需要用到%group了
%group group1
%hook ViewController
-(void)zjj_ClickTap:(NSString *)str{
NSLog(@"HOOK了第一组");
}
%end
%end
%group group2
%hook ViewController
-(void)zjj_ClickTap:(NSString *)str{
NSLog(@"HOOK了第二组");
}
%end
%end
%ctor{
%init(group1)%init(group2)
}
构造方法:%ctor
在Logos里面有一个函数是%ctor,这个是Logos里面的构造函数,我们可以在里面创建group
编译时候系统会先找%ctor构造函数,然后初始化group1和group2,因为group2是后加载的, 所以会覆盖group1的内容
我们可以通过系统版本来hook不同的group
%ctor{
NSString *v = [UIDevice currentDevice].systemName;
if(v.doubleValue >= 11.0){
%init(group1)
}else{
%init(group2)
}
}
我们运行一下,发现跟预料中的一样
但是最开始时候我们并没有去写group,但依然hook了,这是因为在Logos里面有一个隐式的group:_ungrouped,并且默认在构造函数%ctor里面去初始化它
析构函数:%dtor
既然有构造函数,那就有析构函数,在Logos里面析构函数就是%dtor,也就是相当于dealloc方法,也就是在应用挂掉的时候触发的
%dtor{
}
打印日志:%log
我们再来了解一下%log,我们在方法里面写上
%hook ViewController
-(void)zjj_ClickTap:(NSString *)str{
%log;
NSLog(@"HOOK了第一组");
}
%end
然后运行打印,
2020-11-20 20:41:56.962367+0800 ZJJTestLogos[9350:2251906] -[<ViewController: 0x105619e40> zjj_ClickTap:点击了屏幕]
我们发现会把方法调用者,方法名,参数值都打印出来了,这就是%log的功能
调回原方法:%orig
平时我们开发过程中在hook完方法后会调回方法,在Logos里面调回原方法就是%orig,假如方法有返回值,直接return %orig就行了
- 如果不想改变参数的话,直接调用%orig就行了
- 假如我们想改变返回值时候,这样就行了:%orig(@"123",@"234")
添加方法:%new
有时候我们需要对类进行添加方法,在Logos中,是使用%new:
@interface ViewController ()
@end
%hook ViewController
%new
-(void)addMethod{
NSLog(@"sdf");
}
-(void)zjj_ClickTap:(NSString *)str{
%log;
NSLog(@"HOOK了第一组");
[self addMethod];
}
%end
在我们调用addMethod方法时候,self需要去找函数,这个时候我们就需要将这个方法声明一下,所以需要在Logos里面加上interface,我们也可以拿到ViewController的头文件,然后在头文件里面添加addMethod方法,再在Logos里面import ViewController一下就行了
上面我们添加的是对象方法,我们再添加一下类方法:
%new
+(void)addClassMethod{
NSLog(@"addClassMethod");
}
调用时候我们可以用:
[self.class addClassMethod];
获取类名:%c
不过我们可以使用%c函数来获取类:
[%c(ViewController) addClassMethod];
这样就可可以调用类方法了
三、Monkey的hook本质
在之前iOS逆向-- fishhook原理分析里面有初探防护,也就是哼哦OK了方法交换函数,来避免方法呗hook,现在我们把那个包拿过来放到monkey工程里面,看看能不通过monkey工程hook到
原工程代码:
- (IBAction)btnClick1:(id)sender {
NSLog(@"按钮1调用了!");
}
- (IBAction)btnClick2:(id)sender {
NSLog(@"按钮2调用了!");
}
viewController的防护代码
+(void)load
{
//防护代码!
struct rebinding bd;
bd.name = "method_exchangeImplementations";
bd.replacement = myExchange;
bd.replaced = (void *)&exchangeP;
NSLog(@"防护来了!!");
struct rebinding rebs[] = {bd};
rebind_symbols(rebs, 1);
}
//防护代码
//函数指针变量
void (*exchangeP)(Method _Nonnull m1, Method _Nonnull m2);
void myExchange(Method _Nonnull m1, Method _Nonnull m2){
NSLog(@"检测到HOOK!!");
}
然后我们在monkey的xm文件里面写上下面代码:
%hook ViewController
- (void)btnClick1:(id)sender {
%log;
NSLog(@"HOOK了按钮");
}
%end
运行,点击按钮:
2020-11-20 21:30:28.574244+0800 002--初探防护[9408:2274225] -[<ViewController: 0x143e1c250> btnClick1:<UIButton: 0x143e21010; frame = (94 263; 30 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x282ba5980>>]
2020-11-20 21:30:28.574556+0800 002--初探防护[9408:2274225] HOOK了按钮
我们发现hook到了,说明我们对方法交换method_exchangeImplementations的防护是无效的,我们再就想到是不是对setIMP和getIMP,进行防护试试呢?
我们把防护代码改成下面
+(void)load
{
//防护代码!
struct rebinding bd;
bd.name = "method_exchangeImplementations";
bd.replacement = myExchange;
bd.replaced = (void *)&exchangeP;
NSLog(@"防护来了!!");
struct rebinding bd1;
bd1.name = "method_getImplementation";
bd1.replacement = myExchange;
bd1.replaced = (void *)&getIMP;
struct rebinding bd2;
bd2.name = "method_setImplementation";
bd2.replacement = myExchange;
bd2.replaced = (void *)&setIMP;
struct rebinding rebs[] = {bd1,bd2};
rebind_symbols(rebs, 2);
}
//防护代码
//函数指针变量
void (*exchangeP)(Method _Nonnull m1, Method _Nonnull m2);
IMP _Nonnull (*setIMP)(Method _Nonnull m, IMP _Nonnull imp);
IMP _Nonnull (*getIMP)(Method _Nonnull m);
void myExchange(Method _Nonnull m1, Method _Nonnull m2){
NSLog(@"检测到HOOK!!");
}
再次运行,拿到app包,放到monkey工程中去,然后运行
2020-11-20 21:30:28.574244+0800 002--初探防护[9408:2274225] -[<ViewController: 0x143e1c250> btnClick1:<UIButton: 0x143e21010; frame = (94 263; 30 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x282ba5980>>]
2020-11-20 21:30:28.574556+0800 002--初探防护[9408:2274225] HOOK了按钮
发现还是hook到了,这是为啥呢,考虑到我们在上一篇文章中防护代码要在进攻代码之前执行才行,我们在monkey工程中创建一个类
2020-11-20 22:02:48.858198+0800 002--初探防护[9436:2288652] 进攻代码来了
2020-11-20 22:02:48.779300+0800 002--初探防护[9436:2288652] 防护来了!!
我们发现进攻代码在防护代码之前,所以我们要把防护代码写在原工程的framework里面,因为frameword的执行时机要比类的执行时机早,并且注入的framework都是按照顺序依次排列的,先注入的在前面,后注入的在后面
我们在创建一个framework放到源工程里面,在编译,我们发现无法hook了,说明,monkeyhook方法是通过hook 它的setIMP和getIMP 函数进行hook的