Hacktricks-skills macos-mig-analyzer
Analyze Mach Interface Generator (MIG) IPC on macOS. Use this skill whenever the user mentions MIG, Mach IPC, macOS inter-process communication, binary analysis of Mach-O files with IPC, extracting dispatch tables from macOS binaries, or reverse engineering macOS system services. Trigger for any task involving .defs files, mach_port, bootstrap_look_up, jtool2 MIG analysis, or identifying RPC functions in macOS binaries.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator/SKILL.MDmacOS MIG Analyzer
A skill for analyzing Mach Interface Generator (MIG) based inter-process communication on macOS systems.
When to Use This Skill
Use this skill when:
- Analyzing macOS binaries that use Mach IPC
- Reverse engineering MIG-based system services
- Extracting dispatch tables from Mach-O binaries
- Understanding
interface definition files.defs - Identifying RPC functions in macOS daemons
- Debugging Mach port communication
- Using
to parse MIG informationjtool2
MIG Fundamentals
MIG (Mach Interface Generator) simplifies Mach IPC code creation by generating server and client code from Interface Definition Language (IDL) files with
.defs extension.
Definition File Structure
MIG definitions have 5 sections:
- Subsystem declaration: Specifies name and ID, optionally marked as
KernelServer - Inclusions and imports: Uses C-preprocessor, supports
anduimportsimport - Type declarations: Custom types with
,[in/out]tran
,c[user/server]typedestructor - Operations: RPC method definitions (5 types:
,routine
,simpleroutine
,procedure
,simpleprocedure
)function - Generated code: Server and client stubs
Operation Types
| Type | Expects Reply |
|---|---|
| Yes |
| No |
| Yes |
| No |
| Yes |
Binary Analysis Workflow
Step 1: Identify MIG Usage
Check if a binary uses MIG by looking for
_NDR_record dependency:
jtool2 -S <binary> | grep NDR # or nm <binary> | grep NDR
MIG servers have dispatch tables in
__DATA.__const (macOS userland) or __CONST.__constdata (kernel).
Step 2: Extract MIG Dispatch Information
Use
jtool2 to parse MIG data from the binary:
# Extract dispatch table jtool2 -d __DATA.__const <binary> | grep MIG # Find function calls (BL instructions) jtool2 -d __DATA.__const <binary> | grep BL
Step 3: Locate Routine Descriptors
The dispatch table contains
routine_descriptor structs (0x28 bytes each):
struct routine_descriptor { mig_impl_routine_t impl_routine; // 8 bytes - actual function address mig_stub_routine_t stub_routine; // 8 bytes - stub function int const_count; // 4 bytes int var_count; // 4 bytes routine_arg_descriptor_t *arg_desc; // 8 bytes mach_msg_size_t maxsize; // 4 bytes // padding to 0x28 };
Each descriptor is 0x28 bytes. The first 8 bytes contain the function address.
Step 4: Map Message IDs to Functions
MIG uses sequential IDs starting from the subsystem ID:
// Example: subsystem myipc 500 // First operation = ID 500, second = ID 501, etc. // In generated code: msgh_id = InHeadP->msgh_id - 500; // Calculate index routine = subsystem.routine[msgh_id].stub_routine;
Step 5: Analyze Server Routine
The
myipc_server function (or similar) handles message dispatch. Key patterns:
- Validates message ID range
- Calculates array index:
msgh_id - start_id - Looks up function pointer in dispatch table
- Calls the appropriate handler
Look for this pattern in decompiled code:
if (msgh_id >= start && msgh_id <= end) { routine = dispatch_table[msgh_id - start].stub_routine; if (routine != 0) { routine(InHeadP, OutHeadP); return TRUE; } }
Common Analysis Tasks
Extract All MIG Functions from Binary
# Get the dispatch table address jtool2 -s <binary> | grep -A5 "__DATA.__const" # Dump the section jtool2 -d __DATA.__const <binary> > mig_dump.txt # Look for routine descriptors (search for MIG patterns) grep -A20 "subsystem" mig_dump.txt
Find Bootstrap Service Names
MIG servers often register with bootstrap:
# Search for bootstrap service strings jtool2 -s <binary> | grep -i bootstrap strings <binary> | grep -E "^[a-z0-9._-]+$" | grep -v "^[0-9]"
Identify Client vs Server
| Indicator | Client | Server |
|---|---|---|
Uses | Yes | Yes |
Calls | Yes | Yes |
| Has dispatch table | No | Yes |
Uses | Yes | No |
Uses | No | Yes |
Has | No | Yes |
Debugging MIG Communication
Enable MIG debug logging:
# View MIG kernel debug messages kdv all | grep MIG # Or use trace trace -f <binary>
Example Analysis
Simple MIG Definition
subsystem myipc 500; userprefix USERPREF; serverprefix SERVERPREF; #include <mach/mach_types.defs> #include <mach/std_types.defs> simpleroutine Subtract( server_port : mach_port_t; n1 : uint32_t; n2 : uint32_t);
Generated Server Structure
const struct SERVERPREFmyipc_subsystem SERVERPREFmyipc_subsystem = { myipc_server_routine, // Server routine 500, // start ID 501, // end ID sizeof(union __ReplyUnion__), 0, { { 0, _XSubtract, 3, 0, 0, sizeof(__Reply__Subtract_t) } } };
Server Implementation Pattern
kern_return_t SERVERPREFSubtract(mach_port_t server_port, uint32_t n1, uint32_t n2) { // Your handler code here printf("Received: %d - %d = %d\n", n1, n2, n1 - n2); return KERN_SUCCESS; } int main() { mach_port_t port; kern_return_t kr; // Register with bootstrap kr = bootstrap_check_in(bootstrap_port, "com.example.service", &port); // Start message server mach_msg_server(myipc_server, sizeof(union __RequestUnion__), port, MACH_MSG_TIMEOUT_NONE); }
Client Implementation Pattern
int main() { mach_port_t port; kern_return_t kr; // Lookup service kr = bootstrap_look_up(bootstrap_port, "com.example.service", &port); if (kr != KERN_SUCCESS) { printf("Failed to lookup service\n"); return 1; } // Call RPC USERPREFSubtract(port, 40, 2); }
Tools Reference
jtool2 Commands
# Show section symbols jtool2 -s <binary> # Dump section data jtool2 -d <section> <binary> # Show dependencies jtool2 -S <binary> # Disassemble jtool2 -D <binary>
Finding MIG Examples on System
# Find .defs files mdfind "*.defs" # Find mach_port.defs mdfind mach_port.defs # Compile example mig -DLIBSYSCALL_INTERFACE mach_ports.defs
Common Pitfalls
- Message ID offset: Remember to subtract the subsystem start ID when indexing into the dispatch table
- Reply handling: Only
,routine
, andprocedure
types expect repliesfunction - Port management: First argument is always the server port; MIG handles reply ports automatically
- NDR encoding: Data is encoded for cross-system compatibility via
_NDR_record - Dispatch table location: Varies by platform (
vs__DATA.__const
)__CONST.__constdata
Next Steps
For deeper analysis:
- Use Hopper/IDA to decompile the server routine
- Extract routine descriptors using the 0x28-byte stride
- Map each function address to its handler
- Trace actual IPC calls with
ordtracekdv - Compare against known system services for patterns
References
- OS Internals, Volume I, User Mode - Jonathan Levin
- macOS Mach IPC documentation
- jtool2 GitHub repository
- MIG man page:
man mig