目录
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 地址-
打开 https://www.ipaddress.com,在搜索框中输入连接失败的域名
raw.githubusercontent.com
查询结果如下
-
在本机的 host 文件中添加上面查询到的
raw.githubusercontent.com
所对应的 IP 地址sudo vim /etc/hosts
185.199.108.133 raw.githubusercontent.com
解决方案 B:使用 git 命令将
MonkeyDev.git
克隆到本地,然后直接在本地执行安装脚本md-install
-
使用 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), 完成.
-
在本地执行安装脚本
md-install
~ > cd ~/Desktop/MonkeyDevGitHub/bin ~/Desktop/MonkeyDevGitHub/bin > sudo bash md-install
-
-
② 在安装过程中,下载
dump.py
和dump.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.py
和dump.js
文件-
使用 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), 完成.
-
进入到
~/Desktop/MonkeyDevGitHub/bin
目录,编辑安装脚本md-install
,将下载dump.py
和dump.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"
-
在本地执行安装脚本
md-install
(会跳过对dump.py
和dump.js
文件的下载)~ > cd ~/Desktop/MonkeyDevGitHub/bin ~/Desktop/MonkeyDevGitHub/bin > sudo bash md-install
-
单独下载
dump.py
和dump.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), 完成.
-
将
~/Desktop/Frida-iOS-Dump-GitHub
目录下的dump.py
和dump.js
文件,拷贝到/opt/MonkeyDev/bin
目录下
-
-
③ 在安装过程中,修改用户
profile
文件时,找不到MacOSX Package Types.xcspec
和MacOSX 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.xcspec
和MacOSX Product Types.xcspec
文件存储的目录发生了变化解决方案:手动修改安装脚本
md-install
中MacOSX Package Types.xcspec
和MacOSX Product Types.xcspec
文件的存储路径-
使用 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), 完成.
-
进入到
~/Desktop/MonkeyDevGitHub/bin
目录,编辑安装脚本md-install
,将脚本中MacOSX Package Types.xcspec
和MacOSX Product Types.xcspec
文件原来的路径注释掉# macosxSDKSpecificationsPath=$macosSdkPlatformPath/Developer/Library/Xcode/Specifications # packageTypesForMacOSXPath="$macosxSDKSpecificationsPath/MacOSX Package Types.xcspec" # productTypesForMacOSXPath="$macosxSDKSpecificationsPath/MacOSX Product Types.xcspec"
添加
MacOSX Package Types.xcspec
和MacOSX 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"
-
在本地执行安装脚本
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...
-
打开 Xcode 查看 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 路径找不到的错误!不胜繁琐! -
解决方案
这里为了省事,直接为
~/theos
在opt/
创建一个符号链接,以后对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 任何对象方法和类方法:
- 支持任意 Objective-C 对象方法和类方法的跟踪打印
- 支持多架构(
arm32
、arm64
、i386
、x86_64
) - 支持跟踪父类和子类相同的方法
- 支持各种灵活配置,包括方法黑白名单等
GitHub 地址:OCMethodTrace - Trace Any Objective-C Method Calls
-
配置说明
① 全局配置:
-
LogLevel
:Trace 日志级别,对应枚举MDTraceLogLevel
。必须定义,默认值:0
0:MDTraceLogLeveError # 错误级别 1:MDTraceLogLeveDebug # 调试级别 2:MDTraceLogLeveMax
-
LogWhen
:Trace 日志输出时机,对应枚举MDTraceLogWhen
。必须定义,默认值:0
0:MDTraceLogWhenStartup # 启动即输出日志 1:MDTraceLogWhenVolume # 根据音量键控制输出日志(增加音量:输出日志;降低音量:关闭日志:默认:关闭日志) 2:MDTraceLogWhenRegexString # 日志中包含匹配字符串才输出日志 3:MDTraceLogWhenMax
LogRegexString # 日志正则匹配字符串,仅当 LogWhen=MDTraceLogWhenRegexString 有效。日志包含指定正则字符串才输出日志
-
TraceFlag
:Trace 控制位(尽量在该处扩展),对应枚举MDTraceFlag
全局必须定义,默认值:0x02
。指定类可选定义,默认值:0x00
0x00:MDTraceFlagDefault # 默认值 0x01:MDTraceFlagDoesNotUseDescription # 跳过调用对象 description 方法,避免不正规的 description 实现导致递归 0x02:MDTraceFlagDumpClassListInfo # 打印类列表信息,便于调试 0x04:MDTraceFlagDumpClassMethod # 打印某个类的方法(不包括父类方法),便于调试 0x08:MDTraceFlagDumpSuperClassMethod # 打印某个类的父类方法(包括递归父类的方法),便于调试
-
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
② 指定类配置:
-
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 时必须定义
-
CORE_CLASS_LIST
:引擎指定类列表,必须定义TraceMode # 定义同 USER_CLASS_LIST 的 TraceMode,但是仅支持 MDTraceModeOff 和 MDTraceModeExcludeBlackList TraceFlag # 定义同全局配置,但是粒度更小,属于某个类范围内,可选定义,默认值:0x00 MethodBlackList # 黑名单方法列表,可选定义,当 TraceMode=MDTraceModeExcludeBlackList 时必须定义
-
ClassRegexString
:类正则匹配字符串,仅当TraceObject=MDTraceObjectRegexClass
有效# 每个匹配的类使用默认类配置(可以理解成一个默认的 User 类) # 使用场景:在不确定 Trace 哪些类的情况下,批量 Trace 一些有规律命名方式的类,匹配字符串如:"Notification|^CM|(F|f)igCaptureSource"
-
CORE_CLASS_LIST
和USER_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:
- 通过遍历堆栈链,判断是否存在循环递归调用的算法性能太差,需优化
- 开启全局 dump 类方法(
MDTraceFlagDumpClassMethod
)容易导致异常,不建议开启
感谢:
在编译时,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(@"这里是析构函数"); }