美文网首页
iOS逆向实战--015:代码注入

iOS逆向实战--015:代码注入

作者: 帅驼驼 | 来源:发表于2021-04-25 18:42 被阅读0次

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

注入原理

当运行重签名的App时,想让它触发当前项目中的代码,例如:写在ViewController的中代码,这是不可能的。因为项目中的App在安装时会被重签名App替换掉,它们根本不是一个MachO文件。

代码注入原理:

使用MachOView分析可执行文件

wx8.0.2.ipa中导出WeChat可执行文件,拖入MachOView,可能会出现没有权限的错误提示

解决方法,使用MachOView菜单中的File -> Open...

选择WeChat可执行文件

成功解析WeChat中的内容

dyld加载可执行文件时,先读取MachO中的Header,获取MachO类型。然后读取Load Commands,通过读取__PAGEZERO__TEXT__DATA__LINKEDIT等段,可以得到MachO的大小,代码段和数据段的位置,告知dyld应该如何将MachO加载到内存中。当dyld读取代码段时,通过读取LC_MAIN找到程序入口。所以读取Load Commands几乎可以读取到MachO的所有信息

dyld除了加载MachO,还要加载UIkitFoundation等系统库,以及Frameworks目录下的动态库

Load Commands中,列出MachO所依赖的所有系统库以及三方库

所以dyld会加载Load Commands中包含的所有库。如果将注入的代码包装成一个动态库,将其插入到Load Commands中,理论上动态库可以被加载,注入的代码也可以被执行

Framework注入

Framwork注入流程:

  • 通过Xcode新建Framwork,将库安装进入App
  • 通过yololib注入Framwork库路径
./yololib MachO文件路径 库路径
  • 所有的Framwork加载都是由dyld加载进入内存被执行的
  • 注入成功的库路径会写入到MachO文件的LC_LOAD_DYLIB字段中

案例1:

使用Framework注入代码

打开自动重签名WeChat项目,创建Framework动态库

命名HOOK

动态库中创建Class,命名Inject

打开Inject.m文件,写入以下代码:

#import "Inject.h"

@implementation Inject

+(void)load{
   NSLog(@"\n\n\n\n\n🍺🍺🍺🍺🍺\n\n\n\n\n");
}

@end

编译项目,在App包中的Frameworks目录,可以找到HOOK.framework动态库

此时HOOK动态库中的代码还不能被执行,因为MachOLoad Commands中,还没有插入HOOK动态库的LC_LOAD_DYLIB字段

使用yololibMachO注入动态库

MachO文件,拷贝到yololib文件的同级目录

注入动态库

./yololib WeChat Frameworks/HOOK.framework/HOOK
-------------------------
2021-04-22 17:14:52.339 yololib[22774:33058817] dylib path @executable_path/Frameworks/HOOK.framework/HOOK
2021-04-22 17:14:52.340 yololib[22774:33058817] dylib path @executable_path/Frameworks/HOOK.framework/HOOK
Reading binary: WeChat

2021-04-22 17:14:52.341 yololib[22774:33058817] Thin 64bit binary!
2021-04-22 17:14:52.341 yololib[22774:33058817] dylib size wow 72
2021-04-22 17:14:52.341 yololib[22774:33058817] mach.ncmds 124
2021-04-22 17:14:52.341 yololib[22774:33058817] mach.ncmds 125
2021-04-22 17:14:52.341 yololib[22774:33058817] Patching mach_header..
2021-04-22 17:14:52.399 yololib[22774:33058817] Attaching dylib..

2021-04-22 17:14:52.399 yololib[22774:33058817] size 71
2021-04-22 17:14:52.399 yololib[22774:33058817] complete!

查看MachOLoad Commands,成功将HOOK动态库插入到最后,路径是注入时设置的参数2

解压缩wx8.0.2.ipa,将MachO文件替换,然后重新打包ipa

将重新打包的wx8.0.2.ipa,拷贝到项目中APP目录

真机运行项目,App安装成功,正常运行,同时打印注入代码

dylib注入

dylib注入流程:

  • 通过Xcode新建dylib库(注意:dylib属于macOS,所以需要修改属性)
  • 添加Target依赖,让Xcode将自定义dylib文件打包进入App
  • 利用yololib进行注入

案例1:

使用dylib注入代码

打开自动重签名WeChat项目,创建dylib动态库

命名HOOK

Build Settings中,将Base SDK设置项修改为iOS

Code Signing Identity设置项修改为iOS Developer

切换到AppTarget,选择Build Phases,点击+,选择New Copy Files Phase

Copy FilesDestination中,选择Frameworks

点击+,选择libHOOK.dylib

打开HOOK.m文件,写入以下代码

#import "HOOK.h"

@implementation HOOK

+(void)load{
  NSLog(@"\n\n\n\n\n🍺🍺 dylib 🍺🍺\n\n\n\n\n");
}

@end

使用脚本注入动态库:将yololib文件,拷贝到项目根目录

打开rsign.sh文件,加入以下代码:

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

真机运行项目,App安装成功,正常运行,同时打印注入代码

Method Swizzle

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

OC中,SELIMP之间的关系,就好像一本书的“目录

  • SEL是方法编号,就像“标题”一样
  • IMP是方法实现的真实地址,就像“页码”一样
  • 它们是一一对应的关系

Runtime提供了交换两个SELIMP对应关系的函数

OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) 
   OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

通过这个函数交换两个SELIMP对应关系的技术,我们称之为Method Swizzle(方法欺骗)

多种HOOK方式

  • class_addMethod方式:让原始方法可以被调用,不至于因为找不到SEL而崩溃
  • class_replaceMethod方式:直接给原始的方法替换IMP
  • method_setImplementation方式:直接重新赋值新的IMP
窃取登录密码

案例1:

点击登录按钮,输出用户输入的密码

延用Framwork代码注入的案例

打开rsign.sh文件,加入以下代码:

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

真机运行项目,进入登录页,使用Debug View Hierarchy动态调试

找到登录按钮的TargetAction

  • TargetWCAccountMainLoginViewController
  • ActiononNext

想要打印密码框的内容,必须先找到密码框的位置,然后通过控件的属性拿到内容

【方式一】:动态调试

找到密码框的位置,找到存储内容的text属性

  • 可以通过响应链条,根据左侧树状图结构,对ViewController.view.subviews进行递归遍历,找到符合条件的WCUITextField为止

使用动态调试,虽然也能找到密码框的位置,但寻找的过程太过繁琐,不推荐使用

【方式二】:静态分析

目前通过动态分析,可以确定登录按钮和密码框都在WCAccountMainLoginViewController

使用class-dump工具,通过MachO导出OC中所有类和方法列表以及成员变量

MachO文件,拷贝到class-dump文件的同级目录

导出OC中所有类和方法列表

./class-dump -H WeChat -o ./headers/
-------------------------
2021-04-25 13:51:24.070 class-dump[31659:33427860] Warning: Parsing instance variable type failed, ready_
2021-04-25 13:51:26.308 class-dump[31659:33427860] Warning: Parsing instance variable type failed, underlying
2021-04-25 13:51:26.308 class-dump[31659:33427860] Warning: Parsing instance variable type failed, enable
...
2021-04-25 13:52:09.288 class-dump[31659:33427860] Warning: Parsing method types failed, getKeyExtensionList:
2021-04-25 13:52:09.292 class-dump[31659:33427860] Warning: Parsing method types failed, getKeyExtensionList:
2021-04-25 13:52:09.292 class-dump[31659:33427860] Warning: Parsing method types failed, getExtensionListForSelector:

打开headers目录,列出所有导出的文件列表,找到WCAccountMainLoginViewController文件

打开WCAccountMainLoginViewController文件,没有找到WCUITextField控件,但发现一个WCAccountTextFieldItem类型控件,名称为_textFieldUserPwdItem

打开WCAccountTextFieldItem文件,还是没有找到WCUITextField控件,但它继承自WCBaseTextFieldItem类,我们要找的控件很有可能在父类中

打开WCBaseTextFieldItem文件,找到WCUITextField控件

使用代码之前,可以先结合动态调试,验证能否顺利获取到用户密码

通过动态调试,成功获取密码框里的内容

使用代码,获取用户密码

打开Inject文件,写入以下代码:

#import "Inject.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation Inject

+(void)load{
   Method wx_onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
   Method my_onNext = class_getInstanceMethod(self, @selector(hook_onNext));
   method_exchangeImplementations(wx_onNext, my_onNext);
}

-(void)hook_onNext{
   UITextField *txtPwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
   NSLog(@"密码:%@", txtPwd.text);
}

@end

真机运行项目,点击登录按钮

密码:123456

成功输出用户密码

案例2:

窃取密码后,希望可以调用原始的代码逻辑

日常开发中,方法交互一般写在当前类的分类中。由于交互后hook_onNext指向了onNextIMP,所以代码中直接调用hook_onNext即可

打开Inject文件,修改hook_onNext方法:

-(void)hook_onNext{
   UITextField *txtPwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
   NSLog(@"密码:%@", txtPwd.text);
   
   [self hook_onNext];
}

但是,HOOK动态库中的Inject文件,和WCAccountMainLoginViewController类没有任何关联。虽然hook_onNext指向onNextIMP,但是调用objc_msgSend函数时,给VC发送hook_onNext,程序会因为找不到SEL而崩溃

下面介绍三种方式,都可以解决上述问题

【方式一】:class_addMethod

既然VC中没有hook_onNext方法,可以使用class_addMethod函数给VC添加一个hook_onNext,调用时不会因为找不到SEL而崩溃

打开Inject文件,写入以下代码:

#import "Inject.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation Inject

+(void)load{
   Method wx_onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
   
   class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(hook_onNext), hook_onNext, @"v@:");
   Method my_onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(hook_onNext));
   
   method_exchangeImplementations(wx_onNext, my_onNext);
}

void hook_onNext(id self, SEL _cmd){
   UITextField *txtPwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
   NSLog(@"密码:%@", txtPwd.text);
   
   [self performSelector:@selector(hook_onNext)];
}

@end
  • 之前的hook_onNext方法,改为函数实现,增加OC方法的两个隐式参数
  • 使用class_addMethod函数,给VC增加hook_onNext方法,函数名即是IMP
  • VC下的hook_onNextonNext进行方法交互
  • hook_onNext函数中,如果直接调用函数,调用的不是替换后的IMP,而是hook_onNextIMP,这样会形成递归。所以想调用原始方法,要使用objc_msgSendperformSelector方式调用

真机运行项目,点击登录按钮,窃取密码后,调用原始的代码逻辑

密码:123456

【方式二】:class_replaceMethod

VConNext方法的IMP替换

打开Inject文件,写入以下代码:

#import "Inject.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation Inject

IMP (*oldImp)(id self, SEL _cmd);

+(void)load{
   oldImp = class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), hook_onNext, @"v@:");
}

void hook_onNext(id self, SEL _cmd){
   UITextField *txtPwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
   NSLog(@"密码:%@", txtPwd.text);
   
   oldImp(self, _cmd);
}

@end
  • 声明IMP类型的oldImp函数指针
  • 使用class_replaceMethod函数,直接将onNextIMP替换为hook_onNextIMP,将返回的原始IMP赋值给oldImp
  • hook_onNext函数中,想调用原始方法,直接调用oldImp并传入隐式参数即可

【方式三】:method_setImplementation

获取原始的IMP,重新赋值新IMP

打开Inject文件,写入以下代码:

#import "Inject.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation Inject

IMP (*oldImp)(id self, SEL _cmd);

+(void)load{
   Method wx_onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
   oldImp = method_getImplementation(wx_onNext);
   
   method_setImplementation(wx_onNext, hook_onNext);
}

void hook_onNext(id self, SEL _cmd){
   UITextField *txtPwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
   NSLog(@"密码:%@", txtPwd.text);
   
   oldImp(self, _cmd);
}

@end
  • 使用method_getImplementation函数,获取onNextIMP,赋值给oldImp
  • 使用method_setImplementation函数,直接赋值hook_onNextIMP
总结

注入原理:

  • Xcode将注入的动态库打包进App包中
  • MachOLoad Commands中,包含注入动态库的LC_LOAD_DYLIB字段
  • DYLD加载注入的动态库

注入方式:

  • Framework注入
  • dylib注入

Framwork注入流程:

  • 通过Xcode新建Framwork,将库安装进入App
  • 通过yololib注入Framwork库路径

dylib注入流程:

  • 通过Xcode新建dylib库(注意:dylib属于macOS,所以需要修改属性)
  • 添加Target依赖,让Xcode将自定义dylib文件打包进入App
  • 利用yololib进行注入

案例,窃取登录密码:

  • 分析思路
  • 动态调试,界面入手
  • 静态分析,使用class-dump,通过MachO导出OC的类和方法列表

Method Swizzle

  • 使用method_exchangeImplementations交互SELIMP对应关系,此方式存在隐患,如果不在同一个类,无法调用原始方法,因为找不到SEL而崩溃
  • 使用class_addMethod添加方法,不至于因为找不到SEL而崩溃。过程比较复杂,不推荐使用
  • 使用class_replaceMethod替换SELIMP
  • 使用method_getImplementationmethod_setImplementation配合,直接重新赋值新的IMP。逻辑清晰,大部分HOOK框架使用此方式,推荐使用

相关文章

  • iOS逆向实战--015:代码注入

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

  • iOS逆向 代码注入+Hook

    iOS逆向 代码注入+HookiOS逆向 代码注入+Hook

  • iOS逆向-代码注入

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

  • iOS逆向工程 - 代码注入

    本文主要介绍利用动态库对第三方App实现代码静态注入,一般有两种注入方式:Framework,Dylib。大体包含...

  • iOS逆向之 代码注入

    一 framework注入 新建工程001--Demo,根据我上一篇文章的重签名方式之三 脚本重签名的xcodeC...

  • iOS逆向开发--代码注入

    概述 当我们要调试一个三方APP的时候,就少不了让三方的APP执行我们自己写的代码,或者是截获三方APP的某一些方...

  • iOS逆向-代码注入(IV)

    framework注入 dyld dyld (动态库加载器),负责加载程序和程序所有依赖的动态库。内核读取 Mac...

  • iOS逆向之代码注入

    准备工作 已去壳ipa,重签运行,可参照iOS逆向之砸壳与重签[https://www.jianshu.com/p...

  • iOS逆向二-代码注入

    iOS逆向二-代码注入 脚本自动重签补一下上一篇的尾巴!重签过程其实完全可以通过脚本完成,我们啥都不用干的下面这个...

  • iOS逆向 代码注入+Hook

    本文涉及内容无风险,但某信有检测BundId机制,建议不要大号登录 本文是建立在应用重签名的基础上 iOS逆向 应...

网友评论

      本文标题:iOS逆向实战--015:代码注入

      本文链接:https://www.haomeiwen.com/subject/heaxrltx.html