Intercept system calls from child process

Hello there!


I want to reroute system calls made from a child process X to a custom handler, which than returns to the app like it would have, had the call actually gone to the kernel. Ideally, everything should take place in user space and without root rights.

On Linux, I can do this via the ptrace call, but the implementation on macOS looks a little bit limited for me.


My research so far:


Can anybody give me some pointers on how to intercept and reroute system calls on macOS?


Thanks in advance,

thejack


PS: https://stackoverflow.com/questions/61144869/replace-syscall-in-userspace-in-various-operating-systems, same question, same author, but sadly, no solution

Replies

I’m presuming you’re targeting macOS here; other Apple platforms are sandboxed to the point where this is infeasible.

Can you explain more about your high-level goals? Specifically:

  • What kind of executable is the child process running? Something that you built? Or have control over? Or some arbitrary code from the OS? Or from another developer?

  • Are you looking to deploy this widely? Or just to a limited audience?

These things matter because many of the more useful techniques are restricted by platform security even on the Mac. For example, an easy way to solve this problem is to use

DYLD_LIBRARY_PATH
[1] to override
libSystem
(well,
libsystem_kernel.dylib
these days) but that won’t work for platform binaries, or for third-party binaries with the hardened runtime enabled. And other techniques, like DTrace, only work if SIP is disabled.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] See the

dyld
man page.

I don't know if the Apple shade is really warranted. macOS is a hodge-podge of various different operating systems. If you are expecting it to be a faithful implementation of any one of them, you are bound to be disappointed. It does what it does. Some things it just won't do. Such is life.


Have you looked into using hypervisor code? There is at least one VM in the app store, so it can be done in user space, and even the sandbox. There are some open source versions like qemu and xhyve.

Thanks for the quick response.


Yes, I will in fact be targeting macOS only, it will be a command line-tool that allows the user to run a given program in a sandbox.

$ doinsandbox blabla # where doinsandbox is my tool, and blabla the user-given program, that should run seperated from the system


I do know about the

DYLD_LIBRARY_PATH-Trick, but I would prefer a real solution that cannot be easily bypassed. The only solutions that I currently know of are 1. building my own kext 2. loading the MACH-Binary manually and "overwriting" the INT instructions before executing it, but that seems rather hacky.

Thanks for these pointers, I will check them out. But in my understanding, QEMU emulates the whole processor (and does therefore not need to handle the interrupts) - this is not what I want.

And your distribution model?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Not over the App Store, it will be a command line application that will be published on github.

But I do not want to meddle with the users system too much, so I would prefer a clean solution over writing my own kext or disabling SIP.


Thanks again for the quick response

I don’t think you’ll be able to meet all of your goals here. Consider your list of potential child processes. Most of these — anything built in to the OS, shipped via the Mac App Store, or with the hardened runtime enable — is protected by SIP. You cannot intercept such a program’s system calls without either disabling SIP or re-signing the program.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"