The Arctic Mac

Finding New Hidden Preferences Using GDB

Introduction

If you haven't already done so, you might want to read the intro to hidden preferences.

Now, you've probably seen lots of hidden preferences by this point, and you're familiar with the defaults command, but maybe you've wondered how people figure them out... In the past, finding these hidden preferences involved going through the binary code of an application and looking for readable strings, and then guessing how they might work. However, as of Mac OS 10.6, this no longer works, because of changes in the way executables are built and stored. So, we turn to gdb, the GNU debugger, which allows us to inspect running processes, and see what strings they use. The advantage to this approach is that you can limit your search to just the strings that the application is using. Since you're already in the debugger, it's not too much extra work to take a look at what kind of data the program is expecting back, and what domain it belongs to (which it turns out is not always the domain of the app you're inspecting - see the Finder page for an example). If you're a little braver, you can even try to figure out what the application is doing, although that requires you to actually know a little bit about programming. The disadvantage of this technique is that you have to wait for the application to ask for the preference in question, which, for many settings, it won't do right away when it starts up.

Oh, and the exact routine suggested below works on 10.6, but may or may not work on older systems. The same general technique should apply, but because the programs are built differently, the exact commands below are unlikely to work.

Getting off the Ground

For starters here, you'll need to have the Mac OS X developer tools installed. They're listed as optional installs on your Snow Leopard install disc, or can be downloaded from ADC if you have an account.

We're going to be working here on the Finder, but the same technique should generally apply to other apps as well with only slight modifications (and I'll discuss some of those further down). That being the case, I'd suggest you go ahead and quit the Finder (you have a Quit Menu Item, right?) before you get started.

Our technique here will be to have gdb run the Finder and watch for it to make calls to the standard library's preferences functions. Then (to save us lots of hard work), we'll have gdb print out the name of the setting being asked for, along with the domain it belongs to. After we've run the Finder for long enough that we think we've got all the settings we want, we'll quit the Finder again (good old QuitMenuItem), and stop GDB. Finally, we'll use sort to alphabetize the recorded settings and remove duplicates. All that's left after that is for you to go through the text file we'll have generated to remove little bits of extra verbosity that gdb prints out, and to look for prefs you'd like to change. Ready?

Getting Our Hands Dirty

Copy and paste the following commands into a text editor of your choice and save them someplace convenient. I picked to save them to my Desktop in a file called gdbPrefsCmds.txt. (Note that I think the lines should come out right here, however if you use Safari and copy/paste, it should take care of that for you anyway).

set logging redirect
set logging file ~/Desktop/gdbFinder.txt
set logging on
break CFPreferencesCopyAppValue
commands 1
silent
if $rdi
if $rsi
print-object [$rdi stringByAppendingFormat:@";%@",$rsi]
else
print-object [$rdi stringByAppendingString:@";--noDomain--"]
end
end
continue
end
break CFPreferencesCopyValue
commands 2
silent
if $rdi
if $rsi
print-object [$rdi stringByAppendingFormat:@";%@",$rsi]
else
print-object [$rdi stringByAppendingString:@";--noDomain--"]
end
end
continue
end
break CFPreferencesCopyMultiple
commands 3
silent
if $rdi
if $rsi
print-object [[$rdi description] stringByAppendingFormat:@";;%@",$rsi]
else
print-object [[$rdi description] stringByAppendingString:@";;--noDomain--"]
end
end
continue
end
run

This does several things. First we tell gdb to save logging to a file instead of printing it to the terminal. This will speed things up considerably, though the Finder will still run quite a bit slower with gdb tracing it than it would otherwise. Then we set breakpoints (where gdb will catch the execution and can do interesting things) in the functions where the Finder goes to get it's preferences. Then we tell gdb that any time it stops at one of those places, it should print out the setting name and domain and continue execution so we aren't stuck waiting all day. It's also got some if statements that should help avoid some problems I had during my testing where gdb would have problems printing things out, and then stop waiting for input, while the Finder looked like it had hung up. If you get the spinning beachball at any point for more than a few seconds, go to gdb and you'll probably see the prompt has come up (gdb). Just enter a 'c' (for continue) and hit return, and things should get back on the road.

Ready, Set, Run GDB

In the terminal (you've already quit the Finder, RIGHT???), type (all one line, sorry it wraps here):

gdb -x ~/Desktop/gdbPrefsCmds.txt /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder

This says to gdb to load up the Finder's executable, and then run the commands in the file you created. Since the last of those commands is 'run', the Finder will launch (slowly) after you run the command. Great.

This is where you want to sit around for a while and wait. Do some stuff in the Finder. Maybe burn a disc, or create an archive or use QuickLook or something. Whatever you want. Maybe open the preferences window to make sure it checks all the preference settings that are in the GUI too. Whatever. Take a while (I let mine run for a day or two), and then come back here to proceed.

Go ahead and quit the Finder, and then type quit in gdb to exit it as well.

Finish Up

Now you'll want to do things with the preferences you've found right. Well, I lied a little above. The first thing to actually do is to open the gdbFinder.txt file (on your Desktop unless you haven't been following my directions) up in a text editor and search for a double semicolon ;;. This is because there's a way that and app can get multiple preferences at once, and I was too lazy to make them print consistently with everything else, so you'll want to remove them from your file before you sort it. The Finder does this only one place that I'm aware of (with label names and colors), but you may run into more of them. Just cut and paste the whole block (should be surrounded by parentheses and have a domain after it) into another file for safe keeping, then save your removals and close the file.

Then do the following in the terminal:

sort -u -o ~/Desktop/FinderPrefs.txt ~/Desktop/gdbFinder.txt

This just says to sort gdbFinder.txt removing duplicates and saving the result to FinderPrefs.txt. Then open that up in a text editor.

You'll see a long list of preferences and domains, but there will be a few extra lines of gdb output in there as well. As a rule, if a line doesn't have a semicolon, it's not a preference and you can delete it. Most such lines also have a space in them (which is an easy way to find them and get rid of them if you want to do so all at once), though some preference keys have spaces in them too.

Anyway, you're almost ready to be turned loose, except you may be wondering about all the domains "kCFPreferencesCurrentApplication" and "kCFPreferencesAnyApplication" you see are. After all, aren't domains supposed to be more like com.apple.finder? Well, for more details, you can read the documentation, but for some reason, the app doesn't always call for preferences with its domain explicitly. Please don't ask me why, I don't know. kCFPreferencesCurrentApplication is equivalent to whatever the application's domain is, and kCFPreferencesAnyApplication is the NSGlobalDomain (-g option in defaults).

Anyhow, now you've got a long list of preferences. Most of them probably have a GUI. It's up to you to find the ones that don't and figure out what they do. After all, that's why I posted this tutorial - so that I don't have to do that last part all by myself! Good Luck!

Always running Apps

Okay, you say, so that's great as far as the Finder goes. But what about the Dock. I can't quit the Dock, and when I try to run a second copy of it in gdb to get the prefs, it just quits right away.

Well, it turns out that there's a Mac OS X service called LaunchServices that is responsible for making sure there's always a copy of the Dock (or SystemUIServer) running. It turns out there's a command line control for LaunchServices that lets you do most whatever you want. So you can, for example

launchctl unload /System/Library/LaunchAgents/com.apple.Dock.plist

To kill the Dock. And then when you're done with gdb, you'll need to

launchctl load /System/Library/LaunchAgents/com.apple.Dock.plist
launchctl start com.apple.Dock.agent

Though you may find that after unloading the bundle, the Dock won't auto restart (if you, say, kill it) until after you've rebooted your computer. But you don't kill your Dock that often, right?

So, now you're really set, and I can happily send you on your way and remind you to send me any any really cool ones you find, and to let others know as well, either through Secrets or Mac OS X Hints.

Contact The ArcticMac