iOS逆向 11:代码注入(下)

简介: iOS逆向 11:代码注入(下)

本文主要是以WeChat为例,讲解如何破坏WeChat注册、以及如何获取登录密码


引子


在进行WeChat实践操作时,首先需要了解一个概念:Method Swizzing(即方法交换)


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


在OC中,SEL和IMP之间的关系,类似与一本书的目录,是一一对应的


  • SEL:方法编号,类似于目录中的标题
  • IMP:方法实现的真实地址指针,类似于目录中的页码



同时,Runtime中也提供了用于交换两个SEL和IMP的方法,method_exchangeIMP,我们可以通过这个函数交换两个SEL和IMP的对应关系

更为具体的讲解请参考这篇文章:iOS-底层原理 21:Method-Swizzling 方法交换


破坏微信注册


准备工作:需要新建一个工程,并重签名,且采用Framework注入的方式



这里破坏的微信的注册,主要是通过runtime方法进行注册方法的hook


1、获取注册的相关信息


  • 1、通过lldb调试获取WeChat的注册


image.png

2、获取注册的target、action

image.png

  • target:WCAccountLoginControlLogic
  • action:onFirstViewRegister


2、简单hook演示


  • 1、通过class_getInstanceMethod +method_exchangeImplementations方法,hook注册的点击事件
@implementation inject
+ (void)load{
//    改变微信的注册
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegister));
    Method newMethod = class_getInstanceMethod(self, @selector(my_method));
    method_exchangeImplementations(oldMethod, newMethod);
}
- (void)my_method{
    NSLog(@"CJLHook --- 注册不了了!");
}
@end

2、重新运行,点击注册按钮,发现执行的是我们自己的方法

image.png


3、点击登录时,获取用户的密码


准备工作


  • 1、点击登录,输入密码
  • 2、点击Debug View Hierarchy动态调试登录界面
  • 3、获取登录按钮信息

image.png

  • target:WCAccountMainLoginViewController
  • action:onNext
  • 4、获取密码的类:WCUITextField

image.png

方式1:通过lldb获取密码


  • llfb获取密码的调试如下


image.png

此时问题来了,如果我们想通过hook登录方法,在点击登录按钮时,如何获取密码呢?有以下几种方式


  • 1、通过响应链,一层一层查找,缺点是很繁琐
    image.png
  • 2、静态分析:可以通过class-dump获取类、方法的列表,其本质也是从Mach-O文件读取出来的


hook登录按钮 - 动态调试获取


  • 1、- 在CJLHook的inject类中hook登录按钮的方法
@implementation inject
+ (void)load{
//    改变微信的注册
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    Method newMethod = class_getInstanceMethod(self, @selector(my_onNext));
    method_exchangeImplementations(oldMethod, newMethod);
}
- (void)my_onNext{
}
@end

2、通过class_dump工具会dump出OC中类列表(包括成员变量)、方法列表,具体操作如下:


  • 1)将class-dump、wechat可执行文件拷贝至同一个文件夹

image.png

2)在终端执行以下命令:./class-dump -H WeChat -o ./headers/,其本质是通过读取Mach-O文件获取

image.png

3、通过sublime Text打开headers文件夹,在其中查找WCAccountMainLoginViewController类,找到密码的成员变量_textFieldUserPwdItem

image.png

查找类文件顺序为:WCAccountTextFieldItem -> WCBaseTextFieldItem -> WCUITextField

image.png

动态调试获取-04


4、通过获取的成员变量,利用lldb动态调试获取密码

image.png

  • po [(WCAccountMainLoginViewController *)0x10a84e800 valueForKey:@"_textFieldUserPwdItem"]
  • po [(WCAccountTextFieldItem *)0x281768360 valueForKey:@"m_textField"]
  • po ((WCUITextField *)0x10a1566e0).text


hook登录按钮 - hook代码注入方式获取


  • 1、修改 my_onNext 方法
@implementation inject
+ (void)load{
//    改变微信的注册
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    Method newMethod = class_getInstanceMethod(self, @selector(my_onNext));
    method_exchangeImplementations(oldMethod, newMethod);
}
- (void)my_onNext{
    UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"密码是:%@", pwd.text);
}
@end

运行结果如下所示

image.png

  • 2、然后此时需要在my_onNext中调用原来的方法,走回原来的登录流程,此时的my_onNext方法修改如下
- (void)my_onNext{
    UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"密码是:%@", pwd.text);
    //调回原来的方法objc_msgSend(WCAccountMainLoginViewController,onNext的IMP)
    [self my_onNext];
}

3、在[self my_onNext];处加断点,验证此时的my_onNext中的self、_cmd

image.png4、然后继续执行,发现程序会崩溃,即在执行objc_msgSend后会直接崩溃,原因是找不到my_onNext

image.png

解决崩溃的方案:添加一个method


  • 修改inject中的代码,此时是通过class_addMethodWCAccountMainLoginViewController中新增一个方法,然后在和原来的onNext交换新增后的方法
@implementation inject
+ (void)load{
    //原始的method
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    //给WCAccountMainLoginViewController添加新方法
    class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext), new_onNext, "v@:");
    //获取添加后的方法
    Method newMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext));
    //交换
    method_exchangeImplementations(oldMethod, newMethod);
}
//新的IMP
void new_onNext(id self, SEL _cmd){
    UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"密码是:%@", pwd.text);
    //调回原来的方法
objc_msgSend(WCAccountMainLoginViewController,onNext的IMP)
    [self performSelector:@selector(new_onNext)];
}
@end

重新运行后查看可以走到原来的登录流程,且同时可以获取用户密码


代码注入优化:通过替换的方式


但是上面的代码看上不并不简洁,所以我们来对其一些优化,采用class_replaceMethod函数进行替换原来的onNext方法,修改后的代码如下

+ (void)load{
    //原始的method
    Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
    //替换
    old_onNext = class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), new_onNext, "v@:");
}
//原来的IMP
IMP (*old_onNext)(id self, SEL _cmd);
//新的IMP
void new_onNext(id self, SEL _cmd){
    UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"密码是:%@", pwd.text);
    //调回原来的方法
objc_msgSend(WCAccountMainLoginViewController,onNext的IMP)
    old_onNext(self, _cmd);
}

更好的方式


  • 通过method_getImplementation、method_setImplementation方法进行覆盖原来onNext方法的IMP
+ (void)load{
    //原始的method
    old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
    //通过set覆盖原始的IMP
    method_setImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)), new_onNext);
}
//原来的IMP
IMP (*old_onNext)(id self, SEL _cmd);
//新的IMP
void new_onNext(id self, SEL _cmd){
    UITextField *pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
    NSLog(@"密码是:%@", pwd.text);
    //调回原来的方法objc_msgSend(WCAccountMainLoginViewController,onNext的IMP)
    old_onNext(self, _cmd);
}


总结


  • Method Swizzing(即方法交换):是利用OC的Runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的


  • 多种hook方式:


  • 1、 class_addMethod方式: 利用AddMethod方式,让原始方法可以被调用,不至于因为找不到SEL而崩溃
  • 2、class_replaceMethod方式:利用class_replaceMethod,直接给原始的方法替换IMP
  • 3、method_setImplementation方式:利用method_setImplementation,直接重新赋值原始的新的IMP


相关文章
|
6月前
|
移动开发 安全 数据安全/隐私保护
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
|
6月前
|
移动开发 前端开发 数据安全/隐私保护
【工具】iOS代码混淆工具-iOS源码混淆
【工具】iOS代码混淆工具-iOS源码混淆
72 1
|
6月前
|
移动开发 前端开发 安全
最强大的 iOS 应用源码保护工具:Ipa Guard,保护你的商业机密代码
最强大的 iOS 应用源码保护工具:Ipa Guard,保护你的商业机密代码
|
1月前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异:从代码到用户体验
【10月更文挑战第5天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。它们在技术架构、开发环境及用户体验上有着根本的不同。本文通过比较这两种平台的开发过程,揭示背后的设计理念和技术选择如何影响最终产品。我们将深入探讨各自平台的代码示例,理解开发者面临的挑战,以及这些差异如何塑造用户的日常体验。
|
6月前
|
缓存 开发工具 iOS开发
优化iOS中Objective-C代码调起支付流程的速度
优化iOS中Objective-C代码调起支付流程的速度
104 2
|
6月前
|
移动开发 安全 数据安全/隐私保护
iOS 代码混淆和加固技术详解
iOS 代码混淆和加固技术详解
|
6月前
|
移动开发 前端开发 数据安全/隐私保护
iOS 代码混淆 - 从入门到放弃
iOS 代码混淆 - 从入门到放弃
|
6月前
|
移动开发 安全 前端开发
iOS代码混淆工具
iOS代码混淆工具
107 1
|
6月前
|
安全 算法 数据安全/隐私保护
iOS 代码加固与保护方法详解 - 提升 iOS 应用安全性的关键步骤
iOS 代码加固与保护方法详解 - 提升 iOS 应用安全性的关键步骤
|
6月前
|
移动开发 安全 数据安全/隐私保护
iOS代码混淆工具
🔒 这是一篇介绍iOS代码混淆工具的技术博客,旨在帮助开发者提高代码安全性。本工具来自于Github的混淆词库和代码,通过差异化处理和代码合并生成数亿种用于混淆的单词和垃圾代码,确保每次混淆不会出现重复,混淆后的代码跟手写没有任何区别,完美解决代码4.3和2.3.1问题。