I'm currently working on a method of detecting apps that might have global access to user keystrokes. Among the currently available APIs(CGEventTap, IOHIDManager) I've also encountered [NSEvent addGlobalMonitorForEventsMatchingMask:handler], which upon further inspection seems to call into InstallEventHandler( found in HIToolbox):
extern OSStatus
InstallEventHandler(
EventTargetRef inTarget,
EventHandlerUPP inHandler,
ItemCount inNumTypes,
const EventTypeSpec * inList,
void * inUserData,
EventHandlerRef * outRef)
An example of this would be(requires approving the binary in System Preferences -> Accesibility):
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h>
//Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/CarbonEvents.h
extern void RunApplicationEventLoop(void);
OSStatus globalKeyPress(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData)
{
NSEvent *anEvent = [NSEvent eventWithEventRef:theEvent];
printf("%s", anEvent.characters.UTF8String);
fflush(stdout);
return CallNextEventHandler(nextHandler, theEvent);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
EventTypeSpec espec = {
.eventClass = kEventClassKeyboard,
.eventKind = kEventRawKeyUp
};
EventHandlerUPP handlerFunction = NewEventHandlerUPP(globalKeyPress);
OSStatus err = InstallEventHandler(GetEventMonitorTarget(), handlerFunction, 1, &espec, NULL, NULL);
if (err) {
NSLog(@"InstallEventHandler: %d", err);
}
RunApplicationEventLoop();
}
return 0;
}
The documentation for GetEventMonitorTarget states that:
The event monitor target is a special event target used to
* monitor user input events across all processes. When an event
* handler is installed on the event monitor target, the Carbon
* Event Manager examines the EventTypeSpec for user input event
* types, such as mouse-down, mouse-up, key-down, and so forth. It
* then requests that the WindowServer make copies of any of these
* events that are sent to any process, and deliver them to the
* current process also...
I'm guessing that some process must be managing a global list of apps registered for these events.
How can I inspect the list(ideally by registering a callback, but polling is also fine) of processes which have installed one of these global event handlers?