docs: fishhook 底层原理

This commit is contained in:
杭城小刘
2020-08-10 20:16:18 +08:00
parent 4c6ccac464
commit 9fd9df9337
21 changed files with 10583 additions and 1168 deletions

6582
1.74.md Normal file

File diff suppressed because it is too large Load Diff

1153
1.75.md Normal file

File diff suppressed because it is too large Load Diff

2512
1.80.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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 如何查看呢?

View File

@@ -5,4 +5,5 @@
学过 React.js 之后你再去学习 React Native 会很简单,一些核心的东西理解之后会很简单。比如 React 中的单向数据流、虚拟 Dom、diff 算法、数据变动的批量更新机制、diff 之后的 UI 渲染。
样式布局方面增加了 flexbox这样子布局在移动端会非常方便非常简单
样式布局方面增加了 flexbox这样子布局在移动端会非常方便非常简单

View File

@@ -1,2 +1,3 @@
# 带你打造一套 APM 监控系统
内容脱敏中...

File diff suppressed because it is too large Load Diff

View File

@@ -171,7 +171,13 @@ CI、CD 在稍微有点规模的公司内部都会内建一套自己的系统。
旧版本 cocoapods 中:
- SDK、App 主工程都可以使用 `#import <NativeQS/NativeQS.h>`、`#import "NativeQS.h"`、`#import <NativeQS.h>`
2. 部分 SDK 的使用了未在 podspec 文件中声明的依赖,在新版本 cocoapods 下会报错(某些 SDK 由于历史原因造成新版本丢失依赖描述)
- 涉及到的 SDKCMRCTToast
- 改造点:
问题基本定位是在于, 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
- 改造点:

View File

@@ -0,0 +1,3 @@
# 打造一个通用、可配置、多句柄的数据上报 SDK
内容脱敏中...

View File

@@ -355,7 +355,6 @@ HTTPStubsProtocol 继承自 NSURLProtocol可以在 HTTP 请求发送之前对
[参考资料](https://github.com/draveness/analyze/blob/master/contents/OHHTTPStubs/如何进行%20HTTP%20MockiOS.md)

100
Chapter1 - iOS/1.85.md Normal file
View 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 ,区分 typeh5、RN、Nativenative 则 runtimeperformSelector
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
View File

@@ -0,0 +1,7 @@
# GCD 源码探究
可以设计实现一个线程池涉及几个角色、任务队列、调度者如何调度、线程池的有哪些策略。iOS GCD 的线程池策略可以类比 Java 中的4个线程池策略明白不同语言设计的共同之处
- https://juejin.im/post/6855807995570618375

157
Chapter1 - iOS/1.87.md Normal file
View 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个字节。
![image-20200726130625217](/Users/lbp/Library/Application Support/typora-user-images/image-20200726130625217.png)
![image-20200726125926892](/Users/lbp/Library/Application Support/typora-user-images/image-20200726125926892.png)
发现 `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` 返回的就是指针指向的对象大小.
![image-20200726130920234](/Users/lbp/Library/Application Support/typora-user-images/image-20200726130920234.png)
结论:
- 当某个类继承自 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 个字节大小的空间用来存放 ivars64位系统
2. 某个类继承自 NSObject 的情况,内存如何分配

40
Chapter1 - iOS/1.88.md Normal file
View File

@@ -0,0 +1,40 @@
# fishhook 原理
1. image: 代码编译后的可执行文件,被加载到内存中,就叫做镜像文件。
2. MachO 结构:
3. iOS 代码在编译时没有办法确定方法的实现地址。**动态库共享缓存**里面有动态库。NSLog 属于 Foundation 框架,每个手机内部中的地址不一定。
![image-20200810182447587](https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/image-20200810182447587.png)
4. DYLD 动态链接器负责将可执行文件加载到内存中。App 启动后DYLD 将 Foundation、UIKit 等加载进动态库共享缓存中,但是加载到的位置不确定。
5. 可执行文件头有 **Load Commands**:加载命令。告诉 DYLD 依赖了什么。
6. 从 NSLog 找到代码实现经历过2种方式。早期重定向。现在PIC 技术(位置代码独立)。
7. 可执行文件:
- 代码段:可读可执行
- 数据段:可读可写
8. 写的业务代码里面假如某一行调用了 `NSLog`,那么在编译阶段,使用 NSLog 只是 IDE 提供了功能,让你可以看到声明而已。编译后的可执行文件,还是不知道 NSLog 的具体函数地址,它指向了一个表(类似一个应用程序的外部函数名,函数真正实现地址),去这个表里面找地址,这个表叫做**符号表**。
当 DYLD 加载当前可执行文件的时候,才将这个表每个编号对应的函数地址去填上去,这个动作叫做**符号绑定**。
当真正去调用 NSLog 函数的时候才去这个符号表中去寻找函数地址,去调用实现。
| 编号(符号) | 地址 | |
| ------------ | -------- | ---- |
| NSLog | 0xaabbcc | |
| ... | ... | |
![image-20200810190329801](https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/image-20200810190329801.png)
9. fishhook 做的事情就是将系统的符号表,将符号表中的特定符号对应的地址,修改为自定义的函数地址。起到了 hook 作用。也就是说外部的 c 函数,在 iOS 中的调用属于**动态调用**。
https://www.bilibili.com/video/BV1UZ4y1u7Ba?from=search&seid=14997461811427810898

View File

@@ -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)

View File

@@ -13,7 +13,11 @@
React 性能高效的一个原因就是 Virtual Dom 的应用和 diff 之后的 Batch Update批量处理类比 Vue 中的 $nextTick。有 Native 开发经验的同学对于这里应该有似曾相识的感觉,和 RunLoop 很像。任何 UI 层变动的东西提交给系统,系统再下一次的运行循环到来的时候统一去渲染。)
- https://www.infoq.cn/article/2iviqjklwa4JkF0YNQGZ
- https://www.infoq.cn/article/AiQMbjI0oXZ1UrueiBze
## Diff 算法
@@ -235,4 +239,3 @@ class Welcome extends React.Component {
- Vue 设计思想How easy it can be。ReactHow corrct it can be 和 all in jscss写法也在用 js 控制,比如 styled-component

View File

@@ -88,7 +88,8 @@ app.on('activate', function () {
app 对象在 `whenReady` 的时候执行 createWindow 方法。内部创建了一个 `BrowserWindow` 对象,指定了大小和功能设置。
1. webPreferences Object (可选) - 网页功能的设置。
1. webPreferences Object (可选) - 网页功能的设置。
2. preload String (可选) - 在页面运行其他脚本之前预先加载指定的脚本 无论页面是否集成 Node, 此脚本都可以访问所有 Node API 脚本路径为文件的绝对路径。 当 node integration 关闭时, 预加载的脚本将从全局范围重新引入 node 的全局引用标志。
`mainWindow.loadFile('index.html')` 加载了同级目录下的 index.html 文件。也可以加载服务器资源(部署好的网页),比如 `win.loadURL('https://github.com/FantasticLBP')`
@@ -143,7 +144,7 @@ electron 分为**渲染进程和主进程**。和 Native 中的概念不一样
- 问题1: 不稳定
早期浏览器需要借助插件来实现类似 Web 视频、Web 游戏等各种“强大”的功能。但插件往往是最容易出现问题的模块。此外因为运行在浏览器进程中,所以一个插件的意外奔溃到导致整个浏览器到的奔溃。
早期浏览器需要借助插件来实现类似 Web 视频、Web 游戏等各种“强大”的功能。但插件往往是最容易出现问题的模块。因为运行在浏览器进程中,所以一个插件的意外奔溃到导致整个浏览器到的奔溃。
除了插件之外,**渲染引擎模块也是不稳定的**。通常一些复杂的 Javascript 代码就有可能导致渲染引擎模块的奔溃。和插件一样,渲染引擎的奔溃会导致整个浏览器奔溃。
@@ -184,8 +185,6 @@ electron 分为**渲染进程和主进程**。和 Native 中的概念不一样
对于**内存泄漏的解决办法更加简单**。当关闭某个页面的时候,整个渲染进程就会被关闭,所以该进程所占用的内存都会被系统回收,于是轻松解决了浏览器页面的内存泄漏问题。
**解决了安全问题。**采用多进程架构可以使用**安全沙箱技术**。沙箱可以看成是操作系统给浏览器一个小黑盒黑盒内部可以执行程序但是不能访问操作系统资源、不能访问硬盘数据也不能在敏感位置读取任何数据例如你的文档和桌面。Chrome 把插件进程和渲染进程使用沙箱隔离起来,这样即使在渲染进程或者浏览器进程中执行了恶意代码,恶意代码也无法突破沙箱限制去获取系统权限。
沙箱隔离起来的进程必须使用 IPC 通道才可以与浏览器内核进程通信,通信进程就会进行安全的检查。

View File

@@ -21,10 +21,3 @@
## 反馈
定期更新博文。如果在查看文章的时候发现了问题可以提出 issue。95年小双鱼关注大前端领域有事情可以通过[微博](http://weibo.com/u/3194053975)联系)
## 交流
如果你也是大前端路上的一名修行者,可以通过[微博](http://weibo.com/u/3194053975)联系我,拉你入群一起在「大前端自习室」学习交流。

View File

@@ -87,6 +87,10 @@
* [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)
* [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)
* [Chapter2 - Web FrontEnd](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter2%20-%20Web%20FrontEnd/chapter2.md)
* [1、-last-child与-last-of-type你只是会用有研究过区别吗](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter2%20-%20Web%20FrontEnd/2.1.md)
@@ -177,7 +181,7 @@
* [17、一套开发规范](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.18.md)
* [18、云服务器靠谱推荐](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.19.md)
* [19、规范化团队 git 提交信息](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.20.md)
* [20、如何画架构图](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.21.md)
* [20、如何画架构图](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter7%20-%20Geek%20Talk/7.21.md)
* [Chapter8 - Finance](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter8%20-%20Finance/chapter8.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB