mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-24 20:00:37 +00:00
docs: fishhook 底层原理
This commit is contained in:
@@ -522,7 +522,7 @@ LinkMap 文件分为3部分:Object File、Section、Symbols。
|
||||
|
||||
Objective-C 中的方法都会通过 **objc_msgSend** 来调用,而 objc_msgSend 在 Mach-O 文件里是通过 **_objc_selrefs** 这个 **section** 来获取 selector 这个参数的。
|
||||
|
||||
所以,_objc_selrefs 里的方法一定是被调用了的。**_objc_classrefs** 里是被调用过的类, **objc_superrefs** 是调用过 super 的类(继承关系)。通过 _objc_classrefs 和 _objc_superrefs,我们就可以找出使用过的类和子类。
|
||||
里是被调用过的类, **objc_superrefs** 是调用过 super 的类(继承关系)。通过 _objc_classrefs 和 _objc_superrefs,我们就可以找出使用过的类和子类。
|
||||
|
||||
那么,Mach-O 文件中的 _objc_selrefs、_objc_classrefs、_objc_superrefs 如何查看呢?
|
||||
|
||||
|
||||
@@ -5,4 +5,5 @@
|
||||
学过 React.js 之后你再去学习 React Native 会很简单,一些核心的东西理解之后会很简单。比如 React 中的单向数据流、虚拟 Dom、diff 算法、数据变动的批量更新机制、diff 之后的 UI 渲染。
|
||||
|
||||
|
||||
样式布局方面增加了 flexbox,这样子布局在移动端会非常方便,非常简单,
|
||||
样式布局方面增加了 flexbox,这样子布局在移动端会非常方便,非常简单,
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# 带你打造一套 APM 监控系统
|
||||
|
||||
内容脱敏中...
|
||||
File diff suppressed because it is too large
Load Diff
@@ -171,7 +171,13 @@ CI、CD 在稍微有点规模的公司内部都会内建一套自己的系统。
|
||||
旧版本 cocoapods 中:
|
||||
|
||||
- SDK、App 主工程都可以使用 `#import <NativeQS/NativeQS.h>`、`#import "NativeQS.h"`、`#import <NativeQS.h>`
|
||||
|
||||
2. 部分 SDK 的使用了未在 podspec 文件中声明的依赖,在新版本 cocoapods 下会报错(某些 SDK 由于历史原因造成新版本丢失依赖描述)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- 涉及到的 SDK:CMRCTToast
|
||||
- 改造点:
|
||||
问题基本定位是在于, App 主工程引用的 SdkBbs2 SDK 依赖了 SdkBbs2 版本(该版本的依赖描述为 `CMRCTToast (~> 0.1)` )
|
||||
@@ -190,6 +196,7 @@ CI、CD 在稍微有点规模的公司内部都会内建一套自己的系统。
|
||||
s.dependency 'CMDevice', '~> 0.1'
|
||||
```
|
||||
所以, 将 `CMRCTToast.podspec` 中的依赖修改掉。需要兼容不同 RN SDK 的版本。
|
||||
|
||||
3. 部分 pod 的 hook 脚本会失败。
|
||||
- 涉及到的 SDK:无
|
||||
- 改造点:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# 打造一个通用、可配置、多句柄的数据上报 SDK
|
||||
|
||||
内容脱敏中...
|
||||
@@ -355,7 +355,6 @@ HTTPStubsProtocol 继承自 NSURLProtocol,可以在 HTTP 请求发送之前对
|
||||
|
||||
|
||||
|
||||
[参考资料](https://github.com/draveness/analyze/blob/master/contents/OHHTTPStubs/如何进行%20HTTP%20Mock(iOS).md)
|
||||
|
||||
|
||||
|
||||
|
||||
100
Chapter1 - iOS/1.85.md
Normal file
100
Chapter1 - iOS/1.85.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# 统跳
|
||||
|
||||
|
||||
定义:
|
||||
|
||||
使用统一的 URL 来描述特定的业务功能,并通过路由映射到任意资源目标,并基于此可实现 rewrite、埋点、监控、灰度、A/B 等功能
|
||||
|
||||
|
||||
|
||||
iOS非统跳接口调用检测设计方案
|
||||
|
||||
工作原理
|
||||
|
||||
由于ObjectC语言自身的限制,获取编译时的调用关系信息相对较为困难。因此,没有一个简便的方法获取到哪些接口调用了SDK中的方法。
|
||||
|
||||
ObjectC语言继承自C语言的头文件引用的机制,可以作为检测的点。即,如果App中调用了SDK中的某个方法,那么一定会直接或者间接引用了SDK中的某个头文件。通过检测引用头文件,引导iOS开发删除对应SDK的头文件,那么应该可以暴露出App中直接调用SDK接口的位置。从而达到辅助检测非统跳接口的目的。
|
||||
|
||||
|
||||
配置文件
|
||||
配置文件主要用于在检测过程中,提供工程的目录结构相关信息。在生成过程中,提供生成结果文件需要的额外信息。
|
||||
|
||||
```YAML
|
||||
category: login_register_sdk
|
||||
plist: Example/LoginRegisterSDK/LoginRegisterSDK-Info.plist
|
||||
workspace: Example/LoginRegisterSDK.xcworkspace
|
||||
xcodeproj: Example/LoginRegisterSDK.xcodeproj
|
||||
scheme: LoginRegisterSDK-Example
|
||||
source_directory: LoginRegisterSDK/
|
||||
pods_directory: Example/Pods
|
||||
ignore:
|
||||
- LoginRegisterSDK/**/*SDK.m
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
调用流程
|
||||
|
||||
```objective-c
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// ...
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[[TNTRouter sharedRouter] manuallyFetchRoutingTableWithCompletion:^(BOOL succeeded) {
|
||||
|
||||
}];
|
||||
});
|
||||
|
||||
return YES;
|
||||
}
|
||||
```
|
||||
|
||||
```objective-c
|
||||
- (void)manuallyFetchRoutingTableWithCompletion:(TNTManualFetchRoutingTableCompletion)completion {
|
||||
NSDictionary *params = [self getRouterRequestParams];
|
||||
[TMTrinityMGetTask manuallyFetchWithKey:TMNeutronRouterRewriteKey
|
||||
params:params
|
||||
moduleCallback:^(NSString * _Nullable result, NSError * _Nullable error) {
|
||||
if (error) return ;
|
||||
if (result.length == 0) return;
|
||||
|
||||
// encryp & json
|
||||
NSDictionary *routesAndWrites = [self routesAndRewriteForRemoteEncryptedString:result];
|
||||
|
||||
// routes is existed
|
||||
if (routesAndWrites) {
|
||||
// process routes
|
||||
[self loadRoutesAndWritesFromRemote:routesAndWrites];
|
||||
|
||||
// save decrpy data string
|
||||
[self saveRemoteRouteString:result];
|
||||
|
||||
if(completion) completion(true);
|
||||
}
|
||||
} callback:nil];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 参考资料
|
||||
|
||||
- [iOS performSelector传递两个以上参数](https://www.jianshu.com/p/4118793c88df)
|
||||
-
|
||||
|
||||
|
||||
之前看代码理解设计太慢了,不清楚设计背景,所以想请教你一下
|
||||
|
||||
1. 使用统一的 URL 来描述特定的业务功能,并通过路由映射到任意资源目标,并基于此可实现 rewrite、埋点、监控、灰度、A/B 等功能
|
||||
如何理解rewrite、埋点、监控、灰度、A/B 等功能。
|
||||
|
||||
用统跳 url 后,调用某 SDK ,区分 type,h5、RN、Native,native 则 runtime,performSelector,
|
||||
H5、RN 没看到处理逻辑?
|
||||
|
||||
2. 通跳的设计背景是什么?解决业务上什么问题?举个例子理解下工作流程
|
||||
- RoutingTableGenerator.rb 扫描工程 TNT_TARGET,写入 Targets.json 的目的是什么?
|
||||
- DefaultRoutingTableFetcher.rb 调用接口,写入文件
|
||||
- ProjectConfigurator.rb 调用上述2个脚本,写入工程 script
|
||||
|
||||
3. TrinityParams 和 TrinityConfigurator 2个 SDK 背景、作用分别是什么?
|
||||
7
Chapter1 - iOS/1.86.md
Normal file
7
Chapter1 - iOS/1.86.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# GCD 源码探究
|
||||
|
||||
|
||||
|
||||
可以设计实现一个线程池,涉及几个角色、任务队列、调度者如何调度、线程池的有哪些策略。iOS GCD 的线程池策略可以类比 Java 中的4个线程池策略,明白不同语言设计的共同之处
|
||||
|
||||
- https://juejin.im/post/6855807995570618375
|
||||
157
Chapter1 - iOS/1.87.md
Normal file
157
Chapter1 - iOS/1.87.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Objective-C 底层
|
||||
|
||||
1. Objective-C 中对象、类主要是基于 C/C++ 中的结构体实现的。
|
||||
|
||||
方法一:
|
||||
|
||||
可以用 clang 验证。`clang -rewrite-objc main.m -o main.cpp`
|
||||
转到指定平台代码。`xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp`
|
||||
|
||||
```
|
||||
struct NSObject_IMPL {
|
||||
Class isa;
|
||||
};
|
||||
|
||||
/// An opaque type that represents an Objective-C class.
|
||||
typedef struct objc_class *Class;
|
||||
```
|
||||
|
||||
所以 Class 是指向结构体的指针,所以在 64 位系统中占据8个字节,32位系统占据4个字节。
|
||||
|
||||
NSObject 就是结构体占据8个字节。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
发现 `class_getInstanceSize` 和 `malloc_size` 结果不一致。
|
||||
|
||||
```c
|
||||
// Class's ivar size rounded up to a pointer-size boundary.
|
||||
uint32_t alignedInstanceSize() const {
|
||||
return word_align(unalignedInstanceSize());
|
||||
}
|
||||
```
|
||||
|
||||
`class_getInstanceSize` 返回的是类的实例对象的成员变量的大小(四舍五入等于指针指向对象的大小,所以不精确)
|
||||
|
||||
```c
|
||||
extern size_t malloc_size(const void *ptr);
|
||||
/* Returns size of given ptr */
|
||||
```
|
||||
|
||||
`malloc_size` 返回的就是指针指向的对象大小.
|
||||
|
||||

|
||||
|
||||
结论:
|
||||
|
||||
- 当某个类继承自 NSObject 的时候,如果没有其他属性,则这个类占据16个字节。`class_getInstanceSize` 占据 8 , `malloc_size` 占据 16
|
||||
- 当某个类继承自 NSObject 的时候,如果有其他属性,则这个类占据16个字节。`class_getInstanceSize` 占据 16 , `malloc_size` 占据 16
|
||||
|
||||
方法二: 从源代码角度出发验证(从上大下)
|
||||
|
||||
```c++
|
||||
// NSObject.mm
|
||||
// Replaced by ObjectAlloc
|
||||
+ (id)allocWithZone:(struct _NSZone *)zone {
|
||||
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
|
||||
}
|
||||
|
||||
// objc-class-old.mm
|
||||
id
|
||||
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
|
||||
{
|
||||
id obj;
|
||||
|
||||
if (fastpath(!zone)) {
|
||||
obj = class_createInstance(cls, 0);
|
||||
} else {
|
||||
obj = class_createInstanceFromZone(cls, 0, zone);
|
||||
}
|
||||
|
||||
if (slowpath(!obj)) obj = _objc_callBadAllocHandler(cls);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// objc-class-old.mm
|
||||
/***********************************************************************
|
||||
* _class_createInstance. Allocate an instance of the specified
|
||||
* class with the specified number of bytes for indexed variables, in
|
||||
* the default zone, using _class_createInstanceFromZone.
|
||||
**********************************************************************/
|
||||
static id _class_createInstance(Class cls, size_t extraBytes)
|
||||
{
|
||||
return _class_createInstanceFromZone (cls, extraBytes, nil);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ALWAYS_INLINE id
|
||||
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
|
||||
int construct_flags = OBJECT_CONSTRUCT_NONE,
|
||||
bool cxxConstruct = true,
|
||||
size_t *outAllocatedSize = nil)
|
||||
{
|
||||
ASSERT(cls->isRealized());
|
||||
|
||||
// Read class's info bits all at once for performance
|
||||
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
|
||||
bool hasCxxDtor = cls->hasCxxDtor();
|
||||
bool fast = cls->canAllocNonpointer();
|
||||
size_t size;
|
||||
|
||||
size = cls->instanceSize(extraBytes);
|
||||
if (outAllocatedSize) *outAllocatedSize = size;
|
||||
|
||||
id obj;
|
||||
if (zone) {
|
||||
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
|
||||
} else {
|
||||
obj = (id)calloc(1, size);
|
||||
}
|
||||
if (slowpath(!obj)) {
|
||||
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
|
||||
return _objc_callBadAllocHandler(cls);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!zone && fast) {
|
||||
obj->initInstanceIsa(cls, hasCxxDtor);
|
||||
} else {
|
||||
// Use raw pointer isa on the assumption that they might be
|
||||
// doing something weird with the zone or RR.
|
||||
obj->initIsa(cls);
|
||||
}
|
||||
|
||||
if (fastpath(!hasCxxCtor)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
|
||||
return object_cxxConstructFromClass(obj, cls, construct_flags);
|
||||
}
|
||||
|
||||
// objc-runtime-new.h
|
||||
// Class's ivar size rounded up to a pointer-size boundary.
|
||||
uint32_t alignedInstanceSize() const {
|
||||
return word_align(unalignedInstanceSize());
|
||||
}
|
||||
|
||||
// objc-runtime-new.h
|
||||
size_t instanceSize(size_t extraBytes) const {
|
||||
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
|
||||
return cache.fastInstanceSize(extraBytes);
|
||||
}
|
||||
|
||||
size_t size = alignedInstanceSize() + extraBytes;
|
||||
// CF requires all objects be at least 16 bytes.
|
||||
if (size < 16) size = 16;
|
||||
return size;
|
||||
}
|
||||
```
|
||||
|
||||
`CF requires all objects be at least 16 bytes.` 系统为 NSObject 对象分配了至少16个字节大小的空间,但是它使用了 8 个字节大小的空间用来存放 ivars(64位系统)
|
||||
|
||||
2. 某个类继承自 NSObject 的情况,内存如何分配
|
||||
40
Chapter1 - iOS/1.88.md
Normal file
40
Chapter1 - iOS/1.88.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# fishhook 原理
|
||||
|
||||
1. image: 代码编译后的可执行文件,被加载到内存中,就叫做镜像文件。
|
||||
|
||||
2. MachO 结构:
|
||||
|
||||
3. iOS 代码在编译时没有办法确定方法的实现地址。**动态库共享缓存**,里面有动态库。NSLog 属于 Foundation 框架,每个手机内部中的地址不一定。
|
||||
|
||||

|
||||
|
||||
4. DYLD 动态链接器,负责将可执行文件加载到内存中。App 启动后,DYLD 将 Foundation、UIKit 等加载进动态库共享缓存中,但是加载到的位置不确定。
|
||||
|
||||
5. 可执行文件头有 **Load Commands**:加载命令。告诉 DYLD 依赖了什么。
|
||||
|
||||
6. 从 NSLog 找到代码实现,经历过2种方式。早期:重定向。现在:PIC 技术(位置代码独立)。
|
||||
|
||||
7. 可执行文件:
|
||||
|
||||
- 代码段:可读可执行
|
||||
- 数据段:可读可写
|
||||
|
||||
8. 写的业务代码里面假如某一行调用了 `NSLog`,那么在编译阶段,使用 NSLog 只是 IDE 提供了功能,让你可以看到声明而已。编译后的可执行文件,还是不知道 NSLog 的具体函数地址,它指向了一个表(类似一个应用程序的外部函数名,函数真正实现地址),去这个表里面找地址,这个表叫做**符号表**。
|
||||
|
||||
当 DYLD 加载当前可执行文件的时候,才将这个表每个编号对应的函数地址去填上去,这个动作叫做**符号绑定**。
|
||||
|
||||
当真正去调用 NSLog 函数的时候才去这个符号表中去寻找函数地址,去调用实现。
|
||||
|
||||
| 编号(符号) | 地址 | |
|
||||
| ------------ | -------- | ---- |
|
||||
| NSLog | 0xaabbcc | |
|
||||
| ... | ... | |
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
9. fishhook 做的事情就是将系统的符号表,将符号表中的特定符号对应的地址,修改为自定义的函数地址。起到了 hook 作用。也就是说外部的 c 函数,在 iOS 中的调用属于**动态调用**。
|
||||
|
||||
https://www.bilibili.com/video/BV1UZ4y1u7Ba?from=search&seid=14997461811427810898
|
||||
|
||||
@@ -87,4 +87,8 @@
|
||||
* [81、__asm__ 重命名符号](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.81.md)
|
||||
* [82、runtime](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.82.md)
|
||||
* [83、NSURLProtocol 应用场景](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.83.md)
|
||||
* [84、WKWebView 技巧集合](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.84.md)
|
||||
* [84、WKWebView 技巧集合](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.84.md)
|
||||
* [85、统跳技术](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.85.md)
|
||||
* [86、GCD 源码探究](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.86.md)
|
||||
* [87、Objective-C 底层探究](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.87.md)
|
||||
* [88、fishhook 原理](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.88.md)
|
||||
|
||||
Reference in New Issue
Block a user