hacktricks/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-iokit.md

14 KiB
Raw Blame History

macOS IOKit

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

基本信息

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

{% hint style="danger" %} IOKit暴露的函数在客户端尝试调用函数时可以执行额外的安全检查但请注意应用程序通常受到沙箱的限制只能与IOKit函数进行交互。 {% endhint %}

驱动程序

在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框架的关键部分它作为一个数据库用于表示系统的硬件配置和状态。它是一个层次化的对象集合,表示系统上加载的所有硬件和驱动程序,以及它们之间的关系

您可以使用命令行工具**ioreg**从控制台检查IORegistry对iOS特别有用

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

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

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

  1. IOService Plane这是最常见的plane显示表示驱动程序和nub驱动程序之间的通信通道的服务对象。它显示这些对象之间的提供者-客户端关系。
  2. IODeviceTree Plane该plane表示设备连接到系统时的物理连接。通常用于可视化通过USB或PCI等总线连接的设备的层次结构。
  3. IOPower Plane:以电源管理方面的对象及其关系显示。它可以显示哪些对象影响其他对象的电源状态,对于调试与电源相关的问题非常有用。
  4. IOUSB Plane专注于USB设备及其关系显示USB集线器和连接设备的层次结构。
  5. IOAudio Plane该plane用于表示系统中的音频设备及其关系。
  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;
}

其他函数可以用来调用IOKit函数除了**IOConnectCallScalarMethod,还有IOConnectCallMethodIOConnectCallStructMethod**...

反向驱动程序入口点

例如,您可以从固件映像ipsw中获取这些函数。然后,将其加载到您喜欢的反编译器中。

您可以从**externalMethod**函数开始反编译,因为这是将接收调用并调用正确函数的驱动程序函数:

那个可怕的调用解码的意思是:

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

{% endcode %}

请注意,在上面的定义中,缺少了**self**参数,正确的定义应该是:

{% code overflow="wrap" %}

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

{% endcode %}

实际上,您可以在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个元素组成的数组

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

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

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥