Friday, July 25, 2014

Snooping On Mac OS X Apps

On iOS an app is limited in scope to it's sandbox (unless you are on a jailbroken device) and so you can take a quick look at the apps filesystem within Xcode to see what it is storing. This is not so for the keychain but you can guess the keychain is in use if the app links the Security.framework.

Now, for Mac OS X, since the app may have access to more resources it can be harder to track down what resources the app you are testing is using. There are a couple ways to figure this out though. Here are the ways I use to get an idea what resources a Mac OS X app is using:

  • Activity
    • Launch Activity Monitor
    • Find your app
    • Double click it
    • Choose the "Open File and Ports" tab.
    • See what the app has open currently
  • Use Dtrace
    • This is the long bread method of finding out what your app is doing.
    • Even better you can trace all file opens on a specific process (meaning you get a trace of the opens int eh terminal).
    • To do this open
    • This command must be run as root so I will show you the command with sudo at the front which will prompt the terminal to ask for your root password to complete the command:
      • sudo opensnoop
Here is sample output from opensnoop:
sudo opensnoop
  UID    PID COMM          FD PATH                 
   89  72769 mdworker       7 /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk/System/Library/Frameworks/OpenGLES.framework/glp_sh_interposer.dylib.dSYM/Contents 
   89  72769 mdworker       7 /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk/System/Library/Frameworks/OpenGLES.framework/glp_sh_interposer.dylib.dSYM/Contents/Resources 
   89  72769 mdworker       7 /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk/System/Library/Frameworks/OpenGLES.framework/glp_sh_interposer.dylib.dSYM/Contents/Resources/DWARF 
   89  72769 mdworker       7 /Developer/Applications/

Notice that you get the User ID, the Process ID, the Process (COMM), the File Descriptor, and the Path to the file that was opened.

You can play with opensnoop to limit by process name, process id, etc.  Here is the usage output:
USAGE: opensnoop [-a|-A|-ceghsvxZ] [-f pathname]
                 [-n name] [-p PID]
       opensnoop                # default output
                -a              # print most data
                -A              # dump all data, space delimited
                -c              # print cwd of process
                -e              # print errno value
                -g              # print command arguments
                -s              # print start time, us
                -v              # print start time, string
                -x              # only print failed opens
                -Z              # print zonename
                -f pathname # pathname name to snoop
                -n name # process name to snoop
                -p PID # process ID to snoop
       opensnoop -v             # human readable timestamps
       opensnoop -e             # see error codes
       opensnoop -f /etc/motd   # snoop this file only

I like to use this by process name, start the process, run the app a bit to exercise most of the functionality, and then look at what opensnoop found. Turn on printing of the errno values and you can see if any file opens are failing.

Come to think of it, another Dtrace script I love is errinfo. This one will show you system calls that are returning an error. It is a good one to use when you know the name of a process having a problem. For example, I once worked with a app that was not logging when it launched another process but the process never started. Missed error checking aside, I used this to show that the exec was failing due to a permission error. The permissions on the sub-process were not set correctly and the other process, running as a different user could not exec the process since it did not have the correct permissions. We fixed the missing error checking and the permission error and all was well.

If you want to know what Dtrace scripts your Mac has then run "apropos Dtrace". Here is what my Mac is showing for Mavericks:
Tcl_CommandTraceInfo(3tcl), Tcl_TraceCommand(3tcl), Tcl_UntraceCommand(3tcl) - monitor renames and deletes of a command
bitesize.d(1m)           - analyse disk I/O size by process. Uses DTrace
cpuwalk.d(1m)            - Measure which CPUs a process runs on. Uses DTrace
creatbyproc.d(1m)        - snoop creat()s by process name. Uses DTrace
dappprof(1m)             - profile user and lib function usage. Uses DTrace
dapptrace(1m)            - trace user and library function usage. Uses DTrace
diskhits(1m)             - disk access by file offset. Uses DTrace
dispqlen.d(1m)           - dispatcher queue length by CPU. Uses DTrace
dtrace(1)                - generic front-end to the DTrace facility
dtruss(1m)               - process syscall details. Uses DTrace
errinfo(1m)              - print errno for syscall fails. Uses DTrace
execsnoop(1m)            - snoop new process execution. Uses DTrace
fddist(1m)               - file descriptor usage distributions. Uses DTrace
filebyproc.d(1m)         - snoop opens by process name. Uses DTrace
hotspot.d(1m)            - print disk event by location. Uses DTrace
httpdstat.d(1m)          - realtime httpd statistics. Uses DTrace
iofile.d(1m)             - I/O wait time by file and process. Uses DTrace
iofileb.d(1m)            - I/O bytes by file and process. Uses DTrace
iopattern(1m)            - print disk I/O pattern. Uses DTrace
iopending(1m)            - plot number of pending disk events. Uses DTrace
iosnoop(1m)              - snoop I/O events as they occur. Uses DTrace
iotop(1m)                - display top disk I/O events by process. Uses DTrace
kill.d(1m)               - snoop process signals as they occur. Uses DTrace
lastwords(1m)            - print syscalls before exit. Uses DTrace
loads.d(1m)              - print load averages. Uses DTrace
newproc.d(1m)            - snoop new processes. Uses DTrace
opensnoop(1m)            - snoop file opens as they occur. Uses DTrace
pathopens.d(1m)          - full pathnames opened ok count. Uses DTrace
perldtrace(1)            - Perl's support for DTrace
pidpersec.d(1m)          - print new PIDs per sec. Uses DTrace
plockstat(1)             - front-end to DTrace to print statistics about POSIX mutexes and read/write locks
priclass.d(1m)           - priority distribution by scheduling class. Uses DTrace
pridist.d(1m)            - process priority distribution. Uses DTrace
procsystime(1m)          - analyse system call times. Uses DTrace
runocc.d(1m)             - run queue occupancy by CPU. Uses DTrace
rwbypid.d(1m)            - read/write calls by PID. Uses DTrace
rwbytype.d(1m)           - read/write bytes by vnode type. Uses DTrace
rwsnoop(1m)              - snoop read/write events. Uses DTrace
sampleproc(1m)           - sample processes on the CPUs. Uses DTrace
seeksize.d(1m)           - print disk event seek report. Uses DTrace
setuids.d(1m)            - snoop setuid calls as they occur. Uses DTrace
sigdist.d(1m)            - signal distribution by process. Uses DTrace
syscallbypid.d(1m)       - syscalls by process ID. Uses DTrace
syscallbyproc.d(1m)      - syscalls by process name. Uses DTrace
syscallbysysc.d(1m)      - syscalls by syscall. Uses DTrace
topsyscall(1m)           - top syscalls by syscall name. Uses DTrace
topsysproc(1m)           - top syscalls by process name. Uses DTrace
weblatency.d(1m)         - website latency statistics. Uses DTrace

Have fun with Dtrace, it can open up your debugging and give you a view into processes you don't own.

No comments: