Man in the Middle System Espionage
October 7th, 2005Many system services on the Mac rely on command-line tools either stored in public paths or stashed away in hard-to-find places. When I’m curiously poking around on my system (as I’m a bit too often likely to be doing), I often discover one of these master-slave relationships and wonder exactly what’s going on behind the scenes. Usually there are some magic command-line incantations that may or may not be documented for the tool itself. By examining what parts of the system do to take advantage of these command-line entry points, we can potentially come up with ways of facilitating improved behaviors for our own purposes.
As an example, let’s take the screencapture tool. Located at /usr/sbin/screencapture, this is what provides the core functionality for the Cmd-Shift-3 and Cmd-Shift-4 screenshot shortcuts. The process responsible for invoking screencapture is the SystemUIServer. Now, if we are curious about what exactly gets passed to the screencapture tool, we have two obvious choices: intercept the invoker, or intercept the invoked.
Intercepting the invoker can be difficult. Not only do you have to identify which process it is, but you then have to attach to the calling process and attempt to break at the relevant code that executes the target tool. Much easier at times when you just want to take a quick peek is to swap the called tool with a bogus stand in. You don’t even need developer tools! The stand-in is named identically to the real deal, but instead of performing the expected function, it simply echoes the provided arguments to a specified temporary file:
#!/usr/bin/perl `echo "@ARGV\n" >> /tmp/testDCJ`;
I like to send the arguments to a temporary file instead of printing to the standard output, because some callers will manipulate the forked task such that its standard output is disabled. The nice thing about the simple script above is that it’s completely independent of the tool being replaced. You can keep it around and simply copy it in when you want to peek on something. Don’t forget to move the original aside!
Of course, you could also add another line below the echo to go ahead and call through to the original application, if you want to keep the functionality going while your snooping is in place. For instance, this lets me see exactly what screencapture is being asked to do, without diminishing its ability to do it:
#!/usr/bin/perl `echo "@ARGV\n" >> /tmp/testDCJ`; exec("/usr/sbin/screencapture-original", @ARGV);
Once you’ve got this mechanism in place, the possibility of mucking with the passed in parameters is irresistible. Say I don’t like the screenshot sounds, and I tend to take more screenshots of windows than of selected ranges. I just sneak the required parameters in before passing the ones that came from SystemUIServer. I’m done with my snooping so I’ll comment-out the argument printing line until the next time I need to pay attention. This is a simple example, but with a little parsing you can remove unwanted options from the passed-in ARGV array, fine-tune provided filenames, etc:
#!/usr/bin/perl #`echo "@ARGV\n" >> /tmp/testDCJ`; exec("/usr/sbin/screencapture-original", '-W', '-x', @ARGV);
The major drawback to leaving your custom file in place of the original is that it’s bound to get clobbered when you update your system and Apple releases a new version of the patched tool. One way to avoid this problem is to keep your man in the middle stashed somewhere outside of the target directory, and point at it with a symbolic link. For instance, if you put your perl script in /Users/Shared, you can perform the patch as follows:
% cd /usr/sbin/ % sudo mv screencapture screencapture-original % sudo chmod 755 /Users/Shared/ScreenCaptureHack.pl % sudo ln -s /Users/Shared/ScreenCaptureHack screencapture
Now when Apple comes along with a new screencapture, you can just pop in and repeat those steps to get your custom functionality back (or write a script to do it!).
While we’re talking about screencapture, let me point out some useful, under-published user defaults that control the way SystemUIServer invokes screencapture. I was turned on to the presence of these by a post in MacOSX-Talk, which prompted me to search for and find more detailed explanations on MacOSXHints.com. I found hints for the "type" and "location" keys there, which encouraged me to do my own further snooping (set a breakpoint on CFPreferencesCopyAppValue while re-launching SystemUIServer) and discover the "name" key. Using these keys, you can customize your screen captures to be other than the default PNG "Picture N" on the Desktop (as of Tiger):
% defaults write com.apple.screencapture name "Booyah\!" % defaults write com.apple.screencapture type "jpg" % defaults write com.apple.screencapture location "/Users/daniel/Documents/Screen\ Captures" % killall SystemUIServer
The killall at the end is not necessary for some of the keys, but appears to be necessary for the location.
Now all of my screen captures get stowed away in a tidy subfolder of my Documents folder, they are jpg format for maximum portability, and they have a very silly name.
Many behaviors of Mac OS X are unchangeable, or require heavy hex-editing to alter. But thanks to the frequent use of command-line helpers and defaults-driven behavior, much can be altered with a little snooping and careful patching.
April 26th, 2007 at 10:33 am
[…] from http://www.red-sweater.com/blog/36/coding-for-readabilityPingBack from […]