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

推荐订阅源

PCI Perspectives
PCI Perspectives
J
Java Code Geeks
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
博客园 - 聂微东
雷峰网
雷峰网
The Cloudflare Blog
V
V2EX
Hacker News: Ask HN
Hacker News: Ask HN
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Hacker News - Newest:
Hacker News - Newest: "LLM"
爱范儿
爱范儿
NISL@THU
NISL@THU
T
The Exploit Database - CXSecurity.com
Project Zero
Project Zero
腾讯CDC
罗磊的独立博客
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
S
Security Affairs
S
SegmentFault 最新的问题
月光博客
月光博客
P
Privacy International News Feed
Last Week in AI
Last Week in AI
博客园 - Franky
C
Cisco Blogs
宝玉的分享
宝玉的分享
Forbes - Security
Forbes - Security
WordPress大学
WordPress大学
博客园 - 叶小钗
L
LINUX DO - 最新话题
博客园_首页
Spread Privacy
Spread Privacy
大猫的无限游戏
大猫的无限游戏
S
Secure Thoughts
IT之家
IT之家
人人都是产品经理
人人都是产品经理
I
Intezer
W
WeLiveSecurity
C
CERT Recently Published Vulnerability Notes
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell
S
Security @ Cisco Blogs
L
Lohrmann on Cybersecurity
美团技术团队

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 Revocation
BlockHook with Private Data
杨萧玉 · 2019-06-19 · via yulingtianxia's blog

文章目录

  1. 1. Block 为何会有 Private Data
    1. 1.1. 特殊的 invoke 函数
    2. 1.2. 获取 Private Data
  2. 2. 适配 BlockHook
    1. 2.1. Hook 真正要执行的 Block
    2. 2.2. 获取 Block 当前 Hook Token
  3. 3. 总结

在使用 BlockHook Hook 所有 Block 对象时,发现有些 Block 被 Hook 后会 Crash。究其原因发现是它们骨骼惊奇,夹带了很多『私货』,不能直接 Hook!本文讲述 BlockHook 在处理这种 Block 时的技术原理,解开含有 Private Data 的 Block 的神秘面纱。

由于关于 Block Private Data 的资料几乎没有,所以我完全可以当回标题党,把这篇文章的标题叫做『你真的了解 Block 么?』或者『这才是 Hook Block 的正确姿势』之类的。但想想还是算了吧,怕被大佬们嘲笑称又『改变业界』了啊。

Block 为何会有 Private Data

首先来看一段代码:

1
2
3
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"I'm dispatch_block_t");
});

dispatch_block_create 创建的 Block 都很特殊,返回的 Block 包含了参数里传入的 Block。此时 dispatch_block_t 虽然表面上是一种普通的 Block,但它的构造暗藏玄机,含有 Private Data,下面会详细解读。

特殊的 invoke 函数

这种 Block 的 invoke 函数指针是固定的,函数名为 ___dispatch_block_create_block_invoke。在 linux 系统下,函数名为 __dispatch_block_create_block_invoke,嗯少了个下划线。这个函数的定义来自 libdispatch.dylib,也就是我们常用的 GCD。

1
2
3
4
5
6
7
8
9
10
11
extern "C" {


extern void DISPATCH_BLOCK_SPECIAL_INVOKE(void *)
#ifdef __linux__
asm("___dispatch_block_create_block_invoke");
#else
asm("____dispatch_block_create_block_invoke");
#endif
void (*const _dispatch_block_special_invoke)(void*) = DISPATCH_BLOCK_SPECIAL_INVOKE;
}

libdispatch 会通过判断 Block 的 invoke 指针是否为 _dispatch_block_special_invoke,来知道这个 Block 是否含有 Private Data。

1
2
3
4
5
6
DISPATCH_ALWAYS_INLINE
static inline bool
_dispatch_block_has_private_data(const dispatch_block_t block)
{
return (_dispatch_Block_invoke(block) == _dispatch_block_special_invoke);
}

不幸的是,_dispatch_block_special_invoke 是私有的。在非调试场景下是无法通过 dladdr 等方式来获取它的函数名的。也就无法用类似上面的代码来判断 Block 是否含有 Private Data 了。

获取 Private Data

使用 dispatch_block_create 创建的 dispatch_block_t 只是个『壳』,真正执行的是其内部包含的 Block。再加上 GCD 所需的一些数据(queue,group,thread,priority 等),这些数据都需要作为 Private Data 追加在 Block 上。对实现 BlockHook 来说最需要关注的就是 dbpd_magicdbpd_block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
OS_OBJECT_DECL_CLASS(voucher);

struct dispatch_block_private_data_s {
unsigned long dbpd_magic;
dispatch_block_flags_t dbpd_flags;
unsigned int volatile dbpd_atomic_flags;
int volatile dbpd_performed;
unsigned long dbpd_priority;
voucher_t dbpd_voucher;
dispatch_block_t dbpd_block;
dispatch_group_t dbpd_group;
dispatch_queue_t dbpd_queue;
mach_port_t dbpd_thread;
};
typedef struct dispatch_block_private_data_s *dispatch_block_private_data_t;

既然无法用 _dispatch_block_special_invoke 来判断 Block 是否含有 Private Data,可以使用 dbpd_magic 魔数来判断。当其值为 0xD159B10C 时(DisBloc 的意思),则表明含有 Private Data。当然这种溢出的方式同样是有风险的,但触及到 PAGEZERO 概率很低

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define DISPATCH_BLOCK_PRIVATE_DATA_MAGIC 0xD159B10C 

DISPATCH_ALWAYS_INLINE
static inline dispatch_block_private_data_t
bh_dispatch_block_get_private_data(struct _BHBlock *block)
{

uint8_t *x = (uint8_t *)block;

x += sizeof(struct _BHBlock);

dispatch_block_private_data_t dbpd = (dispatch_block_private_data_t)x;
if (dbpd->dbpd_magic != DISPATCH_BLOCK_PRIVATE_DATA_MAGIC) {
return nil;
}
return dbpd;
}

最后真正执行的其实是 dbpd_block 这个 Block,dispatch_block_t 只是个保存各种元数据的壳。

适配 BlockHook

虽然说 Private Data 本身并不是 Block 实现中必要的一环,它只是 GCD 对 Block 数据结构的一种『魔改』扩充。但由于 GCD 内部的一些保护机制,会在修改了 Block 的 invoke 指针后触发 crash(__builtin_trap),所以不能直接对含有 Private Data 的 Block 进行 Hook。这就需要 BlockHook 组件做一些适配工作。

Hook 真正要执行的 Block

既然 dbpd_block 才是真正要执行的 Block,那么 Hook 的时候需要先获取 Private Data,然后对其 dbpd_block 进行 Hook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (BHToken *)block_hookWithMode:(BlockHookMode)mode
usingBlock:(id)aspectBlock
{
if (!aspectBlock || ![self block_checkValid]) {
return nil;
}
struct _BHBlock *bh_block = (__bridge void *)self;
if (!_bh_Block_descriptor_3(bh_block)) {
NSLog(@"Block has no signature! Required ABI.2010.3.16. %@", self);
return nil;
}

dispatch_block_private_data_t dbpd = bh_dispatch_block_get_private_data(bh_block);
if (dbpd && dbpd->dbpd_block) {
return [dbpd->dbpd_block block_hookWithMode:mode usingBlock:aspectBlock];
}
return [[BHToken alloc] initWithBlock:self mode:mode aspectBlockBlock:aspectBlock];
}

获取 Block 当前 Hook Token

因为 Hook 的是 dbpd_block,所以获取 Token 的时候也需要额外处理下。要在 dbpd_block 上通过 AssociatedObject 来获取 Token,而不是 dispatch_block_t 上。

1
2
3
4
5
6
7
8
9
10
11
12
- (BHToken *)block_currentHookToken
{
if (![self block_checkValid]) {
return nil
}
dispatch_block_private_data_t dbpd = bh_dispatch_block_get_private_data((__bridge struct _BHBlock *)(self));
if (dbpd && dbpd->dbpd_block) {
return [dbpd->dbpd_block block_currentHookToken];
}
void *invoke = [self block_currentInvokeFunction];
return objc_getAssociatedObject(self, invoke)
}

总结

代码地址: https://github.com/yulingtianxia/BlockHook

一图以蔽之。