iOS 逆向开发13:代码注入

2021-08-20  本文已影响0人  differ_iOSER

iOS 逆向开发 文章汇总

目录



相关资料下载链接: https://pan.baidu.com/s/1MKKYtjasGPblXDSWGwjz6w 密码: c3so

一、代码注入

一般修改原始的程序,是利用代码注入的方式,注入代码就会选择利用FrameWork或者Dylib等三方库的方式注入。

1.1 Framwork注入演示

接着上一篇文章将WeChat重签名安装到了真机上:

项目-->Products-->Show in Finder-->显示包内容-->将WeChat可执行文件拷贝出来-->将WeChat 拖入到MachOView

MachO通过Load Commands告诉dyld需要加载的各种库以及库的位置(@rpath)参考:iOS-开发进阶05:动态库

1.1.1 创建动态库MyHook

1.1.2 添加Inject类

Build项目既可在包内容中看到MyHook添加到WeChat的Frameworks中了

Inject.m中添加如下代码并重新Build、安装到手机

+ (void)load {
    NSLog(@"\n\n注入成功!\n\n");
}

现在运行项目Inject.m中的load方法是不能执行的。虽然WeChat的Frameworks中加入了MyHook,但WeChat的MachO中没有MyHook 相关的Load Command。因此需要修改MachO才能让dyld加载MyHook。

1.1.3 修改MachO
yololib:专门用来修改MachO文件的(添加Load Command字段)
修改appSign.sh脚本,最后一行添加修改MachO的命令

./yololib "$TARGET_APP_PATH/$APP_BINARY"  "Frameworks/MyHook.framework/MyHook"

拷贝yololib到appSign.sh同级目录-->Command + Shift + K --->Command + R直接安装到真机

可以看到MachO中已经添加了MyHook 相关的Load Command

1.2 Dylib注入演示

新建项目Dylib注入--->安装项目到真机--->拷贝Shell脚本、ipa包、yololib--->Build Phase添加脚本执行命令--->将重签名的包安装到真机上

1.2.1 创建Dylib:MyHooK

1.2.2 修改Base SDK和签名

1.2.3 Copy Files

1.2.4 MyHooK.m添加代码

+ (void)load {
    NSLog(@"\n\n注入成功!!!\n\n");
}

1.2.5 编译MyHooK和项目(Dylib注入),将libMyHooK.dylib添加进Frameworks中

1.2.6 修改appSign.sh脚本,最后一行添加修改MachO的命令

./yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libMyHooK.dylib"

1.2.7 安装项目到真机
Command + Shift + K --->Command + R直接安装到真机(不要先Build再安装不然会导致ibMyHooK.dylib无法添加进Frameworks中)

二、MethodSwizzle

利用OC的Runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的。主要用于OC方法

多种HOOK方式

  1. class_addMethod方式:
    利用AddMethod方式,让原始方法可以被调用,不至于因为找不到SEL而崩溃

  2. class_replaceMethod方式:
    利用class_replaceMethod,直接给原始的方法替换IMP

  3. method_setImplementation方式:
    利用method_setImplementation,直接重新赋值原始的新的IMP

2.1 拿到注册方法

2.1.1 继续使用在上面1.1的WeChat项目,安装WeChat到真机,开启Debug调试

Target:WCAccountLoginControlLogic(控制器)
Action:onFirstViewRegister(注册方法)

2.1.2 添加MethodSwizzle方法

#import <objc/runtime.h>
+ (void)load {
    NSLog(@"\n\n注入成功!\n\n");
    
    Class class = objc_getClass("WCAccountLoginControlLogic");
    
    SEL originalSelector = @selector(onFirstViewRegister);
    SEL swizzledSelector = @selector(my_register);
    
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
    
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

- (void) my_register {
    NSLog(@"my_register");
}

2.2 拿到登录密码

2.2.1 使用class-dump静态分析WeChat可执行文件

./class-dump -H WeChat -o ./headers/

由于onNext方法没有参数因此是通过控件传递的参数

使用class-dump只能dump出OC相关的方法,不能dump Swift的方法。并且dump出的也不是头文件,可执行文件中本来也没有头文件,class-dump只是类似头文件形式的格式方便我们查看

2.2.2 代码中拿到登录密码
改变MethodSwizzle中交换的方法

#import <UIKit/UIKit.h>
+ (void)load {
    NSLog(@"\n\n注入成功!\n\n");
    
    Class class = objc_getClass("WCAccountNewPhoneVerifyViewController");
    
    SEL originalSelector = @selector(onNext);
    SEL swizzledSelector = @selector(my_onNext);
    
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    
    // VC中没有my_onNext(如果是在分类中操作就没有这个问题)
    class_addMethod(class, swizzledSelector, my_onNext, "v@:");

    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

// 新的IMP
void my_onNext(id self, SEL _cmd) {
    NSLog(@"my_onNext");
    
    UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"密码是:%@", pwd.text);
    
    // 调回原来的方法
    [self performSelector:@selector(my_onNext)];
}
+ (void)load {
    NSLog(@"\n\n注入成功!\n\n");
    
    Class class = objc_getClass("WCAccountNewPhoneVerifyViewController");
    old_onNext  = class_replaceMethod(class, @selector(onNext), my_onNext, "v@:");
}

// 原来的IMP
IMP (*old_onNext)(id self, SEL _cmd);

// 新的IMP
void my_onNext(id self, SEL _cmd) {
    NSLog(@"my_onNext");
    
    UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"密码是:%@", pwd.text);
    
    // 调回原来的方法
    old_onNext(self, _cmd);
}
+ (void)load {
    NSLog(@"\n\n注入成功!\n\n");
    
    Class class = objc_getClass("WCAccountNewPhoneVerifyViewController");
    Method originalMethod = class_getInstanceMethod(class, @selector(onNext));
    old_onNext  = method_getImplementation(originalMethod);//原始的Method
    method_setImplementation(originalMethod, my_onNext);
}

// 原来的IMP
IMP (*old_onNext)(id self, SEL _cmd);

// 新的IMP
void my_onNext(id self, SEL _cmd) {
    NSLog(@"my_onNext");
    
    UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"密码是:%@", pwd.text);
    
    // 调回原来的方法
    old_onNext(self, _cmd);
}

推荐使用方法二和方法三,更加安全

三、总结

  1. 代码注入
  1. 案例:
  1. MethodSwizzle (Runtime) -重点! !

参考

iOS-底层探索17:Method-Swizzling 方法交换

上一篇 下一篇

猜你喜欢

热点阅读