Syscall tracing made easy
System call tracing is a useful tool to better understand programs, especially large programs not written by you. For example, I used it recently to understand where my Java runtime was looking for its TLS certificate store. If you’re unfamiliar with the general technique, Julia Evans has a great introduction. Lots of people have also published scripts to perform common operations easily, but none of them worked quite how I wanted, so I decided to write my own. Note that these tools are rather OS specific, and I am using macOS.
My script opens a new shell and watches everything I do inside of it. When I close the shell, it gives me a file with all of the system calls of processes run inside the shell. strace, dtrace, and the rest can connect to running processes or launch new ones (although I’ve found that latter feature iffy), but often I have a whole set of steps I want to run and watch, and the easiest way to do that is from the shell. Using my script, I don’t have to remember any special dtruss commands, I can just use my shell normally and then grep through the syscall dump afterwards. So, without further ado, the annotated script:
#!/bin/bash
# -m is necessary to enable job control in this non-interactive script
set -m
function main {
# sip is system integrity protection, a macOS feature that must be disabled
check_sip
get_sudo
filename=$(mktemp)
# PS1 is the bash prompt, the escape codes produce a prompt like:
# ~/current/dir [rec]$
# with "rec" in red
PS1="\W [\[\e[31m\]rec\[\e[m\]]$ " bash &
pid=$!
# -f means follow, or trace child processes of the shell
# this is important, since usually we want to trace the programs we launch,
# not the shell itself
sudo dtruss -p $pid -f 2> "$filename" &
# bring the shell back to the foreground
fg %1
# this statement runs once the shell exits
echo "Wrote trace from process $pid to $filename"
}
function check_sip {
if is_sip_disabled; then
echo "System integrity protection is disabled. Proceeding."
else
echo "System integrity protection must be disabled to run dtruss."
echo "https://www.imore.com/how-turn-system-integrity-protection-macos"
exit 1
fi
}
function is_sip_disabled {
csrutil status | grep -q 'disabled'
}
function get_sudo {
# prompt the user for their password in advance, for simplicity
# (since sudo dtruss... output is redirected)
# || exit 1 exits early if the user hits ctrl-c at the password prompt
sudo --validate || exit 1
}
main
In order to run dtruss on macOS, you usually have to disable system integrity protection. The script will prompt you to do this if necessary. Assuming that is done, the script will then create a temporary file, open bash in the background, set up dtruss to monitor bash and write to the temporary file, and bring bash to the foreground. You get a custom bash prompt to remind you that your syscalls are being recorded. When you exit the shell, the script will print the filename containing the syscalls made during the bash session, which you can then search through for interesting filenames, sockets, etc.