MonkeyDev(二):补充

MonkeyDev 安装过程中的一些问题

  • ① 在按照官方文档,远程执行安装脚本 md-install 时:

    sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-install)"
    

    报错:

    curl: (7) Failed to connect to raw.githubusercontent.com port 443: Connection refused
    

    网上查了一下资料,说是 GitHub 一些域名的 DNS 解析被污染,导致在 DNS 解析过程中无法通过域名获取正确的 IP 地址


    解决方案 A:修改本机的 host,手动添加 raw.githubusercontent.com 所映射的 IP 地址

    1. 打开 https://www.ipaddress.com,在搜索框中输入连接失败的域名 raw.githubusercontent.com
      raw.githubusercontent.com
      查询结果如下
      Hostname Summary
      Frequently Asked Questions (FAQ)

    2. 在本机的 host 文件中添加上面查询到的 raw.githubusercontent.com 所对应的 IP 地址

      sudo vim /etc/hosts
      
      185.199.108.133 raw.githubusercontent.com
      

    解决方案 B:使用 git 命令将 MonkeyDev.git 克隆到本地,然后直接在本地执行安装脚本 md-install

    1. 使用 git 命令将 MonkeyDev.git 克隆到本地

      # 在桌面新建 MonkeyDevGitHub 目录,并将 MonkeyDev.git 克隆到此目录
      ~ > mkdir ~/Desktop/MonkeyDevGitHub
      ~ > git clone https://github.com/AloneMonkey/MonkeyDev.git ~/Desktop/MonkeyDevGitHub
      正克隆到 '~/Desktop/MonkeyDevGitHub'...
      remote: Enumerating objects: 913, done.
      remote: Total 913 (delta 0), reused 0 (delta 0), pack-reused 913
      接收对象中: 100% (913/913), 19.73 MiB | 13.42 MiB/s, 完成.
      处理 delta 中: 100% (502/502), 完成.
      
    2. 在本地执行安装脚本 md-install

      ~ > cd ~/Desktop/MonkeyDevGitHub/bin
      ~/Desktop/MonkeyDevGitHub/bin > sudo bash md-install
      
  • ② 在安装过程中,下载 dump.pydump.js 文件时报错:

    Failed to download https://raw.githubusercontent.com/AloneMonkey/frida-ios-dump/3.x/dump.py to /opt/MonkeyDev/bin/dump.py
    Failed to download https://raw.githubusercontent.com/AloneMonkey/frida-ios-dump/3.x/dump.js to /opt/MonkeyDev/bin/dump.js
    

    解决方案:单独下载 dump.pydump.js 文件

    1. 使用 git 命令将 MonkeyDev.git 克隆到本地

      # 在桌面新建 MonkeyDevGitHub 目录,并将 MonkeyDev.git 克隆到此目录
      ~ > mkdir ~/Desktop/MonkeyDevGitHub
      ~ > git clone https://github.com/AloneMonkey/MonkeyDev.git ~/Desktop/MonkeyDevGitHub
      正克隆到 '~/Desktop/MonkeyDevGitHub'...
      remote: Enumerating objects: 913, done.
      remote: Total 913 (delta 0), reused 0 (delta 0), pack-reused 913
      接收对象中: 100% (913/913), 19.73 MiB | 13.42 MiB/s, 完成.
      处理 delta 中: 100% (502/502), 完成.
      
    2. 进入到 ~/Desktop/MonkeyDevGitHub/bin 目录,编辑安装脚本 md-install,将下载 dump.pydump.js 文件的命令注释掉

      #downloadFile "https://raw.githubusercontent.com/AloneMonkey/frida-ios-dump/3.x/dump.py" "$MonkeyDevPath/bin/dump.py"
      #downloadFile "https://raw.githubusercontent.com/AloneMonkey/frida-ios-dump/3.x/dump.js" "$MonkeyDevPath/bin/dump.js"
      
    3. 在本地执行安装脚本 md-install(会跳过对 dump.pydump.js 文件的下载)

      ~ > cd ~/Desktop/MonkeyDevGitHub/bin
      ~/Desktop/MonkeyDevGitHub/bin > sudo bash md-install
      
    4. 单独下载 dump.pydump.js 文件

      # 在桌面新建 Frida-iOS-Dump-GitHub 目录,并将 frida-ios-dump.git 克隆到此目录
      ~ > mkdir ~/Desktop/Frida-iOS-Dump-GitHub
      ~ > git clone https://github.com/AloneMonkey/frida-ios-dump.git ~/Desktop/Frida-iOS-Dump-GitHub
      正克隆到 '~/Desktop/Frida-iOS-Dump-GitHub'...
      remote: Enumerating objects: 184, done.
      remote: Total 184 (delta 0), reused 0 (delta 0), pack-reused 184
      接收对象中: 100% (184/184), 46.66 KiB | 287.00 KiB/s, 完成.
      处理 delta 中: 100% (97/97), 完成.
      
    5. ~/Desktop/Frida-iOS-Dump-GitHub 目录下的 dump.pydump.js 文件,拷贝到 /opt/MonkeyDev/bin目录下

  • ③ 在安装过程中,修改用户 profile 文件时,找不到 MacOSX Package Types.xcspecMacOSX Product Types.xcspec 文件:

    File /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/MacOSX Package Types.xcspec not found
    

    这是因为在新版的 Xcode 中,MacOSX Package Types.xcspecMacOSX Product Types.xcspec 文件存储的目录发生了变化

    解决方案:手动修改安装脚本 md-installMacOSX Package Types.xcspecMacOSX Product Types.xcspec 文件的存储路径

    1. 使用 git 命令将 MonkeyDev.git 克隆到本地

      # 在桌面新建 MonkeyDevGitHub 目录,并将 MonkeyDev.git 克隆到此目录
      ~ > mkdir ~/Desktop/MonkeyDevGitHub
      ~ > git clone https://github.com/AloneMonkey/MonkeyDev.git ~/Desktop/MonkeyDevGitHub
      正克隆到 '~/Desktop/MonkeyDevGitHub'...
      remote: Enumerating objects: 913, done.
      remote: Total 913 (delta 0), reused 0 (delta 0), pack-reused 913
      接收对象中: 100% (913/913), 19.73 MiB | 13.42 MiB/s, 完成.
      处理 delta 中: 100% (502/502), 完成.
      
    2. 进入到 ~/Desktop/MonkeyDevGitHub/bin 目录,编辑安装脚本 md-install,将脚本中 MacOSX Package Types.xcspecMacOSX Product Types.xcspec 文件原来的路径注释掉

      # macosxSDKSpecificationsPath=$macosSdkPlatformPath/Developer/Library/Xcode/Specifications
      # packageTypesForMacOSXPath="$macosxSDKSpecificationsPath/MacOSX Package Types.xcspec"
      # productTypesForMacOSXPath="$macosxSDKSpecificationsPath/MacOSX Product Types.xcspec"
      

      添加 MacOSX Package Types.xcspecMacOSX Product Types.xcspec 文件现在的路径

      # hcg 注:macosSdkPlatformPath="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/"
      macosxSDKSpecificationsPath=$macosSdkPlatformPath/Developer/Library/Xcode/PrivatePlugIns
      packageTypesForMacOSXPath="$macosxSDKSpecificationsPath/IDEOSXSupportCore.ideplugin/Contents/Resources/MacOSX Package Types.xcspec"
      productTypesForMacOSXPath="$macosxSDKSpecificationsPath/IDEOSXSupportCore.ideplugin/Contents/Resources/MacOSX Product Types.xcspec"
      
    3. 在本地执行安装脚本 md-install

      ~ > cd ~/Desktop/MonkeyDevGitHub/bin
      ~/Desktop/MonkeyDevGitHub/bin > sudo bash md-install
      
      Password:
      # 下载基础文件
      Downloading MonkeyDev base from Github...
        % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                       Dload  Upload   Total   Spent    Left  Speed
      100 3452k    0 3452k    0     0  49676      0 --:--:--  0:01:11 --:--:-- 57110
      # 下载模板文件
      Downloading Xcode templates from Github...
        % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                       Dload  Upload   Total   Spent    Left  Speed
      100  306k  100  306k    0     0  87209      0  0:00:03  0:00:03 --:--:-- 87185
      # 下载 frida-ios-dump
      Downloading frida-ios-dump from Github...
        % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                       Dload  Upload   Total   Spent    Left  Speed
      100  9669  100  9669    0     0    436      0  0:00:22  0:00:22 --:--:--  2337
        % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                       Dload  Upload   Total   Spent    Left  Speed
      100 11727  100 11727    0     0    252      0  0:00:46  0:00:46 --:--:--  2468
      # 创建符号链接
      Creating symlink to Xcode templates...
      # 修改用户 profile 文件
      Modifying Bash personal initialization file...
      
    4. 打开 Xcode 查看 MonkeyDev 是否安装成功
      MonkeyDev

修复 MonkeyDev 中 Theos 的路径问题

  • 问题描述

    MonkeyDev 在编译时,会使用 Theos 提供的 logify.pl 工具将 *.xm 文件转成 *.mm 文件。即,Monkey 依赖于 Theos。在 MonkeyDev 中,默认 Theos 的安装路径为 opt/theos。而我之前在安装 Theos 的时候,根据 Theos 官方文档的建议,已经将 Theos 安装在了 ~/theos 中。因此,我现在每次新建 MonkeyDev 的工程时,都需要手动将 MonkeyDev 的配置项 MonkeyDevTheosPath 的值,由 opt/theos 修改为 ~/theos。否则,在编译时会报 Theos 路径找不到的错误!不胜繁琐!

  • 解决方案

    这里为了省事,直接为 ~/theosopt/ 创建一个符号链接,以后对 opt/theos 的调用,都会被重定向到 ~/theos

    首先,禁用系统完整性保护(SIP,System Integrity Protection)

    # 重启 macOS 并按住 option 键,直到出现磁盘图标后松开
    # 然后按住 command + R,直到出现苹果图标后松开
    # 之后等待片刻,进入 macOS 恢复模式
    # 在进入恢复模式后,在顶部菜单栏中选择: 实用工具-终端,在终端输入命令
    csrutil disable
    # 如果返回以下提示,则说明 SIP 禁用成功
    Successfully disabled System Integrity Protection.Please restart the machine for the changes to take effect.
    # 重启 macOS
    reboot
    

    然后,创建符号链接,将对 opt/theos 的调用重定向到对 ~/theos 的调用

    # 重新以可读可写的方式挂载文件系统
    ~ > sudo mount -uw /
    # 创建符号链接,将对 opt/theos 的调用重定向到对 ~/theos 的调用
    ~ > sudo ln -s /Users/Airths/theos /opt/
    

    最后,启用系统完整性保护(SIP,System Integrity Protection)

    # 重启 macOS 并按住 option 键,直到出现磁盘图标后松开
    # 然后按住 command + R,直到出现苹果图标后松开
    # 之后等待片刻,进入 macOS 恢复模式
    # 在进入恢复模式后,在顶部菜单栏中选择: 实用工具-终端,在终端输入命令
    csrutil enable
    # 如果返回以下提示,则说明 SIP 启用成功
    Successfully enabled System Integrity Protection.Please restart the machine for the changes to take effect.
    # 重启 macOS
    reboot
    

OCMethodTrace 官方文档

  • 简介

    OCMethodTrace 用于跟踪和打印 Objective-C 任何对象方法和类方法:

    1. 支持任意 Objective-C 对象方法和类方法的跟踪打印
    2. 支持多架构(arm32arm64i386x86_64
    3. 支持跟踪父类和子类相同的方法
    4. 支持各种灵活配置,包括方法黑白名单等

    GitHub 地址:OCMethodTrace - Trace Any Objective-C Method Calls

  • 配置说明

    ① 全局配置:

    1. LogLevel:Trace 日志级别,对应枚举 MDTraceLogLevel。必须定义,默认值:0

      0:MDTraceLogLeveError	# 错误级别
      1:MDTraceLogLeveDebug	# 调试级别
      2:MDTraceLogLeveMax
      
    2. LogWhen:Trace 日志输出时机,对应枚举 MDTraceLogWhen。必须定义,默认值:0

      0:MDTraceLogWhenStartup		# 启动即输出日志
      1:MDTraceLogWhenVolume			# 根据音量键控制输出日志(增加音量:输出日志;降低音量:关闭日志:默认:关闭日志)
      2:MDTraceLogWhenRegexString	# 日志中包含匹配字符串才输出日志
      3:MDTraceLogWhenMax
      
      LogRegexString					# 日志正则匹配字符串,仅当 LogWhen=MDTraceLogWhenRegexString 有效。日志包含指定正则字符串才输出日志
      
    3. TraceFlag:Trace 控制位(尽量在该处扩展),对应枚举 MDTraceFlag
      全局必须定义,默认值:0x02。指定类可选定义,默认值:0x00

      0x00:MDTraceFlagDefault				# 默认值
      0x01:MDTraceFlagDoesNotUseDescription	# 跳过调用对象 description 方法,避免不正规的 description 实现导致递归
      0x02:MDTraceFlagDumpClassListInfo		# 打印类列表信息,便于调试
      0x04:MDTraceFlagDumpClassMethod		# 打印某个类的方法(不包括父类方法),便于调试
      0x08:MDTraceFlagDumpSuperClassMethod	# 打印某个类的父类方法(包括递归父类的方法),便于调试
      
    4. TraceObject:Trace 对象,对应枚举 TraceObject。必须定义,默认值:1

      0:MDTraceObjectNone		# 屏蔽 Trace 所有类
      1:MDTraceObjectCoreClass	# Trace 引擎指定类的方法(仅测试验证使用),仅需要考虑 CORE_CLASS_LIST
      2:MDTraceObjectUserClass	# Trace 用户指定类的方法,需要考虑 USER_CLASS_LIST + "USER_CLASS_LIST 和 CORE_CLASS_LIST 交集"
      3:MDTraceObjectRegexClass	# Trace 用户指定类 + 正则匹配类的方法,需要考虑 USER_CLASS_LIST + "USER_CLASS_LIST 和 CORE_CLASS_LIST 交集" + "匹配 ClassRegexString 的 CLASS_LIST 和 CORE_CLASS_LIST 交集"
      4:MDTraceObjectMax
      
      ClassRegexString 			# 类正则匹配字符串,仅当 TraceObject=MDTraceObjectRegexClass 有效。Trace 名称与正则字符串匹配的类
      
      MDTraceObjectUserClass 和 MDTraceObjectRegexClass 的区别:
      (1).MDTraceObjectUserClass	# 在考虑 USER_CLASS_LIST 指定的类的时候,如果 USER_CLASS_LIST 和 CORE_CLASS_LIST 存在部分交集,交集对应的类需要合并,合并算法详见:+[MDTraceClassInfo mergeInfoWithCoreInfo:userInfo:userInfo:]
      (2).MDTraceObjectRegexClass	# 在考虑 MDTraceObjectUserClass 的基础上,再考虑 ClassRegexString 正则匹配的类
      
      处理 TraceObject 的详细算法见:-[MDMethodTrace parseClassListInfo],算法内部注意优先级顺序:core > user > regex
      

    ② 指定类配置:

    1. USER_CLASS_LIST:用户指定类列表,必须定义

      TraceMode		# Trace 模式,对应枚举 MDTraceMode,可选定义,默认值:1
      	0:MDTraceModeOff				# 屏蔽 Trace 方法
          1:MDTraceModeAll				# Trace 所有方法
          2:MDTraceModeIncludeWhiteList	# Trace 包含"白名单方法列表"的方法
          3:MDTraceModeExcludeBlackList	# Trace 排除"黑名单方法列表"的方法
          4:MDTraceModeMax
      TraceFlag		# 定义同全局配置,但是粒度更小,属于某个类范围内,可选定义,默认值:0x00
      MethodWhiteList	# 白名单方法列表,可选定义,当 TraceMode=MDTraceModeIncludeWhiteList 时必须定义
      MethodBlackList	# 黑名单方法列表,可选定义,当 TraceMode=MDTraceModeExcludeBlackList 时必须定义
      
    2. CORE_CLASS_LIST:引擎指定类列表,必须定义

      TraceMode		# 定义同 USER_CLASS_LIST 的 TraceMode,但是仅支持 MDTraceModeOff 和 MDTraceModeExcludeBlackList
      TraceFlag		# 定义同全局配置,但是粒度更小,属于某个类范围内,可选定义,默认值:0x00
      MethodBlackList	# 黑名单方法列表,可选定义,当 TraceMode=MDTraceModeExcludeBlackList 时必须定义
      
    3. ClassRegexString:类正则匹配字符串,仅当 TraceObject=MDTraceObjectRegexClass 有效

      # 每个匹配的类使用默认类配置(可以理解成一个默认的 User 类)
      # 使用场景:在不确定 Trace 哪些类的情况下,批量 Trace 一些有规律命名方式的类,匹配字符串如:"Notification|^CM|(F|f)igCaptureSource"
      
    4. CORE_CLASS_LISTUSER_CLASS_LIST 的关系:

      (1).CORE_CLASS_LIST	# 存在的意义在于,OCMethodTrace 框架(引擎)内部也需要调用一些 OC 类方法,为了保证整个框架可以跑起来,避免 Trace 到框架内部使用到的类导致的打印递归循环,所以,需排除这些指定类的全部方法或者部分方法
      (2).USER_CLASS_LIST	# 用户自己指定的类,可以任意发挥。但是如果 CORE_CLASS_LIST 有相同的类,会优先 CORE_CLASS_LIST 中的配置。合并算法详见 "+[MDTraceClassInfo mergeInfoWithCoreInfo:userInfo:userInfo:]"
      
  • 技术要点

    ① 支持跟踪父类和子类相同的方法

    SatanWoo 提出了一个方法(见感谢 2),使用 RunTime 的消息转发机制 + 跳板原理() 实现,流程如下:
    每个方法置换到不同的 IMP 桥上 -> 
    从桥上反推出当前的调用关系(Class 和 Selector)-> 
    构造一个中间态新名字 -> 
    forwardingTargetForSelector(self, "中间态新名字")
    
    但是 SatanWoo 的 demo(见感谢3)有两个比较大的缺陷:
    1.仅支持 arm64 平台,不支持多架构
    2.仅支持实例方法,不支持类方法
    
    为此我做了一些改进,如下:
    1.支持多架构:参考 objc4(见感谢4)的 objc-block-trampolines.m,编写了一个通用的跳板实现,其中通过汇编支持多架构
    2.支持类方法:NSObject(OCMethodTrace) 需 hook 类方法 +[NSObject forwardingTargetForSelector:]
    

    ② Trace 输出无限循环递归的特殊处理

    递归调用都发生在 -[OCMethodTrace omt_forwardInvocation:] 函数内部,有两个地方:
    1.对象调用自身的 description 方法导致的无限递归:
      为了打印对象的描述,需要调用对象的 description 方法,而该方法内部很可能会调用对象的其它内部方法,继而递归又调用 -[OCMethodTrace omt_forwardInvocation:],于是无限循环
      当前解决的方法是:在 -[OCMethodTrace omt_forwardInvocation:] 开始处,通过遍历函数堆栈链查找 OMTBlockRunBeforeSelector,OMTBlockRunAfterSelector 和 OMTDescriptionWithTargetSelector 这几个方法,可以知道是否存在递归,发现递归就不要再调用 runBefore 和 runAfter 回调即可
      缺点:遍历函数堆栈链的方法实在不优雅,而且性能也受影响,需要优化!
    
    2.OCMethodTrace 框架使用到的基础类导致的无限递归:
      比如 -[OCMethodTrace omt_forwardInvocation:] 函数范围内用到一些类:如 NSDate,NSString 或者 runBefore 和 runAfter 回调用到的方法等,一旦他们也被 Trace,也会导致循环递归
      解决的思路是:把这些类放在黑名单里,不再 Trace。当然还有降低循环递归的方式是,内部实现尽量使用 C 或者 C++ 实现
    
  • 待做 && 感谢

    TODO:

    1. 通过遍历堆栈链,判断是否存在循环递归调用的算法性能太差,需优化
    2. 开启全局 dump 类方法(MDTraceFlagDumpClassMethod)容易导致异常,不建议开启

    感谢:

    1. https://github.com/qhd/ANYMethodLog
    2. https://satanwoo.github.io/2017/09/24/mainthreadchecker1
    3. https://github.com/SatanWoo/MainThreadChecker.git
    4. https://opensource.apple.com/tarballs/objc4/objc4-723.tar.gz

在编译时,MonkeyDev 中的 Logos 代码会转换成 Objective-C++ 代码

  • Logos 代码如下:

    %group iOS8
    // 在运行时动态地 Hook HcgPerson 类的方法
    %hook HcgPerson
    +(instancetype)personWithName:(NSString *)aName age:(int)anAge {
        %log;
        id aPerson = %orig(@"Airths", 20);
        return aPerson;
    }
    +(void)run {
        [%c(HcgPerson) run];
        %orig;
    }
    %end // end hook HcgPerson
    %end // end group iOS8
    
    
    
    %group iOS9
    // 在运行时动态地为 NSObject 类创建子类 HcgStudent
    %subclass HcgStudent : NSObject
    // 在运行时动态地为 HcgStudent 类添加 addr 属性
    %property (nonatomic, strong) NSString* addr;
    // 在运行时动态地为 HcgStudent 添加 -learn 方法
    %new
    -(NSString *)learn {
        return [NSString stringWithFormat:@"%@ is learning !", self];
    }
    // 在运行时动态地为 HcgStudent 添加 +test 方法
    %new
    +(NSString *)test {
        return @"All HcgStudents are testing !";
    }
    %end // end subclass HcgStudent : NSObject
    %end // end group iOS9
    
    
    
    // 构造函数
    %ctor {
        NSLog(@"这里是构造函数");
        if (kCFCoreFoundationVersionNumber > 1200) {
            %init(iOS9);
        } else {
            %init(iOS8);
        }
    }
    
    // 析构函数
    %dtor {
        NSLog(@"这里是析构函数");
    }
    
  • 对应的 Objective-C++ 代码如下:

    #line 1 "/Users/Airths/Desktop/Training/MyDemo/MyDemoDylib/Logos/MyDemoDylib.xm"
    
    // 导入 CydiaSubstrate 头文件
    #include <substrate.h>
    
    // 根据编译环境初始化宏定义
    #if defined(__clang__)
        #if __has_feature(objc_arc)
            #define _LOGOS_SELF_TYPE_NORMAL __unsafe_unretained
            #define _LOGOS_SELF_TYPE_INIT __attribute__((ns_consumed))
            #define _LOGOS_SELF_CONST const
            #define _LOGOS_RETURN_RETAINED __attribute__((ns_returns_retained))
        #else
            #define _LOGOS_SELF_TYPE_NORMAL
            #define _LOGOS_SELF_TYPE_INIT
            #define _LOGOS_SELF_CONST
            #define _LOGOS_RETURN_RETAINED
        #endif
    #else
        #define _LOGOS_SELF_TYPE_NORMAL
        #define _LOGOS_SELF_TYPE_INIT
        #define _LOGOS_SELF_CONST
        #define _LOGOS_RETURN_RETAINED
    #endif
    
    // 声明用到的类
    @class HcgStudent;
    @class NSObject;
    @class HcgPerson;
    
    // 获取 HcgPerson 类对象
    static __inline__ __attribute__((always_inline)) __attribute__((unused))
    Class _logos_static_class_lookup$HcgPerson(void) {
        static Class _klass;
        if(!_klass) {
            _klass = objc_getClass("HcgPerson");
        }
        return _klass;
    }
    
    #line 1 "/Users/Airths/Desktop/Training/MyDemo/MyDemoDylib/Logos/MyDemoDylib.xm"
    
    # pragma mark - group iOS8 相关方法声明与实现
    // 用于保存 +[HcgPerson personWithName:age:] 方法原始实现的函数指针
    static HcgPerson* (*_logos_meta_orig$iOS8$HcgPerson$personWithName$age$)(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL, NSString *, int);
    // +[HcgPerson personWithName:age:] 方法替换实现的函数声明
    static HcgPerson* _logos_meta_method$iOS8$HcgPerson$personWithName$age$(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL, NSString *, int);
    // 用于保存 +[HcgPerson run] 方法原始实现的函数指针
    static void (*_logos_meta_orig$iOS8$HcgPerson$run)(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL);
    // +[HcgPerson run] 方法替换实现的函数声明
    static void _logos_meta_method$iOS8$HcgPerson$run(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL);
    
    // +[HcgPerson personWithName:age:] 方法的替换实现
    static HcgPerson* _logos_meta_method$iOS8$HcgPerson$personWithName$age$(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST __unused self,
                                                                            SEL __unused _cmd,
                                                                            NSString * aName,
                                                                            int anAge) {
        NSLog(@"+[<HcgPerson: %p> personWithName:%@ age:%d]", self, aName, anAge);
        id aPerson = _logos_meta_orig$iOS8$HcgPerson$personWithName$age$(self, _cmd, @"Airths", 20);
        return aPerson;
    }
    // +[HcgPerson run] 方法的替换实现
    static void _logos_meta_method$iOS8$HcgPerson$run(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST __unused self,
                                                      SEL __unused _cmd) {
      	// 对应 [%c(HcgPerson) run];
        [_logos_static_class_lookup$HcgPerson() run];
        _logos_meta_orig$iOS8$HcgPerson$run(self, _cmd);
    }
    
    
    
    # pragma mark - group iOS9 相关方法声明与实现
    // -[HcgStudent learn] 方法实现的函数声明
    static NSString * _logos_method$iOS9$HcgStudent$learn(_LOGOS_SELF_TYPE_NORMAL HcgStudent* _LOGOS_SELF_CONST, SEL);
    // +[HcgStudent test] 方法实现的函数声明
    static NSString * _logos_meta_method$iOS9$HcgStudent$test(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL);
    
    // HcgStudent 类 addr 属性 getter 的实现
    __attribute__((used)) static NSString* _logos_property$iOS9$HcgStudent$addr(HcgStudent * __unused self,
                                                                                SEL __unused _cmd) {
        return (NSString*)objc_getAssociatedObject(self, (void *)_logos_property$iOS9$HcgStudent$addr);
    };
    // HcgStudent 类 addr 属性 setter 的实现
    __attribute__((used)) static void _logos_property$iOS9$HcgStudent$setAddr(HcgStudent * __unused self,
                                                                              SEL __unused _cmd,
                                                                              NSString* rawValue) {
        objc_setAssociatedObject(self, (void *)_logos_property$iOS9$HcgStudent$addr, rawValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    // -[HcgStudent learn] 方法的实现
    static NSString * _logos_method$iOS9$HcgStudent$learn(_LOGOS_SELF_TYPE_NORMAL HcgStudent* _LOGOS_SELF_CONST __unused self,
                                                          SEL __unused _cmd) {
        return [NSString stringWithFormat:@"%@ is learning !", self];
    }
    // +[HcgStudent test] 方法的实现
    static NSString * _logos_meta_method$iOS9$HcgStudent$test(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST __unused self,
                                                              SEL __unused _cmd) {
        return @"All HcgStudents are testing !";
    }
    
    
    
    # pragma mark - 构造函数与析构函数
    // 构造函数,对应 %ctor 指令
    /*
    param.argc 用于标识从命令行传递给程序的参数的数量。因为第 0 个参数恒定为程序的名称,所以 argc 最少为 1
    param.argv 以空字符结尾的字符串数组。它可以声明为指向 char 的指针数组(char *argv[ ])或者指向 char 指针的指针(char **argv)
    		   第 0 个字符串 (argv[0]) 是程序的名称,之后的每个字符串都是从命令行传递给程序的参数,最后一个字符串 (argv[argc]) 为 NULL
    param.envp 以空字符结尾的字符串数组。它可以声明为指向 char 的指针数组(char *envp[ ])或者指向 char 指针的指针(char **envp)
    		   每一个元素都标识系统的一个环境变量,以 "名称=值" 的形式存在。最后一个元素为 NULL
    */
    static __attribute__((constructor)) void _logosLocalCtor_de956a09(int __unused argc,
                                                                      char __unused **argv,
                                                                      char __unused **envp) {
        NSLog(@"这里是构造函数");
       	// 如果 CoreFoundation.framework 的版本号 > 1200,则初始化 %group iOS9
        if (kCFCoreFoundationVersionNumber > 1200) {
            {
            	// 获取 NSObject 类对象
                Class _logos_class$iOS9$NSObject = objc_getClass("NSObject");
                {
                	// 动态地创建并注册 HcgStudent 类
                    Class _logos_class$iOS9$HcgStudent = objc_allocateClassPair(_logos_class$iOS9$NSObject, "HcgStudent", 0);
                    objc_registerClassPair(_logos_class$iOS9$HcgStudent);
                    // 获取 HcgStudent 元类对象
                    Class _logos_metaclass$iOS9$HcgStudent = object_getClass(_logos_class$iOS9$HcgStudent);
                    // 动态地向 HcgStudent 类对象添加 addr 属性,对应 %property (nonatomic, strong) NSString* addr;
                    {
                		// 添加 addr 属性    	
                        objc_property_attribute_t _attributes[16];
                        unsigned int attrc = 0;
                        _attributes[attrc++] = (objc_property_attribute_t) { "T", "@\"NSString\"" };
                        _attributes[attrc++] = (objc_property_attribute_t) { "&", "" };
                        _attributes[attrc++] = (objc_property_attribute_t) { "N", "" };
                        class_addProperty(_logos_class$iOS9$HcgStudent,
                                          "addr",
                                          _attributes,
                                          attrc);
                     	// 添加 addr 属性的 getter 方法
                        char _typeEncoding[1024];
                        sprintf(_typeEncoding, "%s@:", @encode(NSString*));
                        class_addMethod(_logos_class$iOS9$HcgStudent,
                                        @selector(addr),
                                        (IMP)&_logos_property$iOS9$HcgStudent$addr,
                                        _typeEncoding);
                     	// 添加 addr 属性的 setter 方法
                        sprintf(_typeEncoding, "v@:%s", @encode(NSString*));
                        class_addMethod(_logos_class$iOS9$HcgStudent,
                                        @selector(setAddr:),
                                        (IMP)&_logos_property$iOS9$HcgStudent$setAddr,
                                        _typeEncoding);
                    }
                    // 动态地向 HcgStudent 类对象添加 -learn 方法,对应 %new -(NSString *)learn { ... }
                    {
                        char _typeEncoding[1024];
                        unsigned int i = 0;
                        memcpy(_typeEncoding + i, @encode(NSString *), strlen(@encode(NSString *)));
                        i += strlen(@encode(NSString *));
                        _typeEncoding[i] = '@';
                        i += 1;
                        _typeEncoding[i] = ':';
                        i += 1;
                        _typeEncoding[i] = '\0';
                        class_addMethod(_logos_class$iOS9$HcgStudent,
                                        @selector(learn),
                                        (IMP)&_logos_method$iOS9$HcgStudent$learn,
                                        _typeEncoding);
                    }
                    // 动态地向 HcgStudent 元类对象添加 +test 方法,对应 %new +(NSString *)test { ... }
                    {
                        char _typeEncoding[1024];
                        unsigned int i = 0;
                        memcpy(_typeEncoding + i, @encode(NSString *), strlen(@encode(NSString *)));
                        i += strlen(@encode(NSString *));
                        _typeEncoding[i] = '@';
                        i += 1;
                        _typeEncoding[i] = ':';
                        i += 1;
                        _typeEncoding[i] = '\0';
                        class_addMethod(_logos_metaclass$iOS9$HcgStudent,
                                        @selector(test),
                                        (IMP)&_logos_meta_method$iOS9$HcgStudent$test,
                                        _typeEncoding);
                    }
                }
            }
        // 如果 CoreFoundation.framework 的版本号 <= 1200,则初始化 %group iOS8
        } else {
            {
            	// 获取 HcgPerson 类对象
                Class _logos_class$iOS8$HcgPerson = objc_getClass("HcgPerson");
                // 获取 HcgPerson 元类对象
                Class _logos_metaclass$iOS8$HcgPerson = object_getClass(_logos_class$iOS8$HcgPerson);
                // 调用 CydiaSubstrate 的 MSHookMessageEx 函数,动态替换 +[HcgPerson personWithName:age:] 方法的实现
                {
                    MSHookMessageEx(_logos_metaclass$iOS8$HcgPerson,
                                    @selector(personWithName:age:),
                                    (IMP)&_logos_meta_method$iOS8$HcgPerson$personWithName$age$,
                                    (IMP*)&_logos_meta_orig$iOS8$HcgPerson$personWithName$age$);
                }
                // 调用 CydiaSubstrate 的 MSHookMessageEx 函数,动态替换 +[HcgPerson run] 方法的实现
                {
                    MSHookMessageEx(_logos_metaclass$iOS8$HcgPerson,
                                    @selector(run),
                                    (IMP)&_logos_meta_method$iOS8$HcgPerson$run,
                                    (IMP*)&_logos_meta_orig$iOS8$HcgPerson$run);
                }
            }
        }
    }
    
    // 析构函数,对应 %dtor 指令
    /*
    param.argc 用于标识从命令行传递给程序的参数的数量。因为第 0 个参数恒定为程序的名称,所以 argc 最少为 1
    param.argv 以空字符结尾的字符串数组。它可以声明为指向 char 的指针数组(char *argv[ ])或者指向 char 指针的指针(char **argv)
    		   第 0 个字符串 (argv[0]) 是程序的名称,之后的每个字符串都是从命令行传递给程序的参数,最后一个字符串 (argv[argc]) 为 NULL
    param.envp 以空字符结尾的字符串数组。它可以声明为指向 char 的指针数组(char *envp[ ])或者指向 char 指针的指针(char **envp)
    		   每一个元素都标识系统的一个环境变量,以 "名称=值" 的形式存在。最后一个元素为 NULL
    */
    static __attribute__((destructor)) void _logosLocalDtor_f1ee81aa(int __unused argc,
                                                                     char __unused **argv,
                                                                     char __unused **envp) {
        NSLog(@"这里是析构函数");
    }
    
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值