美文网首页
iOS逆向 代码注入+Hook

iOS逆向 代码注入+Hook

作者: Lee坚武 | 来源:发表于2021-02-18 10:48 被阅读0次

    本文涉及内容无风险,但某信有检测BundId机制,建议不要大号登录

    本文是建立在应用重签名的基础上

    iOS逆向 应用重签名+微信重签名实战 1

    iOS逆向 Shell脚本+脚本重签名 2

    工具: yololib+class_dump 3 密码:8ujj

    一、初次注入

    代码注入有两种方案:通过FrameWork和dylib

    1.脚本重签名

    照着 iOS逆向 Shell脚本+脚本重签名 1 重签名

    2.FrameWork注入

    2.1 新建FrameWork

    在Xcode中File->Target新增一个Framework

    image

    2.2 FrameWork中新建一个类

    2.3 添加一个load方法

    image

    仅仅这样还不够,DYLD会动态加载项目中的Frameworks,但不会加载当前FrameWork

    2.4 运行编译一下

    保证FrameWork放到FrameWorks目录下

    image

    2.5 yololib注入动态库

    建议将yololib复制粘贴到/usr/local/bin目录下,可以随时随地调用

    将app.sh的最后一句代码启用(注意修改FrameWork名称)

    yololib"TARGET_APP_PATH/APP_BINARY""Frameworks/XXXX.framework/XXXX"

    2.6 运行

    不出意外会打印 ❎❎❎❎❎❎❎❎❎❎

    image

    3.dylib注入

    其实就是换了个Target

    3.1 新建Library

    image

    3.2 修改dylib的BaseSDK

    image

    3.3 修改dylib的签名

    修改成iPhone Developer

    image

    3.4 添加依赖

    image

    3.5 运行编译

    只有加进来了才算成功了一半

    image

    3.6 修改脚本

    yololib"TARGET_APP_PATH/APP_BINARY""Frameworks/libFXHook.dylib"

    3.7 运行

    打印 ❎❎❎❎❎❎❎❎❎❎(有时候会报错“image notound”,如果FrameWorks包含了dylib则重新运行就好了)

    image

    二、Method Swizzling初用

    1.定义

    在OC中,SEL和IMP之间的关系,就好像一本书的“目录”。SEL是方法编号,就像“标题”一样。IMP是方法实现的真实地址,就像“页码”一样。他们是一一对应的关系。Runtime提供了交换两个SEL和IMP对应关系的函数 method_exchangeImplementations(<#Method _Nonnull m1#>, <#Method _Nonnull m2#>) ,通过这个函数交换两个SEL和IMP对应关系的技术,我们称之为Method Swizzle(方法欺骗)

    image

    我更愿意把SEL和IMP的关系理解成书的封面和书,原先一本《三国》和《水浒》,在经过方法交换之后翻开《三国》的封面却是《水浒》的内容

    2.代码演示

    NSURL*url = [ NSURLURLWithString:[ @"www.Felix.com/好好学习"stringByAddingPercentEncodingWithAllowedCharacters:[ NSCharacterSetURLQueryAllowedCharacterSet]]];
    
    NSLog( @"%@",url);
    

    比如上述代码看起来很繁琐,这时候就可以用Method Swizzling来实现

    2.1 新建NSURL的分类

    2.2 方法交换

    #import "NSURL+FXUrl.h"
    
    #import <objc/runtime.h>
    
    @implementation NSURL (FXUrl)
    
    + ( void)load {
    
    // 获取原来的方法
    
    Method URLWithString = class_getClassMethod( self, @selector(URLWithString:));
    
    // 获取自定义方法
    
    Method FXURLWithString = class_getClassMethod( self, @selector(FX_URLWithString:));
    
    // 交换方法
    
    method_exchangeImplementations(URLWithString, FXURLWithString);
    
    }
    
    + (instancetype)FX_URLWithString:( NSString*)string {
    
    NSURL*url = [ NSURLFX_URLWithString:string];
    
    if(!url) {
    
    string = [string stringByAddingPercentEncodingWithAllowedCharacters:[ NSCharacterSetURLQueryAllowedCharacterSet]];
    
    }
    
    return[ NSURLFX_URLWithString:string];
    
    }
    
    @end
    

    2.3 疑问点

    ①为什么要用分类去做方法交换呢?

    下文中将会提及

    ②自定义FX_URLWithString中是不是递归了?

    答:load方法执行顺序较早,调用FX_URLWithString时已经进行了方法交换,想调用FX_URLWithString就应该调用URLWithString

    各位看官可能觉得太简单了,接下来就回到重签名项目开始重头戏

    目标:点击“注册”按钮使之无效

    1.获取到对象名称和方法名称

    image

    之前文章中有讲到过,选中控件就能通过地址打印对应的信息(有可能直接显示了对象名称和方法名称)

    2.利用class-dump导出MachO的头文件

    MachO文件在编译出来的ipa包中

    image

    3.搜索头文件查看方法声明

    这里用的是sublime工具,先全局找类(找不到就找父类)再找方法

    image

    4.交换方法

    在第一节注入FrameWork的代码中继续

    #import "InjectCode.h"
    
    #import <objc/runtime.h>
    
    @implementation InjectCode
    
    + ( void)load {
    
    Method oldMethod = class_getInstanceMethod(objc_getClass( "WCAccountLoginControlLogic"), @selector(onFirstViewRegister));
    
    Method newMethod = class_getInstanceMethod( self, @selector(FX_onFirstViewRegister));
    
    method_exchangeImplementations(oldMethod, newMethod);
    
    }
    
    - ( void)FX_onFirstViewRegister {
    
    NSLog( @"想注册吗?想注册先打钱!");
    
    }
    
    @end
    

    四、Hook微信——窃取登录密码

    目标:点击“登录”按钮获取到账号和密码并继续登录

    1.分析

    打印地址去头文件列表查找声明方法

    image

    发现这是一个不带参数的方法,那么账号和密码去那里获取呢?

    image

    我们可以去Viewcontroller的变量中找找线索

    发现了两个可疑的实例变量 _textFieldUserNameItem 和 _textFieldUserPwdItem

    查看WCAccountTextFieldItem没发现什么有实际意义的内容,那再找找父类吧

    image

    WCUITextField *m_textField这个实例变量看起来有点用

    image

    怎么判断是否是我们要找的账号和密码呢?

    输入账号和密码再ViewDebug调试一下

    如下图所示,我们找到了写Hook代码的方向

    image
    (lldb) po 0x133800600
    
    < WCAccountMainLoginViewController:0x133800600>
    
    (lldb) po [(WCAccountMainLoginViewController *) 0x133800600valueForKey:@ "_textFieldUserNameItem"]
    
    < WCAccountTextFieldItem:0x28231f180>
    
    (lldb) po [(WCAccountTextFieldItem *) 0x28231f180valueForKey:@ "m_textField"]
    
    < WCUITextField:0x13090d600; baseClass = UITextField; frame = ( 200; 34544); text = 'Felix'; opaque = NO; autoresize = W+H; tintColor = UIExtendedSRGBColorSpace 0.007843140.73333301; gestureRecognizers = < NSArray:0x280891650>; layer = < CALayer:0x280612560>>
    

    2.开始Hook

    #import "InjectCode.h"
    
    #import <objc/runtime.h>
    
    #import "UIKit/UIKit.h"
    
    //@interface WCAccountTextFieldItem: NSObject
    
    //
    
    //@end
    
    @implementation InjectCode
    
    + ( void)load {
    
    Method oldMethod = class_getInstanceMethod(objc_getClass( "WCAccountMainLoginViewController"), @selector(onNext));
    
    Method newMethod = class_getInstanceMethod( self, @selector(FX_onNext));
    
    method_exchangeImplementations(oldMethod, newMethod);
    
    }
    
    - ( void)FX_onNext {
    
    // /// 声明WCAccountTextFieldItem类,为了不报错
    
    // WCAccountTextFieldItem *account = [self valueForKey:@"_textFieldUserNameItem"];
    
    // /// 导入UIKit框架
    
    // UITextField *accountTF = [account valueForKey:@"m_textField"];
    
    UITextField*accountTF = [[ selfvalueForKey: @"_textFieldUserNameItem"] valueForKey: @"m_textField"];
    
    UITextField*passwordTF = [[ selfvalueForKey: @"_textFieldUserNameItem"] valueForKey: @"m_textField"];
    
    NSLog( @"账号:“%@”n密码:“%@”", accountTF .text, passwordTF .text);
    
    [ selfFX_onNext];
    
    }
    
    @end
    

    正当我们满心欢喜等待神奇的一刻时,熟悉的味道来了

    image

    崩溃原因:WCAccountMainLoginViewController找不到FX_onNext的方法编号,即原工程中WCAccountMainLoginViewController没有FX_onNext声明

    OC方法调用有两个隐藏参数:self(方法调用者)、cmd(方法编号),FrameWork中把onNext的imp替换成了FX_onNext,页面调用登录方法来到我们自定义的方法实现;然后给VC发送FX_onNext消息,必然是unrecognized selector sent to instance

    此时此刻用分类Hook的好处就体现的淋漓尽致,直接给分类加个方法就完事了

    3.解决崩溃完成Hook

    3.1 class_addMethod方法

    利用class_addMethod方法让原始方法可以被调用(麻烦不推荐)

    #import "InjectCode.h"
    
    #import <objc/runtime.h>
    
    #import "UIKit/UIKit.h"
    
    @implementation InjectCode
    
    + ( void)load {
    
    /**
    
    * 1、给哪个类添加方法
    
    * 2、方法编号
    
    * 3、方法实现(地址)
    
    * 4、v代表Void @代表id类型 :代表@selecter类型(可以在帮助文档查看这个方法)
    
    */
    
    BOOLdidAddMethod = class_addMethod(objc_getClass( "WCAccountMainLoginViewController"), @selector(FX_onNext), FX_onNext, "v@:");
    
    if(didAddMethod) {
    
    NSLog( @"添加方法成功");
    
    Method oldMethod = class_getInstanceMethod(objc_getClass( "WCAccountMainLoginViewController"), @selector(onNext));
    
    Method newMethod = class_getInstanceMethod(objc_getClass( "WCAccountMainLoginViewController"), @selector(FX_onNext));
    
    method_exchangeImplementations(oldMethod, newMethod);
    
    }
    
    }
    
    //方法实现IMP
    
    voidFX_onNext( idself, SEL _cmd) {
    
    UITextField*accountTF = [[ selfvalueForKey: @"_textFieldUserNameItem"] valueForKey: @"m_textField"];
    
    UITextField*passwordTF = [[ selfvalueForKey: @"_textFieldUserNameItem"] valueForKey: @"m_textField"];
    
    NSLog( @"账号:“%@” 密码:“%@”", accountTF .text, passwordTF .text);
    
    //使用原来逻辑
    
    [ selfperformSelector: @selector(FX_onNext)];
    
    }
    
    @end
    

    3.2 class_replaceMethod方法

    保存原始方法,利用replaceMethod方法将原始方法的IMP覆盖

    #import "InjectCode.h"
    
    #import <objc/runtime.h>
    
    #import "UIKit/UIKit.h"
    
    @implementation InjectCode
    
    + ( void)load {
    
    onNext = method_getImplementation(class_getInstanceMethod(objc_getClass( "WCAccountMainLoginViewController"), @selector(onNext)));
    
    class_replaceMethod(objc_getClass( "WCAccountMainLoginViewController"), @selector(onNext), FX_onNext, "v@:");
    
    }
    
    IMP (*onNext)( idself,SEL _cmd);
    
    voidFX_onNext( idself, SEL _cmd) {
    
    UITextField*accountTF = [[ selfvalueForKey: @"_textFieldUserNameItem"] valueForKey: @"m_textField"];
    
    UITextField*passwordTF = [[ selfvalueForKey: @"_textFieldUserNameItem"] valueForKey: @"m_textField"];
    
    NSLog( @"账号:“%@” 密码:“%@”", accountTF .text, passwordTF .text);
    
    //使用原来逻辑
    
    onNext( self,_cmd);
    
    }
    

    3.3 method_setImplementation方法

    保存原始方法,利用setImplementation方法将原始方法的IMP重写

    #import "InjectCode.h"
    
    #import <objc/runtime.h>
    
    #import "UIKit/UIKit.h"
    
    @implementation InjectCode
    
    + ( void)load {
    
    onNext = method_getImplementation(class_getInstanceMethod(objc_getClass( "WCAccountMainLoginViewController"), @selector(onNext)));
    
    method_setImplementation(class_getInstanceMethod(objc_getClass( "WCAccountMainLoginViewController"), @selector(onNext)), FX_onNext);
    
    }
    
    IMP (*onNext)( idself,SEL _cmd);
    
    //方法实现IMP
    
    voidFX_onNext( idself, SEL _cmd) {
    
    UITextField*accountTF = [[ selfvalueForKey: @"_textFieldUserNameItem"] valueForKey: @"m_textField"];
    
    UITextField*passwordTF = [[ selfvalueForKey: @"_textFieldUserNameItem"] valueForKey: @"m_textField"];
    
    NSLog( @"账号:“%@” 密码:“%@”", accountTF .text, passwordTF .text);
    
    //使用原来逻辑
    
    onNext( self,_cmd);
    
    }
    
    @end
    

    五、Runtime-API

    // 1.objc_xxx 系列函数

    // 函数名称 函数作用

    objc _getClass 获取Class对象

    objc _getMetaClass 获取MetaClass对象

    objc _allocateClassPair 分配空间,创建类(仅在 创建之后,注册之前 能够添加成员变量)

    objc _registerClassPair 注册一个类(注册后方可使用该类创建对象)

    objc _disposeClassPair 注销某个类

    objc _allocateProtocol 开辟空间创建协议

    objc _registerProtocol 注册一个协议

    objc _constructInstance 构造一个实例对象(ARC下无效)

    objc _destructInstance 析构一个实例对象(ARC下无效)

    objc _setAssociatedObject 为实例对象关联对象

    objc _getAssociatedObje*ct 获取实例对象的关联对象

    objc _removeAssociatedObjects 清空实例对象的所有关联对象

    objc _系列函数关注于宏观使用,如类与协议的空间分配,注册,注销等操作

    // 2.class_xxx 系列函数

    函数名称 函数作用

    class_addIvar 为类添加实例变量

    class_addProperty 为类添加属性

    class_addMethod 为类添加方法

    class_addProtocol 为类遵循协议

    class_replaceMethod 替换类某方法的实现

    class_getName 获取类名

    class_isMetaClass 判断是否为元类

    objc _getProtocol 获取某个协议

    objc _copyProtocolList 拷贝在运行时中注册过的协议列表

    class_getSuperclass 获取某类的父类

    class_setSuperclass 设置某类的父类

    class_getProperty 获取某类的属性

    class_getInstanceVariable 获取实例变量

    class_getClassVariable 获取类变量

    class_getInstanceMethod 获取实例方法

    class_getClassMethod 获取类方法

    class_getMethodImplementation 获取方法的实现

    class_getInstanceSize 获取类的实例的大小

    class_respondsToSelector 判断类是否实现某方法

    class_conformsToProtocol 判断类是否遵循某协议

    class_createInstance 创建类的实例

    class_copyIvarList 拷贝类的实例变量列表

    class_copyMethodList 拷贝类的方法列表

    class_copyProtocolList 拷贝类遵循的协议列表

    class_copyPropertyList 拷贝类的属性列表

    class_系列函数关注于类的内部,如实例变量,属性,方法,协议等相关问题

    // 3.object_xxx 系列函数

    函数名称 函数作用

    object_copy 对象copy(ARC无效)

    object_dispose 对象释放(ARC无效)

    object_getClassName 获取对象的类名

    object_getClass 获取对象的Class

    object_setClass 设置对象的Class

    object_getIvar 获取对象中实例变量的值

    object_setIvar 设置对象中实例变量的值

    object_getInstanceVariable 获取对象中实例变量的值 (ARC中无效,使用 object_getIvar)

    object_setInstanceVariable 设置对象中实例变量的值 (ARC中无效,使用 object_setIvar)

    objcet _系列函数关注于对象的角度,如实例变量

    // 4.method_xxx 系列函数

    函数名称 函数作用

    method _getName 获取方法名

    method _getImplementation 获取方法的实现

    method _getTypeEncoding 获取方法的类型编码

    method _getNumberOfArguments 获取方法的参数个数

    method _copyReturnType 拷贝方法的返回类型

    method _getReturnType 获取方法的返回类型

    method _copyArgumentType 拷贝方法的参数类型

    method _getArgumentType 获取方法的参数类型

    method _getDeion 获取方法的描述

    method _setImplementation 设置方法的实现

    method _exchangeImplementations 替换方法的实现

    method _系列函数关注于方法内部,如果方法的参数及返回值类型和方法的实现

    // 5.property_xxx 系列函数

    函数名称 函数作用

    property _getName 获取属性名

    property _getAttributes 获取属性的特性列表

    property _copyAttributeList 拷贝属性的特性列表

    property _copyAttributeValue 拷贝属性中某特性的值

    property _系类函数关注与属性*内部,如属性的特性等

    // 6.protocol_xxx 系列函数

    函数名称 函数作用

    protocol _conformsToProtocol 判断一个协议是否遵循另一个协议

    protocol _isEqual 判断两个协议是否一致

    protocol _getName 获取协议名称

    protocol _copyPropertyList 拷贝协议的属性列表

    protocol _copyProtocolList 拷贝某协议所遵循的协议列表

    protocol _copyMethodDeionList 拷贝协议的方法列表

    protocol _addProtocol 为一个协议遵循另一协议

    protocol _addProperty 为协议添加属性

    protocol _getProperty 获取协议中的某个属性

    protocol _addMethodDeion 为协议添加方法描述

    protocol _getMethodDeion 获取协议中某方法的描述

    // 7.ivar_xxx 系列函数

    函数名称 函数作用

    ivar _getName 获取Ivar名称

    ivar _getTypeEncoding 获取类型编码

    ivar _getOffset 获取偏移量

    // 8.sel_xxx 系列函数

    函数名称 函数作用

    sel _getName 获取名称

    sel _getUid 注册方法

    sel _registerName 注册方法

    sel _isEqual 判断方法是否相等

    // 9.imp_xxx 系列函数

    函数名称 函数作用

    imp _implementationWithBlock 通过代码块创建IMP

    imp _getBlock 获取函数指针中的代码块

    imp _removeBlock 移除IMP中的代码块

    相关文章

      网友评论

          本文标题:iOS逆向 代码注入+Hook

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