You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C/Objective-C中获取各类I/O平面地址及段偏移以脱离ioreg分析

Accessing I/O Kit Planes (IODeviceTree, IOService, IOACPIPlane) Without ioreg

Hey there, let's break down how to tackle this problem of accessing macOS I/O Kit planes directly—whether you need memory addresses for dumping or just want to traverse the registry without relying on the ioreg tool. First, a quick key point: direct memory pointers to I/O Kit objects are only accessible in kernel mode (user mode can't touch kernel memory due to macOS's security protections like SIP and ASLR). Let's dive in.

Core Background

I/O Kit planes are logical collections of kernel objects (like IODeviceTree for the device tree, IOService for system services, IOACPIPlane for ACPI objects). To get their memory addresses, you'll need to work within a kernel extension (kext) since user-mode processes are sandboxed from kernel memory.

Kernel Mode: Get Plane Addresses & Dump Content

If you're writing a kernel extension, you can use I/O Kit's internal APIs to grab the root of each plane and get its memory address directly.

1. Get the Root of a Plane

Each plane has a root IORegistryEntry object. Use IORegistryEntry::getRegistryRootEntry() with the plane constant to retrieve it:

#include <IOKit/IORegistryEntry.h>
#include <IOKit/IOKitKeys.h>

// Get IODeviceTree plane root
IORegistryEntry* deviceTreeRoot = IORegistryEntry::getRegistryRootEntry(kIODeviceTreePlane);
if (deviceTreeRoot) {
    // Convert the object pointer to a memory address
    uint64_t planeBaseAddr = reinterpret_cast<uint64_t>(deviceTreeRoot);
    IOLog("IODeviceTree plane root address: 0x%llx\n", planeBaseAddr);
    
    // Don't forget to release the object when done!
    deviceTreeRoot->release();
}

// Get IOService plane root
IORegistryEntry* serviceRoot = IORegistryEntry::getRegistryRootEntry(kIOServicePlane);
if (serviceRoot) {
    uint64_t servicePlaneAddr = reinterpret_cast<uint64_t>(serviceRoot);
    IOLog("IOService plane root address: 0x%llx\n", servicePlaneAddr);
    serviceRoot->release();
}

// Get IOACPIPlane root
IORegistryEntry* acpiRoot = IORegistryEntry::getRegistryRootEntry(kIOACPIPlane);
if (acpiRoot) {
    uint64_t acpiPlaneAddr = reinterpret_cast<uint64_t>(acpiRoot);
    IOLog("IOACPIPlane root address: 0x%llx\n", acpiPlaneAddr);
    acpiRoot->release();
}

2. Traverse & Dump Plane Content

Once you have the root entry, you can recursively traverse its children and properties to build a dump. Here's a snippet to print entries and their properties:

void dumpRegistryEntry(IORegistryEntry* entry, int indentLevel) {
    if (!entry) return;

    // Print entry name and address
    const char* entryName = entry->getName();
    IOLog("%*sEntry: %s @ 0x%llx\n", indentLevel, "", entryName, reinterpret_cast<uint64_t>(entry));

    // Iterate over properties
    OSCollectionIterator* propIter = OSCollectionIterator::withCollection(entry->getProperties());
    if (propIter) {
        OSObject* prop;
        while ((prop = propIter->getNextObject())) {
            const char* propName = prop->getPropertyName();
            IOLog("%*sProperty: %s\n", indentLevel + 2, "", propName);
            
            // Optional: Print property values based on type (OSString, OSNumber, etc.)
            if (OSDynamicCast(OSString, prop)) {
                OSString* strProp = OSDynamicCast(OSString, prop);
                IOLog("%*sValue: %s\n", indentLevel + 4, "", strProp->getCStringNoCopy());
            } else if (OSDynamicCast(OSNumber, prop)) {
                OSNumber* numProp = OSDynamicCast(OSNumber, prop);
                IOLog("%*sValue: %llu\n", indentLevel + 4, "", numProp->unsigned64BitValue());
            }
        }
        propIter->release();
    }

    // Iterate over child entries
    OSCollectionIterator* childIter = entry->getChildIterator(kIOServicePlane);
    if (childIter) {
        IORegistryEntry* child;
        while ((child = OSDynamicCast(IORegistryEntry, childIter->getNextObject()))) {
            dumpRegistryEntry(child, indentLevel + 4);
            child->release();
        }
        childIter->release();
    }
}

// Usage example: Dump the entire IODeviceTree
IORegistryEntry* dtRoot = IORegistryEntry::getRegistryRootEntry(kIODeviceTreePlane);
if (dtRoot) {
    dumpRegistryEntry(dtRoot, 0);
    dtRoot->release();
}

3. Accessing IOUSBDevice Objects

IOUSBDevice objects live within the IOService plane—there's no dedicated "IOUSBDevice plane". To find them, you can either:

  • Traverse the IOService plane recursively (using the above dump function), or
  • Use IOServiceMatching("IOUSBDevice") to filter for USB devices directly:
#include <IOKit/usb/IOUSBLib.h>

IOService* usbDevice = IOService::serviceMatching("IOUSBDevice");
if (usbDevice) {
    uint64_t usbDeviceAddr = reinterpret_cast<uint64_t>(usbDevice);
    IOLog("Found IOUSBDevice @ 0x%llx\n", usbDeviceAddr);
    usbDevice->release();
}

User Mode: Safe Traversal (No Direct Memory Pointers)

If you can't use a kext (e.g., SIP is enabled), you can still traverse the I/O Registry from user mode using IOKit's public APIs—though you won't get direct memory addresses. Here's an Objective-C example:

#import <IOKit/IOKitLib.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        mach_port_t masterPort;
        kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
        if (kr != KERN_SUCCESS) {
            NSLog(@"Failed to get master port: %d", kr);
            return 1;
        }

        // Iterate over IODeviceTree plane
        io_iterator_t iterator;
        kr = IORegistryCreateIterator(masterPort, kIODeviceTreePlane, kIORegistryIterateRecursively, &iterator);
        if (kr != KERN_SUCCESS) {
            NSLog(@"Failed to create iterator: %d", kr);
            return 1;
        }

        io_registry_entry_t entry;
        while ((entry = IOIteratorNext(iterator))) {
            char entryName[256];
            kr = IORegistryEntryGetName(entry, entryName, sizeof(entryName));
            if (kr == KERN_SUCCESS) {
                printf("Entry: %s\n", entryName);
            }
            IOObjectRelease(entry);
        }

        IOObjectRelease(iterator);
    }
    return 0;
}

Critical Notes

  • SIP Restrictions: On modern macOS versions, SIP blocks unsigned kexts. For development, you can disable SIP temporarily (reboot into Recovery Mode and run csrutil disable), but for production, you'll need a signed kext with an Apple Developer ID.
  • Memory Safety: Kernel mode code is unforgiving—invalid pointer access will cause kernel panics. Always use OSDynamicCast to validate object types before accessing them, and release objects with release() when done.
  • User Mode Limits: User mode can't get direct memory addresses of kernel objects—this is intentional for security. Use the kernel mode approach only if you absolutely need raw memory pointers for dumping.

内容的提问来源于stack exchange,提问作者user9652975

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:57:50