Recently while looking into the Apple adid daemon, I noticed that I couldn’t attach to the process with lldb even if SIP was completely disabled. After digging into it a little bit I came to the conclusion that adid was calling the ptrace API passing in PT_DENY_ATTACH. There are numerous other posts out there (like this one) that talk about defeating PT_DENY_ATTACH if you’re running the application yourself. In my case adid is started as a LaunchDaemon and is already running by the time a user is logged in. I decided to take a look at how you could defeat the ptrace call even after the application is already running.
First a quick note on how I determined that adid was using the ptrace API. I started off on a virtual machine with SIP disabled. This is my usual setup when inspecting Apple platform binaries. I tried to connect to adid using lldb but it didn’t seem to work.
$ ps xa | grep adid
374 ?? Ss 0:00.06 /System/Library/PrivateFrameworks/CoreADI.framework/adid
$ lldb -o "attach 374"
(lldb) attach 374
error: attach failed: Error 1
$ sudo lldb -o "attach 374"
Password:
(lldb) attach 374
error: attach failed: lost connection
The second attempt running lldb with sudo resulted in debugserver itself crashing with a segmentation fault. This is what made me think that ptrace might be in use. Checking the symbols on adid confirmed this.
$ jtool2 -S /System/Library/PrivateFrameworks/CoreADI.framework/adid | grep ptrace
U _ptrace
From there I decided that I should be able to either catch the ptrace call at boot time by debugging XNU itself or potentially inspect and undo the ptrace call from the running process. I set up my virtual machine for kernel debugging and then decided to look into what the ptrace call itself does when called. Looking in the XNU source we can see that the following happens when calling ptrace(PT_DENY_ATTACH, 0, 0, 0);
...
#define P_LNOATTACH 0x00001000
...
if (uap->req == PT_DENY_ATTACH) {
proc_lock(p);
if (ISSET(p->p_lflag, P_LTRACED)) {
proc_unlock(p);
KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_FRCEXIT) | DBG_FUNC_NONE,
p->p_pid, W_EXITCODE(ENOTSUP, 0), 4, 0, 0);
exit1(p, W_EXITCODE(ENOTSUP, 0), retval);
thread_exception_return();
/* NOTREACHED */
}
SET(p->p_lflag, P_LNOATTACH);
proc_unlock(p);
return(0);
}
...
When the ptrace API is called with the PT_DENY_ATTACH argument what happens is that the proc structures p_lflag field gets updated to have the P_LNOATTACH flag set. Some of the proc fields are exposed to user space APIs but unfortunately it doesn’t look like the p_lflag is. At this point I decided to start up lldb and connect to the kernel and inspect the task itself. (The following commands assume you’ve loaded the lldbmacros that come with the Kernel Debug Kit)
First I got some information about the adid task.
(lldb) showtask -F adid
task vm_map ipc_space #acts flags pid process io_policy wq_state command
0xffffff8017de1188 0xffffff8018d97838 0xffffff801acca980 3 R 374 0xffffff801acecd50 BLT 2 2 0 adid
(lldb) showprocinfo 0xffffff801acecd50
Process 0xffffff801acecd50
name adid
pid:374 task:0xffffff8017de1188 p_stat:2 parent pid: 1
Cred: euid 265 ruid 265 svuid 265
Flags: 0x4004
0x00000004 - process is 64 bit
0x00004000 - process has called exec
Now that we have an address for the process itself we can inspect the p_lflag field.
(lldb) p/x ((struct proc *)0xffffff801acecd50)->p_lflag
(unsigned int) $2 = 0x00801000
You can see that the p_lflag field has the P_LNOATTACH flag set. All we need to do to effectively negate the ptrace call is to change the value of the p_lflag field.
(lldb) expr ((struct proc *)0xffffff801acecd50)->p_lflag = 0x00800000
After this lldb can happily attach to the adid process. This effectively solves the original problem I had, which was being able to debug adid after it has already started, but it got me wondering what other processes Apple has that might be doing the same type of thing. I created a short python script to check all tasks to see if they had the P_LNOATTACH flag set.
(lldb) command script import kernhelper.py
(lldb) script kernhelper.show_ptrace_deny_attach_procs()
...
...
0xffffff8025cabe20: SecurityAgent
0xffffff802a615b70: authorizationhos
0xffffff802cbd87f0: adid
(lldb)
So you can see there are a couple other processes running after first login that also have called ptrace passing in the PT_DENY_ATTACH argument. These correspond to the following binaries:
/System/Library/Frameworks/Security.framework/Versions/A/MachServices/SecurityAgent.bundle/Contents/MacOS/SecurityAgent
/System/Library/Frameworks/Security.framework/Versions/A/MachServices/authorizationhost.bundle/Contents/MacOS/authorizationhost
/System/Library/PrivateFrameworks/CoreADI.framework/adid
If we go even further and try to search for all binaries on a clean macOS 10.14.5 install that reference ptrace we see the following list:
$ cd /System/Library
$ rg -l -a -M 80 _ptrace 2> /dev/null
Kernels/kernel
QuickTime/QuickTimeComponents.component/Contents/MacOS/QuickTimeComponents
CoreServices/AppleFileServer.app/Contents/MacOS/AppleFileServer
Frameworks/DVDPlayback.framework/Versions/A/DVDPlayback
PrivateFrameworks/CoreADI.framework/Versions/A/adid
PrivateFrameworks/CoreFP.framework/Versions/A/fpsd
PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages
PrivateFrameworks/AnnotationKit.framework/Versions/A/XPCServices/com.apple.AnnotationKit.MigratorService.xpc/Contents/MacOS/com.apple.AnnotationKit.MigratorService
Frameworks/Security.framework/Versions/A/MachServices/SecurityAgent.bundle/Contents/MacOS/SecurityAgent
Frameworks/Security.framework/Versions/A/MachServices/authorizationhost.bundle/Contents/MacOS/authorizationhost
I’m not sure if all of these are really using PT_DENY_ATTACH or if some are just referencing the text _ptrace in some way but they are candidates that could be looked at in the future.
My hope is this post shows a good example of how you can get started with kernel debugging learning about the internals of XNU itself as well as showing that as long as we still have control of the kernel on macOS there’s ultimately not much Apple can do to prevent us from inspecting their binaries.