Fixing ptrace(pt_deny_attach, ...) on Mac OS X 10.4 Tiger

20 Nov 2005, 18:05 PST

NOTE: For information on Mac OS X Leopard (10.5), refer to this article.

PT_DENY_ATTACH is a non-standard ptrace() request type available on Mac OS X that prevents a debugger from attaching to the calling process. This article will cover disabling PT_DENY_ATTACH for all processes on Mac OS X 10.4. For more information on how the request type is implemented, please refer to the previous article.

In Mac OS X 10.4 Tiger, Apple has introduced official KEXT Programming Interfaces , with the intention of providing kernel binary compatibility between major operating system releases. As a part of this effort, the sysent array is no longer easily accessible from a kernel extension, removing the ability to (easily) override system calls, and breaking our pt_deny_attach kext on Mac OS X 10.4.

While it is possible to start the program in question under gdb and break on calls to ptrace(), this method won't help at all with software that is either already running or difficult to start under gdb. So, I've updated the kext to work with 10.4 -- It is my computer, after all. If Sony BMG installs a rootkit, I want to be able to look at it.

Interestingly enough, I was not the only person overriding ptrace() -- an unnamed third-party developer was patching the syscall in order to implement some kind of anti-piracy protection. In order to support this anonymous developer on 10.4, Apple added a deprecated API, temp_patch_ptrace() and temp_unpatch_ptrace().

Unsupported ptrace() patch API:

uintptr_t temp_patch_ptrace(uintptr_t new_ptrace)
void temp_unpatch_ptrace(void)

The API is straight-forward. We pass in a pointer to our ptrace() function, temp_patch_ptrace() passes back a pointer to the real ptrace(). Only one patcher is permitted, so we call temp_unpatch_ptrace() first. Sorry anonymous anti-piracy developer!

Our new override function is dead simple:

kern_return_t pt_deny_attach_start (kmod_info_t * ki, void * d) {
        /* If ptrace is already patched, unpatch it */
        /* Patch ptrace() */
        real_ptrace = (void *) temp_patch_ptrace((uintptr_t) our_ptrace);
        printf("Disallowing ptrace(PT_DENY_ATTACH, ...)\n");
        return KERN_SUCCESS;

I've made the source to the kext available here. PGP signature. My PGP Key ID is 4FF5E663