macOS IOKit
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Basic Information
The I/O Kit is an open-source, object-oriented device-driver framework in the XNU kernel, handles dynamically loaded device drivers. It allows modular code to be added to the kernel on-the-fly, supporting diverse hardware.
IOKit drivers will basically export functions from the kernel. These function parameter types are predefined and are verified. Moreover, similar to XPC, IOKit is just another layer on top of Mach messages.
IOKit XNU kernel code is opensourced by Apple in https://github.com/apple-oss-distributions/xnu/tree/main/iokit. Moreover, the user space IOKit components are also opensource https://github.com/opensource-apple/IOKitUser.
However, no IOKit drivers are opensource. Anyway, from time to time a release of a driver might come with symbols that makes it easier to debug it. Check how to get the driver extensions from the firmware here.
It's written in C++. You can get demangled C++ symbols with:
IOKit exposed functions could perform additional security checks when a client tries to call a function but note that the apps are usually limited by the sandbox to which IOKit functions they can interact with.
Drivers
In macOS they are located in:
/System/Library/Extensions
KEXT files built into the OS X operating system.
/Library/Extensions
KEXT files installed by 3rd party software
In iOS they are located in:
/System/Library/Extensions
Until the number 9 the listed drivers are loaded in the address 0. This means that those aren't real drivers but part of the kernel and they cannot be unloaded.
In order to find specific extensions you can use:
To load and unload kernel extensions do:
IORegistry
The IORegistry is a crucial part of the IOKit framework in macOS and iOS which serves as a database for representing the system's hardware configuration and state. It's a hierarchical collection of objects that represent all the hardware and drivers loaded on the system, and their relationships to each other.
You can get the IORegistry using the cli ioreg
to inspect it from the console (specially useful for iOS).
You could download IORegistryExplorer
from Xcode Additional Tools from https://developer.apple.com/download/all/ and inspect the macOS IORegistry through a graphical interface.
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252FPq3VYU8uUd0iuPA6UznM%252Fimage.png%3Falt%3Dmedia%26token%3Dec905471-88ed-47b5-9304-bce167a907e1&width=768&dpr=4&quality=100&sign=a1c488c0&sv=1)
In IORegistryExplorer, "planes" are used to organize and display the relationships between different objects in the IORegistry. Each plane represents a specific type of relationship or a particular view of the system's hardware and driver configuration. Here are some of the common planes you might encounter in IORegistryExplorer:
IOService Plane: This is the most general plane, displaying the service objects that represent drivers and nubs (communication channels between drivers). It shows the provider-client relationships between these objects.
IODeviceTree Plane: This plane represents the physical connections between devices as they are attached to the system. It is often used to visualize the hierarchy of devices connected via buses like USB or PCI.
IOPower Plane: Displays objects and their relationships in terms of power management. It can show which objects are affecting the power state of others, useful for debugging power-related issues.
IOUSB Plane: Specifically focused on USB devices and their relationships, showing the hierarchy of USB hubs and connected devices.
IOAudio Plane: This plane is for representing audio devices and their relationships within the system.
...
Driver Comm Code Example
The following code connects to the IOKit service "YourServiceNameHere"
and calls the function inside the selector 0. For it:
it first calls
IOServiceMatching
andIOServiceGetMatchingServices
to get the service.It then establish a connection calling
IOServiceOpen
.And it finally calls a function with
IOConnectCallScalarMethod
indicating the selector 0 (the selector is the number the function you want to call has assigned).
There are other functions that can be used to call IOKit functions apart of IOConnectCallScalarMethod
like IOConnectCallMethod
, IOConnectCallStructMethod
...
Reversing driver entrypoint
You could obtain these for example from a firmware image (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:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252FwIAtCM7P8Ju0ysZKiiqt%252Fimage.png%3Falt%3Dmedia%26token%3D7ddee35d-7b7c-49e2-b8a1-b757e45f4836&width=768&dpr=4&quality=100&sign=7b0901dc&sv=1)
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252FGFvxMTVzk6xYhEd0XWRv%252Fimage.png%3Falt%3Dmedia%26token%3D96c8500e-a62e-43a5-bdae-9d0640d1a8dc&width=768&dpr=4&quality=100&sign=87436a6a&sv=1)
That awful call demagled means:
Note how in the previous definition the self
param is missed, the good definition would be:
Actually, you can find the real definition in https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/iokit/Kernel/IOUserClient.cpp#L6388:
With this info you can rewrite Ctrl+Right -> Edit function signature
and set the known types:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252FHIVt9HlnLA04MzjRmlEx%252Fimage.png%3Falt%3Dmedia%26token%3D48444d25-d810-4cad-aea6-3ff3dacaffcc&width=768&dpr=4&quality=100&sign=4e39d445&sv=1)
The new decompiled code will look like:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252Fxj9emWA535XnxD84F2aK%252Fimage.png%3Falt%3Dmedia%26token%3D64df1086-7009-4456-89dc-5c014c086a3d&width=768&dpr=4&quality=100&sign=98d2daaf&sv=1)
For the next step we need to have defined the IOExternalMethodDispatch2022
struct. It's opensource in https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/iokit/IOKit/IOUserClient.h#L168-L176, you could define it:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252FYtBlH8UPH2qgF5D8VSwI%252Fimage.png%3Falt%3Dmedia%26token%3Dfb4c2fdd-fbe4-4ed6-83a7-ca7522ac183c&width=768&dpr=4&quality=100&sign=bd14f34a&sv=1)
Now, following the (IOExternalMethodDispatch2022 *)&sIOExternalMethodArray
you can see a lot of data:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252FBlcXz052QnQiHqaPXp57%252Fimage.png%3Falt%3Dmedia%26token%3D6a8e1c62-bdd3-4ddd-b474-cc126f93170f&width=768&dpr=4&quality=100&sign=2d03daeb&sv=1)
Change the Data Type to IOExternalMethodDispatch2022:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252FIhoitgpxFQTcxkjqY6ec%252Fimage.png%3Falt%3Dmedia%26token%3De6ffaea7-5557-4a2d-81ae-ce22a0eece80&width=768&dpr=4&quality=100&sign=7fd910a0&sv=1)
after the change:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252FdiGatBZo7KX3550oK9Ej%252Fimage.png%3Falt%3Dmedia%26token%3D077be647-3cf6-42ec-b795-e84f89b74656&width=768&dpr=4&quality=100&sign=caa32b41&sv=1)
And as we now in there we have an array of 7 elements (check the final decompiled code), click to create an array of 7 elements:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252FBul0XJ4G56SZuHdLjVad%252Fimage.png%3Falt%3Dmedia%26token%3Dd6e9d3d3-14ce-451c-b00b-fe676c5cb965&width=768&dpr=4&quality=100&sign=edec5a22&sv=1)
After the array is created you can see all the exported functions:
![](https://book.hacktricks.xyz/~gitbook/image?url=https%3A%2F%2F129538173-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-L_2uGJGU7AVNRcqRvEi%252Fuploads%252Fxuye1g7RZ6KEyay4bbmB%252Fimage.png%3Falt%3Dmedia%26token%3D4a2d6bf4-2745-4d90-8a64-a8c3c6383e3f&width=768&dpr=4&quality=100&sign=8349fd4e&sv=1)
If you remember, to call an exported function from user space we don't need to call the name of the function, but the selector number. Here you can see that the selector 0 is the function initializeDecoder
, the selector 1 is startDecoder
, the selector 2 initializeEncoder
...
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Last updated