如何在C/Objective-C中获取各类I/O平面地址及段偏移以脱离ioreg分析
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
OSDynamicCastto validate object types before accessing them, and release objects withrelease()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

