In the previous article, I stepped through the normal launch of a notarized app. This can best be summarised in the following sequence of events, as seen in the unified log of macOS 10.14.5 (elapsed times in decimal seconds):
- 0.00 Finder double-click action
- 0.01 IconServices with GUI
- 0.02 Preliminary setup with libsystem_info.dylib
- 0.02 amfid trust evaluation
- 0.04 loginwindow, IconServices again
- 0.05 first app mention to retrieve user by ID
- 0.07 LaunchServices trust evaluation and check-in
- 0.07 studentd appears!
- 0.09 app appearance is sorted out, then a little later its preferences are loaded by cfprefsd
- 0.18 TCC sequence, with trust evaluation, internal code signing error registering ticket
- 0.19 TCC checking entitlements for hardened runtime
- 0.24 app running own code (e.g. integrity checks)
Next, I’m going to look at three different cases in which a notarized app is launched:
- When it is being launched for the first time from a new path (no quarantine flag set).
- When gross changes have been made to its Resources folder.
- When small changes have been made to the app’s Mach-O code.
Launching from a new location
The first differences appear very early on, with the log entry
fileproviderd Foundation begin using plugin
just 0.001 second after the last of the double-click actions. This triggers a series of log entries about com.apple.PlugInKit and com.apple.CloudDocs.MobileDocumentsFileProvider which result in secinitd calling a trust evaluation, a successful AppSandbox request, and culminate in a strange error:
LaunchServices Non-fatal error enumerating at <private>, continuing: Error Domain=NSCocoaErrorDomain Code=260 "The file “PlugIns” couldn’t be opened because there is no such file." UserInfo={NSURL=PlugIns/ -- file:///Users/hoakley/Documents/testZ/PermissionScanner.app/Contents/, NSFilePath=/Users/hoakley/Documents/testZ/PermissionScanner.app/Contents/PlugIns, NSUnderlyingError=0x7fc5779eaff0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
After that, LaunchServices parses the app’s Info.plist file and MobileInstallation data, and there are two entries recording a UNIX error exception 8. amfid’s trust evaluation occurs 0.08 second later than in a ‘normal’ launch, following which the sequence of entries is much the same as above, including studentd’s unwelcome guest appearance.
This additional sequence, which adds trust evaluations for several systems including secinitd, tccd, syspolicyd and lsd, appears to be responsible for the different designated requirements seen in apps which are running from a novel path, even without any quarantine flag being set. Although the additional entries seen here are relatively few in number, they involve a surprising number of different sub-systems.
Changed Resources
For these runs, the apps were launched from their new location rather than /Applications, and their log entries started similarly to the first time that they were run from that location, with
fileproviderd Foundation begin using plugin
However, subsequent entries show that macOS recognised that they had only just been run from that same path, and reported
plugin /System/Library/PrivateFrameworks/CloudDocs.framework/PlugIns/com.apple.CloudDocs.MobileDocumentsFileProvider.appex is still valid
They then breezed through the remaining steps with trust evaluations from only secinitd, tccd and lsd. None of these reported an error, even through the main TCC sequence. It’s only when the app performed its own integrity check that a -67054 error was reported, and the app terminated itself as a result of its own integrity check.
Changed code
Doctoring an app by changing a couple of bytes in its code is a dangerous game, as it is quite likely to result in instability. In my tests, I managed to get away without imposing detectable damage to either of the apps, both of which were killed by the kernel because of their altered executable code.
In one app, 0.07 second after the last double-click action, the kernel wrote in the log:
CODE SIGNING: process 27856[EFIcienC]: rejecting invalid page at address 0x1058ed000 from offset 0x1d000 in file "/Users/hoakley/Documents/testZ/EFIcienC.app/Contents/MacOS/EFIcienC" (cs_mtime:1562515149.249509931 != mtime:1562567123.172648863) (signed:0 validated:0 tainted:1 nx:0 wpmapped:0 dirty:1 depth:2)
The app was crashed immediately as a result. Previous trust evaluations for secinitd and tccd had completed without error. This trust evaluation was probably being performed for lsd, or could perhaps have been the AMFI kernel extension at work.
The other app suffered a similar fate, with the kernel entry reading
CODE SIGNING: process 27886[PermissionScanne]: rejecting invalid page at address 0x1049dd000 from offset 0x4000 in file "/Users/hoakley/Documents/testZ/PermissionScanner.app/Contents/MacOS/PermissionScanner" (cs_mtime:1561533368.589568615 != mtime:1562567237.919781454) (signed:1 validated:1 tainted:1 nx:0 wpmapped:0 dirty:0 depth:0)
However, although that process was then crashed immediately, the crash reporting mechanism behaved strangely and two further copies of the doctored process had to be crashed before order resumed.
To compare the log entries in 10.14.5 against those of 10.14.4, look at those I quoted in this earlier article, and here for 10.14.2. Code signature checks have been evolving considerably in Mojave, and log entries are becoming significantly less informative.