The Endpoint Security Event ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD notification appears to be broken in two ways.
Issue has also been submitted via Feedback Assistant: FB11812980
Background
The ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD Endpoint Security (ES) event is declared in the header file, ESTypes.h In ESMessage.h one find more info about its ES message type: es_event_btm_launch_item_add_t ...and a comment that says it is a:
"notification for launch item being made known to background task management. This includes launch agents and daemons as well as login items added by the user, via MDM or by an app"
This seem to be an ideal notification to detect persistence events (e.g. the installation of a launch or login item). Unfortunately at this time (on macOS 13.0.1 (22A400)) it appears to be broken.
Issues
1️⃣ First issue is that an ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD is delivered for every installed launch agent/daemon whenever a new launch agent/daemon is added (not just for the new launch item)
Steps to reproduce:
From terminal run (via root/sudo): eslogger btm_launch_item_add
Install a launch agent/daemon ...either run an installer creates a launch item or just manually copy an launch item plist: % cp ~/Desktop/com.test.plist ~/LaunchAgents/com.test.plist
This will trigger a deluge of ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD notifications - one for every installed launch agent/daemon ...not just the one that was added 😅
2️⃣ The second issue is that an ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD is not delivered for login items.
Steps to reproduce:
From terminal run (via root/sudo): eslogger btm_launch_item_add
Install a login item ...for example run an installer that installs a login item
Note that no ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD notification is delivered even though the ESMessage header file notes that a notification should be delivered for "...login items added by the user, via MDM or by an app"
Note macOS will detect the login item installation and generate an alert to the user. 🤷🏻♂️
Post
Replies
Boosts
Views
Activity
DNS queries (at least those generated by macOS's dig) when sent over TCP, are prefixed with a 2-byte length.
Observe this by firing up a network monitor (e.g., WireShark), and then generating a DNS query over TCP:
% dig @8.8.8.8 <some domain> +tcp
"Normal" DNS queries (sent over UDP) are not prefixed in this manner.
% dig @8.8.8.8 <some domain>
Though WireShark has no problem with these differences, this poses a problem for libresolv. Specifically the dns_parse_packet function will return NULL for DNS queries over TCP. 😤
A simple workaround is to pass dnsPacket +2 to dns_parse_packet() ...but this feels dirty? 😅 Ideally libresolv would be made a little more robust, able to handle both packet styles.
I've encountered some applications that are validly signed (as reported by codesign dvv / SecStaticCodeCheckValidity), but the notarization ticket has been revoked:
% codesign -v revoked.app
% stapler validate revoked.app
The ticket for revoked.app has been revoked. Gatekeeper will prevent it from running.
% spctl -a -vvv -t install revoked.app
revoked.app: notarization indicates this code has been revoked
What is the recommended / supported approach to perform this check programmatically? In other words, replicate stapler validate or spctl -a -vvv -t install but with APIs). Ideally an API that returns errSecCSRevokedNotarization
I can extract the app's code signing information, cdhashes or notarization ticket (from Contents/CodeResources). Was toying with SecAssessmentTicketLookup and SecTrustEvaluateWithError but so far, no luck.
And SecRequirementCreateWithString(CFSTR("notarized")... and SecStaticCodeCheckValidity just returns errSecCSReqFailed which yes is correct, but doesn't tell us that the ticket was revoked.
I've added a listener block for camera notifications. This works as expected: the listener block is invoked then the camera is activated/deactivated.
However, when I call CMIOObjectRemovePropertyListenerBlock to remove the listener block, though the call succeeds, camera notifications are still delivered to the listener block.
Since in the header file it states this function "Unregisters the given CMIOObjectPropertyListenerBlock from receiving notifications when the given properties change." I'd assume that once called, no more notifications would be delivered?
Sample code:
#import <Foundation/Foundation.h>
#import <CoreMediaIO/CMIOHardware.h>
#import <AVFoundation/AVCaptureDevice.h>
int main(int argc, const char * argv[]) {
AVCaptureDevice* camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
OSStatus status = -1;
CMIOObjectID deviceID = 0;
CMIOObjectPropertyAddress propertyStruct = {0};
propertyStruct.mSelector = kAudioDevicePropertyDeviceIsRunningSomewhere;
propertyStruct.mScope = kAudioObjectPropertyScopeGlobal;
propertyStruct.mElement = kAudioObjectPropertyElementMain;
deviceID = (UInt32)[camera performSelector:NSSelectorFromString(@"connectionID") withObject:nil];
CMIOObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const CMIOObjectPropertyAddress addresses[]) {
NSLog(@"Callback: CMIOObjectPropertyListenerBlock invoked");
};
status = CMIOObjectAddPropertyListenerBlock(deviceID, &propertyStruct, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), listenerBlock);
if(noErr != status) {
NSLog(@"ERROR: CMIOObjectAddPropertyListenerBlock() failed with %d", status);
return -1;
}
NSLog(@"Monitoring %@ (uuid: %@ / %x)", camera.localizedName, camera.uniqueID, deviceID);
sleep(10);
status = CMIOObjectRemovePropertyListenerBlock(deviceID, &propertyStruct, dispatch_get_main_queue(), listenerBlock);
if(noErr != status) {
NSLog(@"ERROR: 'AudioObjectRemovePropertyListenerBlock' failed with %d", status);
return -1;
}
NSLog(@"Stopped monitoring %@ (uuid: %@ / %x)", camera.localizedName, camera.uniqueID, deviceID);
sleep(10);
return 0;
}
Compiling and running this code outputs:
Monitoring FaceTime HD Camera (uuid: 3F45E80A-0176-46F7-B185-BB9E2C0E436A / 21)
Callback: CMIOObjectPropertyListenerBlock invoked
Callback: CMIOObjectPropertyListenerBlock invoked
Stopped monitoring FaceTime HD Camera (uuid: 3F45E80A-0176-46F7-B185-BB9E2C0E436A / 21)
Callback: CMIOObjectPropertyListenerBlock invoked
Callback: CMIOObjectPropertyListenerBlock invoked
Note the last two log messages showing that the CMIOObjectPropertyListenerBlock is still invoked ...even though CMIOObjectRemovePropertyListenerBlock has successfully been invoked.
Am I just doing something wrong here? Or is the API broken?
Recently Apple deprecated the NXFindBestFatArch, used to find the most compatible / appropriate slice in a universal/fat binary.
However its replacement macho_best_slice is broken and will return EBADARCH for any non-Apple binary 😓
The issue seems to be that the dyld3::GradedArchs::grade method does not take into the account the nuances of the CPU sub types of type *_ALL. Namely that any CPU with a more specific sub type (e.g. CPU_SUBTYPE_ARM64E) can also still execute code compiled with a CPU sub type of *_ALL (e.g. CPU_SUBTYPE_ARM64_ALL).
All the details (+code, +debugging, +disassembly) posted at: https://objective-see.org/blog/blog_0x80.html.
Also, hrmm!?:
Users are reporting that 3rd-party software that leverages Apple's Network Extensions (such as LuLu and Windows Defender) are causing networking issues after upgrading to macOS 15.
However as such products were working seamlessly on macOS 14.* and nothing in the code of these products changed between then and now, this would point to bug in macOS.
Users have mentioned the following work arounds:
Disabling the internal (macOS) firewall
Upgrading to macOS 15.1 beta
More info about the issues and these "workarounds" here and here..
Looking for any guidance / insight / technical details from Apple, as users are (understandably) blaming these tools and their developers 😭
Of course if there are updated APIs or some other changes in macOS 15 that developers should consider / conform to, to ensure compatibility that'd be great to know too!