大多数浏览器和
Developer App 均支持流媒体播放。
-
系统扩展和 DriverKit
在推动 macOS 现代化和改进 macOS 安全性与可靠性的道路上,接下来有一步就是为内核扩展和驱动程序提供更加出色的架构。了解如何利用系统扩展和 DriverKit 完成这一过渡。
资源
相关视频
WWDC22
WWDC21
WWDC20
WWDC19
-
下载
我是 Joe 稍后我的同事 Simon 和 Scott 也会加入 我们来自 Core OS 组 我们想告诉你关于 macOS 10.15 Catalina 中 内核扩展的一些新开发
内核扩展或 Kext 这项技术从一开始 就是 macOS 的一部分
使用 kexts 你可以构建功能强大且创新的 App 以扩展操作系统的内置功能 这种扩展系统的能力 是使 Mac 成为 Mac 的重要部分 但是内核扩展 存在一些问题
它们很难开发 而且调试时 它们可能对机器的安全性 和隐私性造成风险 并且它们可能对系统的可靠性构成风险
是时候升级了
macOS Catalina 引入了 两项新技术 名为系统扩展和 DriverKit
使用它们 你的 App 可以 扩展操作系统 比以往任何时候都 更可靠 更安全 更容易开发 这就是我们今天要谈的内容 首先我会介绍这些技术 并向你们展示它们是如何避免 内核扩展问题的
然后 Simon 将向我们 展示如何使用 DriverKit 构建驱动扩展
Scott 将向我们展示如何 编写和调试一个简单的 USB 驱动器
然后我会告诉你们 如何在 App 中包含系统扩展 我们开始吧 系统扩展是我们 在 macOS Catalina 中的 第一项新技术 系统扩展是你 App 的一部分 它以类似于内核扩展的方式 扩展操作系统的功能 但运行在内核之外的用户空间中
在 Catalina 中 可以构建 三种系统扩展 它们是网络扩展 驱动程序和端点安全程序扩展
网络扩展是网络内核扩展的替代
它们可以过滤和重新路由 网络流量或连接到 VPN
有关网络扩展的更多信息 周五上午 有一个专门针对它们的会议
端点安全扩展替代的是 可以使用 KAUTH 接口 拦截和监视与安全 相关事件的 Kext 你可以通过这种方式 构建的一些 App 进行端点检测和响应以及数据丢失保护
如果你对端点安全扩展感兴趣 请访问安全实验室 现在有一个正在进行中 并在这次演讲后继续 还有一个是在周四下午 第三种类型的扩展是 驱动扩展 它可以替代使用 IOKit 的设备驱动 Kext 在 Catalina 中你可以控制 USB 串口 网络接口 和人机接口设备
驱动扩展是 用 DriverKit 构建的 这是我们在 Catalina 中的第二项技术
DriverKit 是一个新的 SDK 包含所有基于 IOKit 的新框架 但经过了更新和变革
旨在使用内核之外的 用户空间构建驱动扩展 现在我们已经遇到了这些新技术 让我们看看它们是如何避免 内核扩展的问题的
在这两个定义中 我都用了一个短语 在内核之外的用户空间
为什么这很重要?
内核是一个非常苛刻 且难以编程的环境 内核管理着机器上 发生的一切 所以它永远不能停止运行 永远不能等任何事情发生 绝不能崩溃
内核中的代码必须快速 必须是可预测的 必须节约使用内存等资源 而且本质上不能有漏洞
要编写满足 所有这些限制的代码 非常困难
系统扩展在内核外的 用户空间中运行 这意味着它们运行在 舒适的现代编程环境中
这使得它们比 Kext 更容易开发 因为内核代码对何时 以及如何在线程之间 分配内存或同步有限制
这意味着它 不能使用大多数系统框架 比如 Foundation 因为它们不是为在这种环境中运行而设计的
Kext 开发中唯一支持的 语言是 C 和 C++
而另一方面 系统扩展 没有这样的限制 这意味着它们可以 使用 macOS SDK 中的任何框架结构 并且可以用包括 Swift 在内的任何语言编写
但有一个例外 驱动扩展由于 与硬件关系密切 仍然存在一些限制
它们必须使用 DriverKit 框架并运行在 并在定制的 Runtime 中运行 从而将它们与系统的其余部分隔开 驱动扩展必须 用 C 或 C++ 编写 而默认为 C++17 系统扩展也比 内核扩展更容易调试 将调试器附加到内核 会使内核和整个机器 包括调试器停止运行 这意味着你通常 需要第二台机器进行调试 你可能需要 特殊的线缆或网络配置来连接机器 构建 测试和调试 Kext 的周期可能很慢 因为 Kext 中的任何崩溃 都意味着整个系统 必须重新启动 内核调试器的支持有限 它无法执行 诸如计算表达式或 打印对象值之类的操作 另一方面 系统扩展 在调试的同时可以保持 内核继续运行
如果扩展程序崩溃 无需重新启动
你可以在一台具有 完全调试支持的机器上构建 测试和调试所有内容 但是与内核扩展相比 系统扩展的最大改进 是在安全性 私密性和可靠性方面 内核有许多工作 但最重要的一项是 定义和执行 系统安全策略的规则 内核将 App 彼此分开 并将 App 与对硬件的直接访问分开 然后它允许它们 遵循安全策略的规则 共享数据和系统服务 加载内核扩展时 它将成为内核的一部分
它可以访问机器上的所有内容 这就是 Kext 强大的原因 但它也可能是种危险 因为内核扩展是 制定安全规则的内核的一部分 所以它在规则之上 如果 Kext 有一个漏洞 允许它被入侵 它就可以接管整个机器 做开发人员和用户从未想过 要做的事情 没有任何安全规则可以约束它
这意味着 Kext 中的 任何漏洞都可能是一个关键性的安全问题 Kext 中的任何漏洞都可能十分危险
发生了什么? 我的幻灯片呢?
Kext 中的任何漏洞都可能 是一个关键的可靠性问题 因为内核不仅会崩溃 还会发生致命错误 并且整个机器都必须重启 如果你是 Kext 开发人员 你肯定会看到很多这个对话框 不幸的是我们的用户也会看到 让我们看看情况如何随着 系统扩展而变化
系统扩展在用户空间中运行 与其他 App 一样 它必须遵守系统安全策略的规则 和其他 App 不一样的是 系统扩展被授予 执行特殊任务的特权
比如 它可以直接访问 相关的硬件设备 或使用特殊的 API 与内核直接通信
如果系统扩展崩溃 系统和 App 的其余部分 不受影响并继续运行 基于所有这些原因 我们认为系统扩展是 Mac 平台向前迈出的一大步 事实上我们认为它的改进效果十分显著 我们建议你立刻采用 正如 Sebastian 昨天 在 Platforms State of the Union 中所说 我们已经开始弃用内核扩展的进程 macOS 10.15 Catalina 将是 最后一个完全 支持内核扩展的版本
具体来说 对于系统扩展所支持的功能 和 DriverKit 支持的设备系列 使用内核扩展来执行 相同的工作现在已被弃用 未来的 macOS 版本 将不会加载这些种类的内核扩展 在未来的版本中 我们将向 DriverKit 添加 更多种类的系统扩展 和更多的设备系列
反过来这些类型的 内核扩展也将被弃用
所以这是对系统扩展的简要介绍
它们通过在用户空间中运行 来避免内核编程的困难 这使得你的 App 能够 以更易于开发和 调试的方式扩展系统 从而保护用户数据的 安全性和可靠性 现在我想把它交给 Simon 他将向你展示如何使用新的 DriverKit Runtime 构建驱动扩展 谢谢 Joe 正如 Joe 刚才所说 驱动扩展是一种 新型的系统扩展 它控制硬件设备并使其服务 在整个 OS 中可用
我们将这些新的 驱动扩展称为 Dext
我们的目标是 尽可能简化 从内核扩展到驱动扩展的过渡
为了向你展示 驱动扩展是如何工作的 以及如何构建自己的扩展或从 Kext 过渡 我们将讨论四件事
我们将讨论它们的 生命周期 它们如何匹配和启动
以及它们如何与 Kext 竞争 我们将讨论如何 使用新的 DriverKit SDK 构建它们 我们将讨论一些安全特性 比如权限 最后我们来讨论有关 兼容性问题 如何将 App 部署到 macOS Catalina 和 Mojave 上
首先让我们来讨论 Kext 的生命周期
我们看看当具有驱动扩展的设备出现时 发生的过程 我们从 IOKit 匹配开始 创建一个代表 你服务的内核服务 这是 Apple 编写的 然后我们启动一个托管驱动的进程 并对你的 DriverKit 类 进行实例化 并且该进程同时也包含 其使用的任何服务的代理对象 比如它的提供者
此设备正在使用 USB 设备或设备 这个设备使用 USB 设备 所以它有一个用于调用的代理对象 代表内核设备 这意味着 DriverKit 驱动程序与 内核扩展一样 可以竞争内核驱动的匹配 你可以使用像 IOReg 等工具 在注册表中查看它们 并且可以将 IOKit 框架 API 与它们一起使用 由于 DriverKit 驱动程序 与内核相互分离 因此另一个设备将 拥有自己的进程 和另一个驱动实例 在 macOS Catalina 中 Apple 已经 开始将自己的几个驱动迁移到驱动扩展中
在这里你可以看到 一个托管 USB 网络设备的进程 该设备在注册表中 作为普通以太网设备 对 OS 的其余部分可见 你还可以在注册表中 看到托管 HID 和串行驱动程序的其他进程 现在我们将讨论如何 使用 DriverKit SDK 构建驱动扩展
我们想让构建驱动扩展 对于目前正在构建 Kext 的人来说 是一个简单的过渡 所以我们从你熟悉的 IOKit C++ API 开始
DriverKit API 是 IOKit API 在用户空间的扩展 我们将它们收集到一个 新的 DriverKit SDK 中 它独立于 macOS SDK 此 SDK 有一个用于可靠性 和安全性的有限 API 外表 并且没有对文件系统 网络或模拟消息的直接访问 这使得 Apple 能够根据 正在运行的驱动程序 调整用户空间进程 并赋予其更高的优先级 和更强的功能 让我们来谈谈 DriverKit SDK 中的一些类 首先 IOService 类 存在于 DriverKit 中 与 IOKit 类非常相似 还有 IOMemoryDescriptor 和 IOBufferMemoryDescriptor 类 与 IOKit 非常相似 IOKit 中的 IOWorkLoop 和 EventSource 类 也有对应的替代 最后还有一个名为 OSAction 的新类 表示 C 函数指针
那让我们来看看 仔细看看其中的一些类 IOService 类具有 IOKit 的 生命周期 API 如开始 暂停和终止
对于同步 每个 IOService 都有一个 默认的 DispatchQueue 所有的方法都在一个队列上调用 包括中断 定时器 以及完成 IODispatchQueue 是基于 Grand Central Dispatch 代码之上的 是针对在受限 DriverKit 环境中运行 而优化的特殊版本
驱动可以控制其他队列 并知道调用哪些方法 在哪些队列上进行高级使用 事件 API 类似于 IOKit 中的 IOWorkLoop 模型 但现在基于 Grand Central Dispatch API 其中包含用于中断和计时器的 队列和调度源
块 API 替代了 IOCommandGate GCD 提供了易于使用的同步参数 这些参数你可能非常熟悉
还有一个 IOSharedDataQueueDispatchSource 类 它提供了一个 共享内存缓冲区 用于快速低开销的消息传递 我们将讨论的最后一个类 是 OSAction 它封装了 IOKit API 的回调 它们总是异步的 并且私有地保存回调 客户端的状态 它们允许以 任意方式定义回调参数 和在编译和运行时中的类型检查 所以 DriverKit 中 有一些类 让我们看看 如何定义这些与 IOKit 略有不同的类
DriverKit 接口使用 扩展名为 .iig 的新文件类型进行描述 并且它们由名为 IIG 的工具处理
IIG 文件是一个 编译的类定义 可以导入常见类型 和结构的的 C 和 C++ 头文件 但是它的类和方法的定义中 确实有额外的属性 允许使用它 用于从单独的 地址空间调用 在这里你可以看到一个基本的类定义 它看起来很普通 但是有一些额外的属性 比如类中的 KERNEL 这意味着类是在内核中定义的
以及一些方法声明中的 LOCAL 这意味着 方法是在用户驱动中实现的 今天 macOS Catalina 开发人员预览版中 提供的一些系列包括 是用于创建网络接口的 NetworkingDriverKit
用于创建 HID 设备的 HIDDriverKit
用于为操作系统提供 USB 串行设备的 USBSerialDriverKit 以及用于在驱动程序中 使用 USB 设备提供程序的 USBDriverKit
Scott 一会儿 会演示 USBDriverKit 对 USB 设备的支持 现在我们将讨论 开发驱动扩展的 一些安全性方面 你的驱动扩展 需要获得一些类型的授权
有些授权是所有驱动扩展 都必须获得的 还有一种被称为 传输授权 是特定于某类设备的 还有一个系列的授权 是向操作系统提供服务所需的权限 Joe 稍后将在本次会议中 讨论代码签名 和获取这些授权的批准过程 现在我们将快速了解部署 适用于所有 macOS 版本的产品
为 macOS Catalina 和较早的版本 部署产品需要 为旧版本安装内核扩展 但要在 macOS Catalina 上 使用系统扩展框架并 提供驱动扩展 现在 Scott 将演示 如何使用新的 USBDriverKit 框架
谢谢 Scott 谢谢 Simon 那么今天我将向你展示如何 构建一个简单的 USB 驱动 该驱动使用新的 USBDriverKit 框架 从中断端点读取数据 我们将简要介绍 如何使用 DriverKit 模板 在 Xcode 中创建一个新项目 之后我们将看一个 内核类和 DriverKit 类的对比 然后接下来我将 详细介绍 DriverKit 中的实现 最后我将简要
使用 LLDB 调试 Dext 使用 Xcode 创建一个新的 DriverKit 项目 非常简单 只要在 Xcode 的 新项目工作流程中选择合适的模板 完成后 Xcode 将 自动生成一些文件以帮助 你开始生成的项目包括 成功构建所需的 标准文件 与内核扩展一样 该项目包括 C++ 实现 权限和 info.pist
除此之外 Xcode 还生成了 Simon 之前提到的 IIG 文件 这个文件包括含 驱动的类定义
因此让我们看一下 MyUserUSBInterfaceDriver 的类定义 你可以看到它看起来 与内核驱动非常相似 例如相同的公共 IOKit 生命周期方法 Start 和 Stop 现有的 DriverKit 首字母将被大写
尽管如此还是有一些 细微但重要的区别
首先 DriverKit 类 需要一个带有 附加属性的不同回调 此属性指示 此方法符合 IOUSBHostPipe 对象定义的 回调类型 并强制执行编译时类型检查 第二 DriverKit 类中 没有声明实例变量 这是因为在初始化期间 必须由驱动 分配所有实例变量 让我们来看看 MyUserUSBInterfaceDriver 是如何完成的
首先你需要 声明一个结构来保存 所有实例变量 以前属于内核类的 所有实例变量都应该是 此结构的一部分 对于这个类我们有指向 与 Kext 相同的 USB 内核类型的指针 例如执行 IO 的 IOUSBHostInterface 提供者 和 IOUSBHostPipe 对象 还有一个 OSAction 对象 用于封装异步 IO 的回调 然后你只需在 初始化例程期间分配结构 这是 MyUserUSBInterfaceDriver 的初始化例程 它以与 Kext 相同的方式 对父类调用 init 然后它分配了实例变量结构
应该注意的是 父类定义了一个实例变量成员 该成员必须赋值为 分配的结果
接下来我们将看一下 Start 的实现 Start 的这一部分 负责调用父类并验证提供者
这里的内容 与内核实现略有不同
你可以看到定义 包含在宏 IMPL 中 并且该宏需要 支持用户进程 和内核代理对象之间的 IPC 通信
而且你也可以看到 调用父类的 Start 采取了不同的形式 接下来使用 USBDriverKit API 打开 IOUSB 主机接口 并分配管道对象
然后分配一个 用于 IO 的内存描述
这应该是一个 相当熟悉的范例 基本上与 Kext 中的相同
在这种情况下 我们正在执行异步 IO 所以我们需要分配一个 OSAction 对象 来封装回调
然后最后剩下的 就是将 IO 排入队列
此时假设 设置成功 则会 进行异步读取完成后我们 将调用 ReadComplete 此驱动的 ReadComplete 方法 将打印传输的字节数和状态 如果成功的话 它会重新排列 IO 队列 接下来我们来看看 运行中的驱动 因此在本演示中 你将看到我已添加到 MyUserUSBInterfaceDriver 的一些日志记录用来打印生命周期方法
我还添加了一个无限循环 我们将使用 LDB 进行调试 然后我还制造了一个崩溃 我们可以看到现在如何 使用新的 DriverKit 框架 进行恢复 因此如果我插入设备 你可以看到初始化开始运行 就像在 Kext 一样 当数据向设备转移时 ReadComplete 被调用 使用 PS 我们可以看到 驱动正在运行 现在我们已经到达了 我添加的无限循环 我们可以用 LDB 看看 在驱动中发生了什么
从前面 我们看到 PID 是 2572 我们需要连接到那个进程
我们需要找到运行 ReadComplete 方法的线程
可以看到是线程 2
这里我们遇到了 一个无限循环 因为我们在用户空间运行 我们可以修改我们的循环变量
在我继续之前 如果你仔细观察 你会发现肯定有一个 空指针引用 会使驱动崩溃 你可以看到它已经崩溃了
但是在不影响系统其他部分的情况下 它会立即重启 然后在拔下设备时 你可以看到你的 Stop 和 Free 方法 将正常运行
所以 -- 使用新的 DriverKit 框架构建 和调试新驱动程序是多么容易 现在我将把这里交给 Joe 来讨论如何在 你的 App 中提供系统扩展
谢谢 Scott
现在我们已经了解如何 构建驱动扩展 我想告诉你们如何在 App 中部署驱动 或其他类型的系统扩展
我们将讨论扩展与 App 之间的关系
以及如何构建 和打包扩展包 我们将讨论 代码签名和权限 以及如何安装 更新和卸载 系统扩展 系统扩展始终是 App 的一部分 这是设计的基本原则 没有独立的系统扩展
这是因为用户 考虑的是 App 他们购买 App 安装并运行 App 你的系统扩展应该是 你的 App 的实施细节
该 App 是用户 与你的扩展程序进行交互和控制的方式 一旦你将系统扩展 打包到一个 App 中 你就可以使用 开发人员 ID 或 通过 Mac App Store 直接将 App 分发给用户 这是内核扩展不可能实现的
由于你的 App 与其扩展之间 存在密切关系 因此用户可以识别 你的系统扩展与 App 有关 你应该使用扩展中的 info.plist 中的 CFBundleDisplayName 键 为其提供良好的本地化名称 并为其提供 App 主图标相关的自定义图标 这样如果扩展 将在用户界面中显示 用户会将其识别为 他们使用的 App 的一部分
你还应该在 扩展的 info.pList 中 包含一个用法说明字符串 该字符串解释了 扩展的功能和用途 以及用户运行它的原因 可以将其看作 类似于日历或 相机访问所需的使用情况
对于驱动扩展 请使用 OSBundleUsageDescription 键 对于其他类型的系统扩展 使用 NSSystemExtensionUsageDescription
请记住为你的 App 支持的所有语言本地化这些字符串 和所有其他字符串 系统扩展本身 是 App 的一个独立子包 包含自己的可执行文件 和 info.plist
下面是一个示例 App 的内部视图 它在其 Contents/Library/SystemExtensions 目录下 展示了一个系统扩展
驱动扩展捆绑包 使用 .dext 文件名后缀 和包类型 DEXT 它们在 info.plist 中使用 OSBundle 键 和内核扩展包类似
驱动扩展包 应该是扁平的 没有 Contents 文件夹 类似于 iOS App
其他类型的 系统扩展包使用 .system 扩展名文件名后缀 和 CFBundlePackageType SYSX 代表 System Extension 在 Xcode 中系统扩展 是一个单独的 Target Xcode 内置了用于 网络扩展和驱动套件驱动的模版
当你创建这样的 Target 时 Xcode 会询问你是否 要将其嵌入已经 属于项目的 App 中 如果这样做 它将创建一个 Copy Files 面板 将扩展构建产品复制到 你的 App 中 一旦构建了系统扩展 就可以使用与 App 签名 相同的证书对其进行签名 不再需要特殊的 Kext 证书 即 Kext 特定的签名证书了
通常用于签署系统扩展 和主 App 的团队 ID 必须匹配 这是一项安全措施 但是可能 你正在构建的扩展 将被打包到其他开发人员的 App 中 例如 有许多产品中 都包含的常见 USB 串行接口芯片的驱动扩展 如果是这样 你可以在系统扩展上使用授权 以允许它打包在 不同的开发人员的 App 中
如果你使用 开发人员 ID 对系统扩展进行签名 则必须先对进行真实性检验 才能在用户系统上运行
有关真实性检验的更多信息 请观看去年的会议 或今天下午晚些时候来 真实性检验实验室
系统扩展使用权限 向操作系统描述其功能 它是什么类型的扩展 以及它可以做什么 例如 DriverKit 驱动扩展 使用了 Simon 展示的 交通和家庭权限
com.apple.developer.system-extension.install 权限
有关权限的 更多信息以及请求 开发团队使用它们 请访问 developer.apple.com/system-extensions 在 Catalina 的开发者种子中 进行本地开发时 你可以关闭系统完整性保护 以在测试时禁用 某些代码签名 和权限检查
请记得在 完成测试后再次打开 系统完整性保护 现在你已经使用系统扩展 构建了 App 如何在用户的系统上 安装扩展 不需要安装器或包 你的系统扩展 保留在你的 App 包中 你的 App 使用新的 系统扩展框架 并创建激活请求 以请求扩展 可供系统使用 系统管理员 将批准请求 大多数 App 应该在 App 启动期间创建 activationRequest 以便立即提供扩展 如果你的扩展 已被激活并获得批准 则 activationRequest 将 快速返回 success 但是你可能希望 在 App 生命周期的 不同时刻 激活你的系统扩展 例如在用户同意许可协议 或进行 App 内购买后 如果需要的话
一旦你的扩展被激活 系统将管理它的生命周期 并在需要时启动它 例如当连接到匹配的硬件设备时 驱动扩展将启动
要更新系统扩展名 请更新 App 包
用户可以安装他们 从你的网站下载的新版本 你的自动更新器可以 立即更新 App 包 或者如果你在 App Store 上发布了一个新版本 它将为用户更新 下次运行 App 并提交激活请求时 系统将注意到 扩展的版本已经更改 它将要求你的 激活请求代理按照 你自己的版本号规则 比较版本号
如果你的代理 确定这是一次升级 系统将停止旧版本的系统扩展 并启动新版本 如果用户希望卸载你的 App 当他们将其移到 废纸篓中时 它的所有扩展也将 自动停用
如果你希望使用 也有一个 deactivationRequest API 但不需要专用的 扩展卸载程序
因此今天我们引入了系统扩展 它是内核扩展的替代 使你的 App 能够以比以往更安全 更易于开发的方式扩展系统
我们看到了如何使用 DriverKit SDK 和框架 这是一个重大的 IOKit 更新 来构建驱动程序 我们看到了如何在一台机器上 编写并调试 示例的 USB 驱动
最后我们讨论了 如何在 App 中包含系统扩展
如果你有任何问题 我们很乐意在 Core OS 实验室 为你解答 今天晚些时候还有周四早上 都有实验室
你可能还想访问 正在进行的安全实验室 或者周四下午 和周五上午访问网络实验室 非常感谢 请继续观看接下来的 WWDC [掌声]
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。