macOS IOKit

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

基本信息

I/O Kit 是一个开源的面向对象的 设备驱动框架,位于 XNU 内核中,处理 动态加载的设备驱动程序。它允许在内核中动态添加模块化代码,支持多种硬件。

IOKit 驱动程序基本上会 从内核导出函数。这些函数参数的 类型预定义的 并经过验证。此外,类似于 XPC,IOKit 只是 Mach 消息 之上的另一层。

IOKit XNU 内核代码 由 Apple 在 https://github.com/apple-oss-distributions/xnu/tree/main/iokit 开源。此外,用户空间的 IOKit 组件也开源 https://github.com/opensource-apple/IOKitUser

然而,没有 IOKit 驱动程序 是开源的。无论如何,偶尔会发布带有符号的驱动程序,这使得调试更容易。查看如何 从固件获取驱动程序扩展

它是用 C++ 编写的。您可以使用以下命令获取去除修饰的 C++ 符号:

# Get demangled symbols
nm -C com.apple.driver.AppleJPEGDriver

# Demangled symbols from stdin
c++filt
__ZN16IOUserClient202222dispatchExternalMethodEjP31IOExternalMethodArgumentsOpaquePK28IOExternalMethodDispatch2022mP8OSObjectPv
IOUserClient2022::dispatchExternalMethod(unsigned int, IOExternalMethodArgumentsOpaque*, IOExternalMethodDispatch2022 const*, unsigned long, OSObject*, void*)

IOKit 暴露的函数 在客户端尝试调用函数时可以执行 额外的安全检查,但请注意,应用程序通常受到 沙箱限制,只能与特定的 IOKit 函数进行交互。

驱动程序

在 macOS 中,它们位于:

  • /System/Library/Extensions

  • 内置于 OS X 操作系统的 KEXT 文件。

  • /Library/Extensions

  • 由第三方软件安装的 KEXT 文件

在 iOS 中,它们位于:

  • /System/Library/Extensions

#Use kextstat to print the loaded drivers
kextstat
Executing: /usr/bin/kmutil showloaded
No variant specified, falling back to release
Index Refs Address            Size       Wired      Name (Version) UUID <Linked Against>
1  142 0                  0          0          com.apple.kpi.bsd (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
2   11 0                  0          0          com.apple.kpi.dsep (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
3  170 0                  0          0          com.apple.kpi.iokit (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
4    0 0                  0          0          com.apple.kpi.kasan (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
5  175 0                  0          0          com.apple.kpi.libkern (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
6  154 0                  0          0          com.apple.kpi.mach (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
7   88 0                  0          0          com.apple.kpi.private (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
8  106 0                  0          0          com.apple.kpi.unsupported (20.5.0) 52A1E876-863E-38E3-AC80-09BBAB13B752 <>
9    2 0xffffff8003317000 0xe000     0xe000     com.apple.kec.Libm (1) 6C1342CC-1D74-3D0F-BC43-97D5AD38200A <5>
10   12 0xffffff8003544000 0x92000    0x92000    com.apple.kec.corecrypto (11.1) F5F1255F-6552-3CF4-A9DB-D60EFDEB4A9A <8 7 6 5 3 1>

直到第9个,列出的驱动程序是加载在地址0。这意味着这些不是实际的驱动程序,而是内核的一部分,无法卸载

为了找到特定的扩展,您可以使用:

kextfind -bundle-id com.apple.iokit.IOReportFamily #Search by full bundle-id
kextfind -bundle-id -substring IOR #Search by substring in bundle-id

要加载和卸载内核扩展,请执行:

kextload com.apple.iokit.IOReportFamily
kextunload com.apple.iokit.IOReportFamily

IORegistry

IORegistry 是 macOS 和 iOS 中 IOKit 框架的一个关键部分,作为表示系统硬件配置和状态的数据库。它是一个 层次化的对象集合,代表系统上加载的所有硬件和驱动程序 及其相互关系。

您可以使用 cli ioreg 从控制台检查 IORegistry(对 iOS 特别有用)。

ioreg -l #List all
ioreg -w 0 #Not cut lines
ioreg -p <plane> #Check other plane

您可以从 Xcode 附加工具 下载 IORegistryExplorer,网址为 https://developer.apple.com/download/all/,并通过 图形 界面检查 macOS IORegistry

在 IORegistryExplorer 中,“平面”用于组织和显示 IORegistry 中不同对象之间的关系。每个平面代表特定类型的关系或系统硬件和驱动程序配置的特定视图。以下是您可能在 IORegistryExplorer 中遇到的一些常见平面:

  1. IOService 平面:这是最通用的平面,显示代表驱动程序和 nubs(驱动程序之间的通信通道)的服务对象。它显示这些对象之间的提供者-客户端关系。

  2. IODeviceTree 平面:该平面表示设备与系统之间的物理连接。它通常用于可视化通过 USB 或 PCI 等总线连接的设备层次结构。

  3. IOPower 平面:以电源管理的方式显示对象及其关系。它可以显示哪些对象影响其他对象的电源状态,便于调试与电源相关的问题。

  4. IOUSB 平面:专注于 USB 设备及其关系,显示 USB 集线器和连接设备的层次结构。

  5. IOAudio 平面:该平面用于表示音频设备及其在系统中的关系。

  6. ...

驱动程序通信代码示例

以下代码连接到 IOKit 服务 "YourServiceNameHere" 并调用选择器 0 内的函数。为此:

  • 首先调用 IOServiceMatchingIOServiceGetMatchingServices 来获取服务。

  • 然后通过调用 IOServiceOpen 建立连接。

  • 最后调用 IOConnectCallScalarMethod 函数,指示选择器 0(选择器是您要调用的函数分配的数字)。

#import <Foundation/Foundation.h>
#import <IOKit/IOKitLib.h>

int main(int argc, const char * argv[]) {
@autoreleasepool {
// Get a reference to the service using its name
CFMutableDictionaryRef matchingDict = IOServiceMatching("YourServiceNameHere");
if (matchingDict == NULL) {
NSLog(@"Failed to create matching dictionary");
return -1;
}

// Obtain an iterator over all matching services
io_iterator_t iter;
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
if (kr != KERN_SUCCESS) {
NSLog(@"Failed to get matching services");
return -1;
}

// Get a reference to the first service (assuming it exists)
io_service_t service = IOIteratorNext(iter);
if (!service) {
NSLog(@"No matching service found");
IOObjectRelease(iter);
return -1;
}

// Open a connection to the service
io_connect_t connect;
kr = IOServiceOpen(service, mach_task_self(), 0, &connect);
if (kr != KERN_SUCCESS) {
NSLog(@"Failed to open service");
IOObjectRelease(service);
IOObjectRelease(iter);
return -1;
}

// Call a method on the service
// Assume the method has a selector of 0, and takes no arguments
kr = IOConnectCallScalarMethod(connect, 0, NULL, 0, NULL, NULL);
if (kr != KERN_SUCCESS) {
NSLog(@"Failed to call method");
}

// Cleanup
IOServiceClose(connect);
IOObjectRelease(service);
IOObjectRelease(iter);
}
return 0;
}

There are 其他 functions that can be used to call IOKit functions apart of IOConnectCallScalarMethod like IOConnectCallMethod, IOConnectCallStructMethod...

反向工程驱动入口点

You could obtain these for example from a 固件镜像 (ipsw). Then, load it into your favourite decompiler.

You could start decompiling the externalMethod function as this is the driver function that will be receiving the call and calling the correct function:

That awful call demagled means:

IOUserClient2022::dispatchExternalMethod(unsigned int, IOExternalMethodArgumentsOpaque*, IOExternalMethodDispatch2022 const*, unsigned long, OSObject*, void*)

注意在之前的定义中缺少了 self 参数,正确的定义应该是:

IOUserClient2022::dispatchExternalMethod(self, unsigned int, IOExternalMethodArgumentsOpaque*, IOExternalMethodDispatch2022 const*, unsigned long, OSObject*, void*)

实际上,您可以在 https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/iokit/Kernel/IOUserClient.cpp#L6388 找到真实的定义:

IOUserClient2022::dispatchExternalMethod(uint32_t selector, IOExternalMethodArgumentsOpaque *arguments,
const IOExternalMethodDispatch2022 dispatchArray[], size_t dispatchArrayCount,
OSObject * target, void * reference)

使用此信息,您可以重写 Ctrl+Right -> Edit function signature 并设置已知类型:

新的反编译代码将如下所示:

在下一步中,我们需要定义 IOExternalMethodDispatch2022 结构体。它是开源的,您可以在 https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/iokit/IOKit/IOUserClient.h#L168-L176 中找到,您可以定义它:

现在,跟随 (IOExternalMethodDispatch2022 *)&sIOExternalMethodArray,您可以看到很多数据:

将数据类型更改为 IOExternalMethodDispatch2022:

更改后:

正如我们现在所看到的,这里有一个 7 个元素的数组(检查最终的反编译代码),点击以创建一个 7 个元素的数组:

数组创建后,您可以看到所有导出的函数:

如果您记得,要从用户空间 调用 一个 导出 函数,我们不需要调用函数的名称,而是 选择器编号。在这里,您可以看到选择器 0 是函数 initializeDecoder,选择器 1startDecoder,选择器 2initializeEncoder...

学习与实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE) 学习与实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks

Last updated