Monthly Archives: July 2018
Crash Reports are essential, if often opaque, guides to problems in your app that every developer has to deal with from time to time.
For small enterprise developers like myself, commercial analytics aren’t really an option. And while there are open source alternatives like UKCrashReporter, I’ve been lucky enough to suffer relatively few crashes and typically just ask users to manually submit crash reports (stored in ~/Library/Logs/DiagnosticReports) as and when they occur.
Choosing a method of collecting crash reports is one thing, but scouring them for useful information is quite another, and the reams of data often included can seem a little overwhelming.
On a good day, your crash report may include a direct reference to the method or function that’s caused the issue, like this one from an early version of my troubleshooting app DetectX Swift:
On other days, though, you might get presented with nothing but a bunch of unhelpful memory addresses:
It’s at this point that you need to enlist the help of the built-in command-line tool atos. In the remainder of this post, we’re going to learn what it does and how to use it to find the line in our source code that caused the crash.
How does atos help?
The beauty of
atos is that it will give you a reference to the source file, function and even line number in your code where the crash occurred. Given that it can do this for a crash that occurred on another user’s machine that you can’t reproduce locally and don’t have access to, this seems like nothing short of magic.
Here’s an example of what
atos might provide from a crash log like the one above:
ViewController.checkReg(isCli:) (in DetectX Swift) (ViewController.swift:4042)
The output from
atos tells me the name of the Swift source file, the name of the function and — here’s the truly wonderous part — even the line number: 4042.
This is all part of the magic of symbollication, which is such an esoteric word I’m not even going to try to spell it again :-). Instead, let’s just get down to the practical nitty-gritty of how to get this kind of data out of our users’ crash reports.
How to use atos
The method is fairly simple and goes like this:
1. Create a folder called “CrashWork”.
2. Go to Xcode’s ‘Organizer’, and right-click on the archive of the version of the app that crashed.
Choose “Show in Finder”. That takes you to the
.xcarchive file in Finder, from which right-click again and choose “Show Package Contents” to open the package:
3. Click on the
.dSYM file and make a copy of it. Switch back to your CrashWork folder and paste the copy in there. Grab a copy of the same version of your app that crashed (you can also get that from the Products folder in the .xcarchive package, if you don’t have one handy elsewhere) and place it in the same folder:
Important: Be sure you’re working with the same version of your app as the user;
atos needs that to match up the addresses in the crash report, and any other version will produce incorrect results or an error.
4. It’s now time to head on over to the Terminal.
cd into your CrashWork directory:
5. The format of the command is generally going to be like this (see man atos for options):
atos -o <path to executable> -arch x86_64 -l <load address> <address>
<path to executable> is the path all the way to the Mach-O binary in your app bundle in the CrashWork folder, so the path in my example looks like this:
atos -o DetectX\ Swift.app/Contents/MacOS/DetectX\ Swift -arch x86_64 -l <load address> <address>
You need to be careful to get the
<load address> and the
<address> the right way around. They are the reverse order of what you see in the crash log:
Now my complete example looks like this:
atos -o DetectX\ Swift.app/Contents/MacOS/DetectX\ Swift -arch x86_64 -l 0x10a10e000 0x000000010a16a601
6. Hitting ‘return’ produces the magic:
Earlier this year, Digita Security’s Patrick Wardle took apart a cross-platform backdoor trojan he nicknamed ”ColdRoot’. Wardle was retro-hunting possible malware by searching for apps on VirusTotal that access Apple’s TCC privacy database.
For those unfamiliar, TCC.db is the database that backs the System Preferences > Security & Privacy | Accessibility preferences pane and which controls, among other things, whether applications are allowed access to the Mac’s Accessibility features. Of interest from a security angle is that one of the permissions an app with access to Accessibility can gain is the ability to simulate user clicks, such as clicking “OK” and similar buttons in authorisation dialogs.
One particular comment in Wardle’s article caught my eye:
there is no legitimate or benign reason why non-Apple code should ever reference [the TCC.db] file!
While in general that is probably true, there is at least one good reason I can think of why a legitimate app might reference that file: reading the TCC.db used to be the easiest way to programmatically retrieve the list of apps that are allowed Accessibility privileges, and one reason why a piece of software might well want to do that is if it’s a piece of security software like DetectX Swift.
If your aim is to inform unsuspecting users of any changes or oddities in the list (such as adware, malware or just sneaky apps that want to backdoor you for their own ends), then reading TCC.db directly is the best way to get that information.
Just call me ‘root’
Since Apple put TCC.db under SIP protection subsequent to my reports on Dropbox’s user-unfriendly behaviour, apps are no longer able to write to the database via SQL injection. An app with elevated privileges can, however, still read the database. A sufficiently privileged app (or user) can output the current list with:
sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db 'select * from access'
On my machine, this indicates that there are twelve applications in the System Preferences’ Privacy pane, all of which are enabled save for two, namely LaunchBar and Safari:
We can see from the output that LaunchBar and Safari both have the ‘allowed’ integer set to ‘0’ (the middle of the three values in “0|0|1”) , whereas all the other apps have it set to ‘1’ (“0|1|1”).
It’s not clear to me why reading the database should require privileges. Certainly, Apple have provided APIs by which developers can test to see if their own apps are included in the database or not. The AXIsProcessTrusted() global function, for example, will return whether a calling process is a trusted accessibility client (see AXUIElement.h for related functions):
However, there remains a case (as I will demonstrate shortly) where developers may well need to know whether apps other than their own are, or are not, in the list. Moreover, there doesn’t seem to be any obvious vulnerability in allowing read access to that data, just so long as the write protection remains, as it does, in place.
The use case for being able to read the TCC.db database is clearly demonstrated by apps like DetectX Swift: security apps that conform to the principle of least privilege, always a good maxim to follow whenever practical. Asking users to grant elevated privileges just to check which apps are in Accessibility is akin to opening the bank vault in order to do an employee head count. Surely, it would be more secure to be able to determine who, if anyone, is in the vault without having to actually take the risk of unlocking the door.
Did they put a CCTV in there?
Without direct access to TCC.db, we might wonder whether there are any other less obvious ways by which we can determine which apps are able to access Accessibility features. There are three possibilities for keeping an eye on bad actors trying to exploit Accessibility without acquiring elevated privileges ourselves, each of which has some drawbacks.
1. Authorisation dialogs
The first is that we can read the property list that records all invocations of the Accessibility authorisation dialog, without admin rights:
defaults read ~/Library/Preferences/com.apple.universalaccessAuthWarning.plist
That gives us a list of all the apps that macOS has ever thrown the ‘some.app would like permission to control your computer’ dialog alert for, along with an indication of the user’s response (1= they opened sys prefs, 0= they hit Deny).
This list could prove useful for identifying adware installers that try to trick users into allowing them into Accessibility (looking at you, PDFPronto and friends), but it’s main drawback is that the list is historical and doesn’t indicate the current denizens of Accessibility. It doesn’t tell us whether the apps in the list are currently approved or not, only that each app listed once presented the user with the option of opening System Preferences and what the user chose to do about it at that time.
2. Distributed Notifications
The second option is that developers can register their apps to receive notifications when any other application gets added or removed from the list with code similar to this:
DistributedNotificationCenter.default().addObserver(self, selector: #selector(self.accessibilityChanged), name: NSNotification.Name.init("com.apple.accessibility.api"), object: nil)
This ability was in fact added to DetectX Swift in version 1.04.
However, Apple hasn’t made this API particularly useful. Although we don’t need elevated privileges to call it, the NSNotification returned doesn’t contain a userInfo dictionary – the part in Apple’s notification class that provides specific information about a notification event. All that the notification provides is news that some change occurred, but whether that was an addition or a deletion, and which app it refers to, is not revealed:
Even so, notification of the change is at least something we can present to the user. Alas, this is only useful if the app that’s receiving the notification is actually running at the time the change occurs. For an on-demand search tool like DetectX Swift, which is only active when the user launches it, the notification is quite likely to be missed.
It would be nice, at least, if Apple would provide a more useful notification or an API for developers wishing to keep their users safe and informed. The lack of a userInfo dictionary in the notification was apparently reported as a bug to Apple several years back, but I suppose it could always use a dupe.
3. AppleScript – everyone’s favourite ‘Swiss Army Knife’
There is, as it turns out, a third way to reliably get exactly all the info we need about who has access to Accessibility. We can use AppleScript to return a complete list of apps in Accessibility that are enabled. Note that the output of this unprivileged script, shown here in the results pane of Script Debugger, returns a more readable version of the same list of apps we obtained from the privileged sqlite3 query of TCC.db, minus Safari and LaunchBar, which as we previously noted were not enabled:
Fantastic! There’s just one problem. While this AppleScript does not require blanket elevated privileges to run – in short, it doesn’t require an administrator password – it does need to be run by an app that is itself already in the list of Accessibility apps. If you have a script runner like Apple’s Script Editor, or third-party tools like Script Debugger or FastScripts, already approved in Accessibility, then you can run it without authorisation. It’s also worth noting that the script relies on launching and quitting System Preferences, which it attempts to do as quietly as possible.
As for DetectX Swift, I may consider adding something like this to a future version as an option for users who are happy to add DetectX to the list of apps in Accessibility.
Have your own tips about accessing TCC.db? Let us know in the Comments!
Featured pic: Can’t STOP Me by smilejustbcuz