macOS IOKit
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Información Básica
El I/O Kit es un framework de controladores de dispositivos de código abierto y orientado a objetos en el núcleo XNU, que maneja controladores de dispositivos cargados dinámicamente. Permite que se agregue código modular al núcleo sobre la marcha, soportando hardware diverso.
Los controladores de IOKit básicamente exportan funciones del núcleo. Estos parámetros de función son predefinidos y son verificados. Además, similar a XPC, IOKit es solo otra capa sobre los mensajes de Mach.
El código del núcleo IOKit XNU es de código abierto por Apple en https://github.com/apple-oss-distributions/xnu/tree/main/iokit. Además, los componentes de IOKit en el espacio de usuario también son de código abierto https://github.com/opensource-apple/IOKitUser.
Sin embargo, ningún controlador de IOKit es de código abierto. De todos modos, de vez en cuando, un lanzamiento de un controlador puede venir con símbolos que facilitan su depuración. Consulta cómo obtener las extensiones del controlador del firmware aquí.
Está escrito en C++. Puedes obtener símbolos C++ demangled con:
# 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*)
Caution
Las funciones expuestas de IOKit podrían realizar verificaciones de seguridad adicionales cuando un cliente intenta llamar a una función, pero ten en cuenta que las aplicaciones suelen estar limitadas por el sandbox con el que pueden interactuar las funciones de IOKit.
Controladores
En macOS se encuentran en:
/System/Library/Extensions- Archivos KEXT integrados en el sistema operativo OS X.
/Library/Extensions- Archivos KEXT instalados por software de terceros
En iOS se encuentran en:
/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>
Hasta el número 9, los controladores listados están cargados en la dirección 0. Esto significa que no son controladores reales, sino parte del núcleo y no se pueden descargar.
Para encontrar extensiones específicas, puedes usar:
kextfind -bundle-id com.apple.iokit.IOReportFamily #Search by full bundle-id
kextfind -bundle-id -substring IOR #Search by substring in bundle-id
Para cargar y descargar extensiones del kernel, haz:
kextload com.apple.iokit.IOReportFamily
kextunload com.apple.iokit.IOReportFamily
IORegistry
El IORegistry es una parte crucial del marco IOKit en macOS e iOS que sirve como una base de datos para representar la configuración y el estado del hardware del sistema. Es una colección jerárquica de objetos que representan todo el hardware y los controladores cargados en el sistema, y sus relaciones entre sí.
Puedes obtener el IORegistry usando el cli ioreg para inspeccionarlo desde la consola (especialmente útil para iOS).
ioreg -l #List all
ioreg -w 0 #Not cut lines
ioreg -p <plane> #Check other plane
Podrías descargar IORegistryExplorer de Xcode Additional Tools desde https://developer.apple.com/download/all/ e inspeccionar el macOS IORegistry a través de una interfaz gráfica.
.png)
En IORegistryExplorer, “planes” se utilizan para organizar y mostrar las relaciones entre diferentes objetos en el IORegistry. Cada plano representa un tipo específico de relación o una vista particular de la configuración de hardware y controladores del sistema. Aquí hay algunos de los planos comunes que podrías encontrar en IORegistryExplorer:
- IOService Plane: Este es el plano más general, que muestra los objetos de servicio que representan controladores y nubs (canales de comunicación entre controladores). Muestra las relaciones proveedor-cliente entre estos objetos.
- IODeviceTree Plane: Este plano representa las conexiones físicas entre dispositivos a medida que están conectados al sistema. A menudo se utiliza para visualizar la jerarquía de dispositivos conectados a través de buses como USB o PCI.
- IOPower Plane: Muestra objetos y sus relaciones en términos de gestión de energía. Puede mostrar qué objetos están afectando el estado de energía de otros, útil para depurar problemas relacionados con la energía.
- IOUSB Plane: Enfocado específicamente en dispositivos USB y sus relaciones, mostrando la jerarquía de hubs USB y dispositivos conectados.
- IOAudio Plane: Este plano es para representar dispositivos de audio y sus relaciones dentro del sistema.
- …
Ejemplo de Código de Comunicación de Controlador
El siguiente código se conecta al servicio de IOKit "YourServiceNameHere" y llama a la función dentro del selector 0. Para ello:
- primero llama a
IOServiceMatchingyIOServiceGetMatchingServicespara obtener el servicio. - Luego establece una conexión llamando a
IOServiceOpen. - Y finalmente llama a una función con
IOConnectCallScalarMethodindicando el selector 0 (el selector es el número que se le ha asignado a la función que deseas llamar).
#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;
}
Hay otras funciones que se pueden usar para llamar a funciones de IOKit además de IOConnectCallScalarMethod como IOConnectCallMethod, IOConnectCallStructMethod…
Invirtiendo el punto de entrada del controlador
Podrías obtener estos, por ejemplo, de una imagen de firmware (ipsw). Luego, cárgalo en tu descompilador favorito.
Podrías comenzar a descompilar la función externalMethod ya que esta es la función del controlador que recibirá la llamada y llamará a la función correcta:
.png)
.png)
Esa horrible llamada desmanglada significa:
IOUserClient2022::dispatchExternalMethod(unsigned int, IOExternalMethodArgumentsOpaque*, IOExternalMethodDispatch2022 const*, unsigned long, OSObject*, void*)
Nota cómo en la definición anterior falta el parámetro self, la buena definición sería:
IOUserClient2022::dispatchExternalMethod(self, unsigned int, IOExternalMethodArgumentsOpaque*, IOExternalMethodDispatch2022 const*, unsigned long, OSObject*, void*)
En realidad, puedes encontrar la definición real en 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)
Con esta información, puedes reescribir Ctrl+Right -> Edit function signature y establecer los tipos conocidos:
.png)
El nuevo código decompilado se verá así:
.png)
Para el siguiente paso, necesitamos tener definida la estructura IOExternalMethodDispatch2022. Es de código abierto en https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/iokit/IOKit/IOUserClient.h#L168-L176, podrías definirlo:
.png)
Ahora, siguiendo el (IOExternalMethodDispatch2022 *)&sIOExternalMethodArray, puedes ver muchos datos:
.png)
Cambia el tipo de dato a IOExternalMethodDispatch2022:
.png)
después del cambio:
.png)
Y como ahora sabemos que hay un array de 7 elementos (verifica el código decompilado final), haz clic para crear un array de 7 elementos:
.png)
Después de que se crea el array, puedes ver todas las funciones exportadas:
.png)
Tip
Si recuerdas, para llamar a una función exportada desde el espacio de usuario, no necesitamos llamar al nombre de la función, sino al número de selector. Aquí puedes ver que el selector 0 es la función
initializeDecoder, el selector 1 esstartDecoder, el selector 2initializeEncoder…
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
HackTricks

