IOKit drivers là attack surface lớn nhất cho iOS kernel exploitation. Mỗi driver expose external methods qua IOUserClient mà userspace có thể gọi — tương tự ioctl() trên Linux.


IOKit Architecture

Driver Hierarchy

IOService (abstract base)
  └── IORegistryEntry (device tree node)
       └── Concrete driver class (e.g., IOSurface)
            └── IOUserClient subclass (e.g., IOSurfaceRootUserClient)
                 ↑
                 Userspace calls IOConnectCallMethod() vào đây

IOUserClient — The Interface

IOUserClient là bridge giữa userspace và kernel driver:

// Kernel side
class IOSurfaceRootUserClient : public IOUserClient {
    // External method dispatch table
    static const IOExternalMethodDispatch sMethods[] = {
        // selector 0
        { &IOSurfaceRootUserClient::s_create_surface,
          0,                    // scalar input count
          kIOUCVariableStructureSize, // struct input size
          0,                    // scalar output count
          0 },                  // struct output size

        // selector 1
        { &IOSurfaceRootUserClient::s_release_surface,
          1, 0, 0, 0 },

        // ... more methods
    };

    IOReturn externalMethod(uint32_t selector,
        IOExternalMethodArguments *args,
        IOExternalMethodDispatch *dispatch,
        OSObject *target,
        void *reference) override;
};

// Userspace side
io_connect_t conn;
IOServiceOpen(service, mach_task_self(), 0, &conn);
IOConnectCallMethod(conn,
    selector,           // Method index
    scalarInput, scalarInputCount,
    structInput, structInputSize,
    scalarOutput, &scalarOutputCount,
    structOutput, &structOutputSize);

External Method Arguments

struct IOExternalMethodArguments {
    uint32_t    version;
    uint32_t    selector;       // Method selector

    // Scalar (register-like) arguments
    const uint64_t *scalarInput;
    uint32_t    scalarInputCount;
    uint64_t   *scalarOutput;
    uint32_t    scalarOutputCount;

    // Struct (buffer) arguments
    const void *structureInput;
    uint32_t    structureInputSize;
    void       *structureOutput;
    uint32_t    structureOutputSize;

    // Memory descriptor
    IOMemoryDescriptor *structureInputDescriptor;
    IOMemoryDescriptor *structureOutputDescriptor;
};

High-Value Targets

IOSurface / IOSurfaceRootUserClient

  • Vai trò: Quản lý shared memory surfaces cho graphics
  • Tại sao quan trọng: Reachable từ app sandbox, complex functionality
  • Exploitation history: Nhiều CVEs, dùng trong hầu hết modern exploit chains
  • Key methods: create_surface, set_value, get_value, release_surface
  • iOS 16+: IOSurfaceRootUserClient bị restricted (duplicate table với fewer methods)

IOSurface kread/kwrite pattern:

1. Create IOSurface
2. Set serialized property values (controlled data in kernel)
3. Trigger vulnerability → overwrite IOSurface internal pointer
4. Get property value → reads from corrupted pointer address (kread)
5. Set property value → writes to corrupted pointer address (kwrite)

AGXAccelerator / IOGPU

  • Vai trò: GPU driver
  • Attack surface: Command buffer parsing, shader compilation
  • iOS 16+: IOGPUDeviceUserClient restricted

AppleAVE2 / AppleAVE2UserClient

  • Vai trò: Video encoding hardware
  • Đặc điểm: iOS-only, symbols stripped → harder to analyze
  • Exploitation: Accessible trước iOS 16 restrictions

AppleSPU

  • Vai trò: Signal Processing Unit
  • Newer target: Less analyzed, potentially less hardened

Reverse Engineering IOKit Drivers

1. Find UserClient Classes

# Từ kernelcache trong IDA/Ghidra:
# Search for strings containing "UserClient"
# Hoặc search cho ::externalMethod references

# Dùng jtool2
jtool2 -d __DATA.__objc_classlist kernelcache | grep UserClient

2. Find External Method Table

Trong IDA:
1. Find class's externalMethod() implementation
2. Look for dispatch table reference (usually in __DATA_CONST.__const)
3. Table entries are IOExternalMethodDispatch structs:
   struct IOExternalMethodDispatch {
       IOExternalMethodAction function;    // 8 bytes (function pointer)
       uint32_t checkScalarInputCount;     // 4 bytes
       uint32_t checkStructureInputSize;   // 4 bytes
       uint32_t checkScalarOutputCount;    // 4 bytes
       uint32_t checkStructureOutputSize;  // 4 bytes
   };

3. Map Method Table

Từ dispatch table, build map:
  Selector 0 → create_surface(0 scalar, var struct, 0 scalar out, 0 struct out)
  Selector 1 → release_surface(1 scalar, 0 struct, 0 scalar out, 0 struct out)
  Selector 2 → ...

Mỗi method = potential attack vector

4. Phrack Methodology


iOS 16+ Restrictions

Apple đã restrict nhiều IOKit UserClients:

Trước iOS 16:
  App sandbox → IOSurfaceRootUserClient (full method table)
  App sandbox → IOGPUDeviceUserClient (full)

Sau iOS 16:
  App sandbox → IOSurfaceRootUserClient_Restricted (limited methods)
  App sandbox → IOGPUDeviceUserClient_Restricted (limited methods)

Restricted UserClients:
  - Duplicate class inheriting from original
  - Separate method dispatch table với fewer methods
  - Sensitive methods (e.g., set_value, get_value) removed

Fuzzing IOKit

Manual Fuzzing

// Brute-force selectors với random input
for (uint32_t sel = 0; sel < 256; sel++) {
    uint64_t scalar[16];
    char structbuf[4096];
    arc4random_buf(scalar, sizeof(scalar));
    arc4random_buf(structbuf, sizeof(structbuf));

    IOConnectCallMethod(conn, sel,
        scalar, 16,
        structbuf, sizeof(structbuf),
        NULL, NULL, NULL, NULL);
}

Intelligent Fuzzing

  • Parse method dispatch table → biết đúng input sizes
  • Dùng Corellium kernel hooks để monitor crashes
  • Coverage-guided fuzzing (phức tạp hơn nhưng hiệu quả)

Vulnerability Patterns Trong IOKit

Pattern Mô tả Prevention
Race condition clientClose() concurrent với externalMethod() Locking, sequencing
UAF Object freed nhưng pointer vẫn dùng Reference counting
Integer overflow Size validation bị bypass Safe integer arithmetic
Missing bounds check Buffer overread/overwrite Input validation
Type confusion Object cast sai type OSDynamicCast() checks
Double free Object freed 2 lần Reference count tracking

Tài Nguyên