XPoCe - XPC Snooping utilties for MacOS and iOS

What is this?

XPC* is the enhanced IPC framework used in *OS. Ever since its introduction in 10.7/iOS 5, its use has exploded, as AAPL is rewriting most of its daemons to use it in place of the venerable raw Mach messages. Mach still provides the medium, but message payloads are now dictionary objects - reducing (but not eliminating) type confusion mistakes, and greatly simplifying parsing. In addition, XPC is closely tied to GCD (offering much better performance) and entitlements (greater security).

I started working on a simple dyld injected library called XPCSnoop back when I embarked on my quest to expose launchd(1) and provided it as a bonus download along with my version of launchctl(8) (Update on that: Quest is going well, launchd, your day in the sun is coming :-)). But as with other quick and dirty projects, it started taking a life of its own, and became super useful. So I've added more features, and am releasing it as its own little tool.

A MUCH better alternative can be found in XPoCe v2


  • Or, in a LaunchDaemon plist, simply add:
  • You can modify the behavior by additional environment variables, or touching files in /tmp as follows:

    Environment Variable/tmp filePurpose
    XPOCE_BACKTRACE/tmp/xpoce_backtraceDump a backtrace at the time of intercepted XPC call
    XPOCE_HEXHex dump data, rather than printing it out
    XPOCE_NODESC/tmp/xpoce_nodescDon't use my object description function - use xpc_copy_description instead
    XPOCE_COLOR/tmp/xpoce_colorColors :-) Makes it much easier to see outgoing/incoming messages, but may mess up grep and more (use less -R)
    XPOCE_OUTOutput to stderr. Otherwise output is /tmp/procname.pid.XPoCe. Useful if you're running via command line.
    XPOCE_NOINCOnly get outgoing (xpc_connection_send* messages.
    JDEBUGMy usual

    The output will be in /tmp/processName.pid.XPoCe and will look something like this:

    XPCCSMWRS Thu Feb  2 14:11:35 2017 (35) Outgoing ==> Peer: (null), PID: 78 (launchservicesd)
     (with reply sync)
    --- Dictionary 0x7faf24d06790, 2 values:
          command: +360
          asn: 139298
    --- End Dictionary 0x7faf24d06790
    Thu Feb  2 14:11:35 2017 <== Peer: (null), PID: 78 (launchservicesd)
    --- Dictionary 0x7faf24d078a0, 3 values:
        cacheable: true
        success: true
        result: // (dictionary 0x7faf24d06b00)--- Dictionary 0x7faf24d06b00, 47 values:
          LSBundlePathINode: +332891
          LSApplicationInSandboxKey: true
          CanBecomeFrontmost: true
          CFBundleExecutable: "LaterAgent"
          DTSDKName: "macosx10.12internal"
          LSDisplayName: "LaterAgent"
          CFBundleShortVersionString: "1.0"
          BuildMachineOSBuild: "16B2545"
          LSBundlePathLastComponentLowerCaseKey: "lateragent.app"
          CFBundleIdentifier: "com.apple.lateragent"
          CFBundleSignature: "????"
          DTCompiler: "com.apple.compilers.llvm.clang.1_0"
          DTPlatformVersion: "GM"
          CFBundleExecutablePath: "/System/Library/PrivateFrameworks/CommerceKit.framework/Versions/A/Resources/LaterAgent.app/Contents/MacOS/LaterAgent"
          LSBundlePath: "/System/Library/PrivateFrameworks/CommerceKit.framework/Versions/A/Resources/LaterAgent.app"
          LSApplicationSandboxedInformationItemsKey: // Array (23 values)
          LSApplicationSandboxedInformationItemsKey[0]: "CFBundleName"
          LSApplicationSandboxedInformationItemsKey[1]: "DTXcode"
          LSApplicationSandboxedInformationItemsKey[2]: "DTSDKName"
          LSApplicationSandboxedInformationItemsKey[3]: "NSHumanReadableCopyright"
          LSApplicationSandboxedInformationItemsKey[4]: "DTSDKBuild"
          LSApplicationSandboxedInformationItemsKey[5]: "CFBundleDevelopmentRegion"
          LSApplicationSandboxedInformationItemsKey[6]: "CFBundleVersion"
          LSApplicationSandboxedInformationItemsKey[7]: "BuildMachineOSBuild"
          LSApplicationSandboxedInformationItemsKey[8]: "NSPrincipalClass"

    Or, in color:

    The output is in simplistic ™ format, so it's much more readable than XML, but can be used directly with my tool to convert back to XML.. or replay (coming soon :-). True to its name, simplistic is a really straightforward format - Strings are quoted (""), uint64s are just numbers, and int64 have a sign (even if positive, so you can tell them from a uint64). This way you can easily figure out what the datatype is, even though I don't print it.

    Note some output (from NSXPCConnection remote Obj-c invocations) appears as the undocumented bplist16 format. That's the serialization for Objective-C remoting which AAPL never bothered to document. I'll support it fully in a future version (and document the format well in Volume I).


    1. How does it work??? Via dyld interposing of the XPC related functions. It's open source, so you can figure it out yourself.
    2. Does this work on *OS? Certainly. BUT, note some caveats:
      • The sandbox thinks it's smart, so will prevent mmap(2) from /private/var. So put this in /usr/lib for best results.
      • dyld also thinks it's pretty smart, so will refuse to honor DYLD_INSERT_LIBRARIES if ! get_task_allow. Fortunately, most jailbreaks patch that.
    3. How do I get this to compile on *OS? You just copy over header files from the MacOS.sdk to the [iPhone/Watch/AppleTV]OS].sdk. These include xpc/, libproc.h, and a bunch of others. Why AAPL insists on hiding those headers is beyond me.
    4. It doesn't work on MacOS for some binaries!!! That's because those binaries are either (A) entitled or (B) __RESTRICTED or (C) Setuid. Disable SIP and things work a lot better.
    5. What if I can't DYLD_INSERT, or the issues in (2) are still a problem? Then you need to force inject this into a running process and do interposing manually. I'm working on a pro version that does just that.
    6. I'm still not getting some messages which are clearly XPC! Or I get the output file, but it's empty?? That's because dyld interposing only gets the calls from the binary to its dylibs, but not calls made from dylib A to dylib B in the shared cache - Those have their stubs resolved to addresses, and cannot be intercepted through simple DYLD interposing. But it's doable. I'm working on a pro version, I remind you.
    7. What about MOXiI 2??!?!?! Volume III is out, Volume I (where this tool is from) will be out in a few months. Volume II will conclude the series. Patience, people. People tell me Volume III was well worth the wait - and I promise that I & II will be even more so.
    8. How do I get the book?! On Amazon, via link to the right, and/or through this website, via Paypal (especially if you're international), but please , be my friend when you transfer money so PYPL doesn't kill me with fees...
    9. I want to know more about Internals!!! Come join TechnoloGeeks' training - NYC February 13th. Click here for more info.
    10. Wait, where's the download?!?!?! Right here. Just trying to make sure people read :-)