惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
N
Netflix TechBlog - Medium
博客园 - 叶小钗
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
V
V2EX
IT之家
IT之家
J
Java Code Geeks
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
GbyAI
GbyAI
D
Docker
S
Secure Thoughts
Recent Announcements
Recent Announcements
Webroot Blog
Webroot Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
云风的 BLOG
云风的 BLOG
博客园_首页
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Security Archives - TechRepublic
Security Archives - TechRepublic
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
N
News | PayPal Newsroom
S
Security @ Cisco Blogs
I
InfoQ
Last Week in AI
Last Week in AI
SecWiki News
SecWiki News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
W
WeLiveSecurity
T
Troy Hunt's Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Attack and Defense Labs
Attack and Defense Labs
美团技术团队
T
The Blog of Author Tim Ferriss
Google DeepMind News
Google DeepMind News
Martin Fowler
Martin Fowler
B
Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Scott Helme
Scott Helme
T
Tor Project blog
Know Your Adversary
Know Your Adversary
有赞技术团队
有赞技术团队
Hugging Face - Blog
Hugging Face - Blog
Recorded Future
Recorded Future
C
Cyber Attacks, Cyber Crime and Cyber Security
AI
AI
G
Google Developers Blog

yulingtianxia's blog

Flutter 官方终于出手了,DartNative 将何去何从? CPP-Summit 2020 实现 Native 异步回调 Flutter 如何实现 Flutter 同步调用 Native API DartNative 内存自动管理 如何实现一行命令自动生成 Flutter 插件 告别 Flutter Channel,调用 Native API 仅需一行代码! BlockHook and Memory Safety Passing Out Parameter in DartNative 在 Flutter 中玩转 Objective-C Block DartNative Struct DartNative Memory Management: C++ Non-Object DartNative Memory Management: NSObject 谈谈 dart_native 混合编程引擎的设计 用 Dart 来写 Objective-C 代码 App 二进制文件重排已经被玩坏了 BlockHook with Invocation(2) BlockHook with Invocation(1) BlockHook with Private Data
BlockHook with Revocation
杨萧玉 · 2019-05-26 · via yulingtianxia's blog

文章目录

  1. 1. 按顺序构造『虚拟的』 Hook 链表
  2. 2. 撤销 Hook
  3. 3. 总结

BlockHook 开创性地解决了 Objective-C 语言界 Hook Block 的问题,但也迎来了新的问题:

  1. 如何知道某个 Block 对象被谁 Hook 过?
  2. 多次 Hook 的先后顺序?
  3. 如何处理好多次 Hook 同一个 Block 后对任意一次 Hook 的撤销?

这些问题也是开发者在使用时向我反馈过的问题,在这篇文章里,这些问题都将会解决。

关于 BlockHook 的原理,可以先阅读之前的文章:

按顺序构造『虚拟的』 Hook 链表

首先要有一个链表来按先后顺序记录一个 Block 对象上所有的 Hook。这个链表的格式以及持有关系也需要考虑在内。

为此我构造了一个虚拟的链表来记录 Hook 的先后关系,而不是单独创建一个链表显式的记录。首先介绍下 Block 与 token 之间的引用关系:

可以看出每个 BHToken 记录了原始和替换后的 invoke 函数指针,那么先后两次 Hook 就靠 invoke 函数指针来关联了:每个 tokne 的 originalInvoke 就是上一次 Hook 的 token 的 replacementInvoke。而拿到 token 又是靠 Block 对象上的 AssociatedObject,且 key 为 replacementInvoke。这样就构造了一条虚拟的链表:想要获得上次 Hook 的 token,只需在 Hook 的 Block 对象上使用 originalInvoke 作为 key 即可。

下面的代码展示了获取下个 token 的 next 实现。因为链表可能会有新的插入和删除节点操作,所以需确保线程安全。

1
2
3
4
5
6
7
8
9
10
11
- (BHToken *)next
{
BHLock *lock = [self.block bh_lockForKey:@selector(next)];
[lock lock]
if (!_next) {
_next = objc_getAssociatedObject(self.block, self.originInvoke);
}
BHToken *result = _next
[lock unlock]
return result
}

这里使用 replacementInvoke 来作为 AssociatedObject 的 key 真是一举多得

  1. 将 token 的生命周期绑定到 Block 对象上,实现 self-managed
  2. 因为函数指针地址是唯一的,确保 Block 上关联每个 token 的 key 不会冲突
  3. Block 的 invoke 指针作为 key,可以找到最后一次 Hook 的 token。进而按 Hook 先后顺序遍历出所有的 token。

下面的代码展示了如何获取最后一次 Hook 的 token。在读取 invoke 函数指针的时候,注意保证线程安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (BHToken *)block_currentHookToken
{
if (![self block_checkValid]) {
return nil
}
void *invoke = [self block_currentInvokeFunction];
return objc_getAssociatedObject(self, invoke)
}

- (void *)block_currentInvokeFunction
{
struct _BHBlock *bh_block = (__bridge void *)self;
BHLock *lock = [self bh_lockForKey:_cmd];
[lock lock]
void *invoke = bh_block->invoke;
[lock unlock]
return invoke
}

撤销 Hook

俗话说『请神容易送神难』。好多 Hook 框架只解决的如何 Hook,但是却无法撤销恢复原样,留下一堆烂摊子。

在搭建了 Hook 链表的基础上,多次 Hook 的链表可以简化成 invoke 函数指针之间的关系:

那么撤销 Hook 就可以从链表头部开始遍历,找到当前要 remove 的 token。接着链表上删除这个 token,而这又可以分为两个子问题:

  1. 移除最后一次 Hook:需要将 Block 的 invoke 指针指向 token 的 originalInvoke
  2. 移除最后一次 Hook:需要将上一次 Hook token 的 originalInvoke 指向当前 token 的 originalInvoke

最后肯定还要解除 Block 对象对 token 的持有。

这部分逻辑的实现代码如下,在操作 Block 的 invoke 指针时依然需要注意线程安全问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
- (BOOL)remove
{
if (self.isStackBlock) {
NSLog(@"Can't remove token for StackBlock!");
return NO;
}
self.deadBlock = nil;
if (self.originInvoke) {
if (self.block) {
BHToken *current = [self.block block_currentHookToken];
BHToken *last = nil;
while (current) {
if (current == self) {
if (last) {
last.originInvoke = self.originInvoke;
last.next = nil;
}
else {
BHLock *lock = [self.block bh_lockForKey:@selector(block_currentInvokeFunction)];
[lock lock];
((__bridge struct _BHBlock *)self.block)->invoke = self.originInvoke;
[lock unlock];
}
break;
}
last = current;
current = [current next];
}
}
self.originInvoke = NULL;
objc_setAssociatedObject(self.block, _replacementInvoke, nil, OBJC_ASSOCIATION_RETAIN);
return YES;
}
return NO;
}

总结

BlockHook 还在不断完善每一个细节,尽可能做到有始有终,至善尽美。