In the ongoing war against malware, new threats pop up almost every day — but they still fall under the same general categories. For this analysis, we’ve taken a closer look at the keylogger malware family. Based on the data obtained from our anti-malware technology Moonlock Engine, we’ve laid out the distribution of keyloggers, their core modules, anti-analysis techniques, and more.
Are keyloggers dangerous?
Only a few keylogger families are designed solely for keyboard capture, and their open-source code is written as PoC (proof of concept) without any malicious intent. However, the harsh reality is that keyloggers are more commonly created for surveillance and data theft purposes.
Keyloggers can be used to steal the following data:
- Typed passwords for personal email or social media accounts
- Credit card numbers and PINs when the user makes online payments
- Private conversations with close contacts
So, a user might have a well-configured VPN, visit trusted websites with HTTPS connections, and provide information only to trusted payment systems. Yet a keylogger will still capture this information at the OS level.
Plus, the majority of keyloggers come with an additional feature: they transmit the collected information to third parties over the network. This can pose a significant threat, depending on what information the keylogger has been transmitting and how long it has been active.
Keyloggers usually are installed and operate without the user’s knowledge. And they can remain hidden in the operating system. For example, Keystroke Spy Keylogger can notify third parties via email when a user types specific words or phrases, as well as hide itself.
Keyloggers in the wild
Over the period from March 1, 2023, to August 31, 2023, Moonlock Lab detected a large number of keylogger samples on user devices. The research was conducted using the Moonlock Engine, which is integrated into CleanMyMac X. Here’s what we have found.
Additionally, when looking at detections broken down by country, we get the following distribution.
Note that the current dataset shows that in France, there is a significant spike in keylogger activity (15.1%) compared to other European countries. But if we count the number of detected keyloggers as a percentage of the total number of users in related countries, Ukraine surpasses France to take first place. This is quite evident due to the geopolitical situation.
Keylogger development and functionality
From a development perspective, the average keylogger typically includes the following core modules:
- Keyboard interception module – how the keylogger uses MacOS System API
- Information storage module – how the keylogger uses files or local databases
- Information transmission module – how the keylogger uses http requests, email, raw tcp/udp sockets, ftp, messengers, etc.
- Persistence mechanism – how the keylogger operates within the system, often including features for hiding
Additional functionalities may include:
- A browser history recording module that targets the most popular browsers like Safari, Firefox, Chrome
- A screenshot creation module, in most cases, is triggered by the typing of specific words
- An input/output buffer interception module that is activated when the user copies text
- A license/payment module related to B2C/B2B keylogger solutions
The more functionalities a keylogger has, the closer it gets to being considered spyware. Keyloggers are only one element of spyware, with other features including screen recording, audio and video capture, geolocation tracking, theft of files or passwords, and others.
Keyloggers by language
From a programming languages perspective, the following distribution data emerged among the 22 keylogger families available in the Moonlock Lab collection.
It’s worth noting that most keyloggers are developed in Objective-C due to its ease of implementing the necessary functionality, as it is a native programming language.
Keylogger macho binary modification
Macho is a binary format for executable files in MacOS, and Moonlock Lab had approximately 420 keylogger macho samples for analysis. The general distribution of keyloggers by family can be observed below.
If the number of samples in the keylogger family exceeds 15, it may indicate that the keylogger is widespread. Therefore, a few conclusions can be drawn.
The less likely scenario is that the keylogger code is auto-generated from a few templates, which is rare for the mentioned Keylogger families. The more likely scenario is that the keylogger is being updated periodically, indicating ongoing development.
Keylogger anti-analysis techniques
As if the world of cybersecurity wasn’t already complex enough, researchers attempting to study malware are often forced to contend with anti-analysis techniques. These are methods that are employed to intentionally complicate the analysis of malware.
In the majority of analyzed keylogger families, there were no anti-analysis techniques. However, there were some exceptions:
- BlueBlood keylogger – some malware samples are packed with UPX (Ultimate Packer for executables)
- Python keyloggers – some are packed using PyInstaller (which bundles a Python application and all its dependencies into a single package)
However, this is quickly resolved. For UPX unpacking, users can execute the following command using the UPX utility:
$> upx -d file-in-upx
You can also utilize the following open-source tools for unpacking and analyzing PyInstaller:
- PyInstaller Extractor
- C++ Python bytecode disassembler and decompiler (supports python3.11)
- A cross-version Python bytecode decompiler (tested with old versions)
Keylogger library/API usage
As a result of analyzing 22 detected families, the following keyboard interception mechanisms at the MacOS API level can be identified.
Note: the code examples below are implemented for malware analysts to investigate the behavior of keyloggers.
MacOS API: CGEvent
The easy-to-use CGEvent API works on macOS 10.4 and above. All you need to do is create a tap, add a run loop events source, and enable a tap. This allows your application to listen and log global events with a predefined CGEventMask
mask.
Application requirements
Works from | Sandbox, non-sandbox | |
Permissions | Accessibility Input Monitoring | |
Min macOS | 10.4 |
There are also some system restrictions for macOS:
- The current process must be running as the root user.
- Access to assistive devices must be enabled. In OS X v10.4, you can enable this feature using System Preferences > Universal Access > Keyboard view.
Documentation
CGEvent Apple Developer Documentation
Code example (PoC)
import Cocoa
final class KeyLogger {
private let eventTap: CFMachPort
init?() {
// 1. Create mask for keyDown and keyUp events
let mask = CGEventMask((1 << CGEventType.keyDown.rawValue) | (1 << CGEventType.keyUp.rawValue))
// 2. Define function for handling CGEvents
func callback(
proxy: CGEventTapProxy,
type: CGEventType,
event: CGEvent,
refcon: UnsafeMutableRawPointer?
) -> Unmanaged<CGEvent>? {
// 8. Convert CGEvent to NSEvent and log info in console
let cgEvent = NSEvent(cgEvent: event)
// 9. Convert CGEvent to NSEvent and log info in console
cgEvent.flatMap {
NSLog("User pressed: \($0.char), modifiers: \($0.modifiersFlag), keyDown: \($0.isKeyDown)")
}
return Unmanaged.passRetained(event)
}
// 3. Create Tap events with mask and defined callback handler
guard let eventTap = CGEvent.tapCreate(
tap: .cghidEventTap,
place: .headInsertEventTap,
options: .defaultTap,
eventsOfInterest: mask,
callback: callback,
userInfo: nil
) else {
return nil
}
// 4. Save reference to eventTap
self.eventTap = eventTap
// 5. Create a run loop event source.
let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
// 6. Add the source to the appropriate run loop.
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes)
// 7. Enable tap events
CGEvent.tapEnable(tap: eventTap, enable: true)
}
}
MacOS API: NSEvent
In MacOS, applications are allowed to track copies of global events that other applications in the system have access to. This applies to keyboard key presses and mouse/trackpad actions alike.
NSEvent stands out from CGEvent due to its Swift-like syntax and straightforward API. Developers don’t need to worry about the RunLoop; all that’s left is to manage the event-handling process.
Application requirements
Works from | Sandbox, non-sandbox | |
Permissions | Accessibility | |
Min macOS | 10.6 |
Documentation
NSEvent Apple Developer Documentation
Code example (PoC)
import Cocoa
final class KeyLogger {
private var observer: Any?
init() {
// 1. Create global monitor for keyDown and keyUp events
// and save a reference to an event handler object.
observer = NSEvent.addGlobalMonitorForEvents(
matching: [.keyUp, .keyDown]
) { event in
// 2. Print event details in console
NSLog("User pressed: \(event.char), modifiers: \(event.modifiersFlag), keyDown: \(event.isKeyDown)")
}
}
}
MacOS API: IOHIDManager
IOKit and IOHIDManager enable developers to access hardware and receive mouse and keyword events. API gives you the ability to listen to generic events from a concrete source, like a keyboard, a gamepad, a keypad, or even a joystick. For a full list, check the list of all kHIDUsage cases.
Application requirements
Works from | Sandbox (Required additional entitlements field com.apple.security.device.usb ), non-sandbox | |
Permissions | Input Monitoring | |
Min macOS | 10.5 |
Documentation
IOHIDManager Apple Developer Documentation
Additional information about USB entitlements can be found here.
Code example (PoC)
import Cocoa
import IOKit
import IOKit.hid
final class KeyLogger {
private let hidManager: IOHIDManager
init() {
// 1. Create hidManager
self.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
// 2. Set matching criteria, in out case generic devices
let matchingDict = [
kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop,
kIOHIDDeviceUsageKey: kHIDUsage_GD_Keyboard
]
// 3. Assign matching devices dictionary to HID manager
IOHIDManagerSetDeviceMatching(hidManager, matchingDict as CFDictionary)
// 4. Setup callback functions with handler call
let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
IOHIDManagerRegisterInputValueCallback(hidManager, { (context, result, sender, inputValue) in
guard let context = context else { return }
let inputMonitor = Unmanaged<KeyLogger>.fromOpaque(context).takeUnretainedValue()
inputMonitor.handleInputValue(result, inputValue: inputValue)
}, context)
// 5. Open the manager and devices to start receiving events.
IOHIDManagerOpen(hidManager, IOOptionBits(kIOHIDOptionsTypeNone))
// 6. Schedule manager work with the run loop to receive events
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
}
private func handleInputValue(_ result: IOReturn, inputValue: IOHIDValue) {
// 7. Check if the result is success
guard result == kIOReturnSuccess else { return }
// 8. Extract element and keyCode value from input
let element = IOHIDValueGetElement(inputValue)
let keyCode = IOHIDElementGetUsage(element)
// 9. Skip handling invalid keys
guard !(keyCode < 4 || keyCode > 231) else { return }
// 10. Define if event is keyDown (1) or keyUp (0).
let isKeyDownEvent = IOHIDValueGetIntegerValue(inputValue) == 1
// 10. Find keyboard value from predefined dictionary
let char = Self.keysDict[keyCode] ?? ""
// 11. Log event to a console
NSLog("User pressed: \(char), keyDown: \(isKeyDownEvent)")
}
}
extension KeyLogger {
static let keysDict: [UInt32: String] = [
4 : "a",
...
231 : "RightCommand"
]
}
Here is an open source example.
MacOS API: IOHIKeyboard (Deprecated)
IOHIKeyboard is a deprecated API inside kernel framework. Functionality is limited to macOS 10.15.2 and above.
In this case, developers have to create a KEXT (kernel extension) and distribute it on the target Mac, which allows it to record keyboard inputs. Developers can track information about event type (key up or down), selected characters, flags (Command, Option, Alt, etc.), and even the time of the keyboard interaction event.
Documentation
IOHIKeyboard Apple Developer Documentation
Open-source code example
An example of the IOHIKeyboard code can be found here.
MacOS API: RegisterEventHotKey
Apple also provides the developer API for keyboard shortcuts. It can be used only as a combination of keys and modifier flags, like Command, Shift, or Option.
The special RegisterEventHotKey
command can intercept event system shortcuts, like Command + A
for selecting all. As soon as each shortcut is registered, API can’t be used for logging separate key down events as in previous examples.
Open-source code example
An example of the RegisterEventHotKey code can be found here (not a keylogger project), as well as how to add user-customizable global keyboard shortcuts (hotkeys) to your macOS app in minutes.
Python lib: Pynput
The current implementation of pynput utilizes Quartz.kCGEvent.
Documentation
Handling the keyboard — pynput 1.7.6 documentation
- “Supported values are: darwin_intercept A callable taking the arguments (event_type, event), where event_type is Quartz.kCGEventKeyDown or Quartz.kCGEventKeyUp, and event is a CGEventRef.”
- “The package pynput.keyboard contains classes for controlling and monitoring the keyboard.“
Code example (PoC)
import pynput.keyboard
...
pynput.keyboard.Listener(on_press=self.process_key_press)
Keylogger distribution
It’s worth noting that keylogger solutions are quite popular around the world, both in the B2C and B2B segments, offered under licenses. The provided statistics confirm this.
Purchased license keyloggers
Among the 22 keylogger families analyzed, 15 are distributed as paid subscriptions that users or employers must manually install on their computers. Such subscriptions often include access to a centralized monitoring system.
For example:
- Spyrix KeyLogger is positioned as a solution for family or employee monitoring.
- iMonitor Mac Keylogger is exclusively positioned as an employee monitoring solution.
It’s interesting that among advertisements for paid keyloggers, there are offers for monitoring one’s own children.
Such examples include:
- Kidlogger is developed by SafeJKA S.R.L., a company registered in Moldova. The company has projects under the Rohos domain. The website encourages users to “choose the package and start using our parental control service today.”
- Refog KeyLogger has rather aggressive advertising. Their website reads, “Computers are everywhere, including your kids’ bedroom.”
Open-source keyloggers
Open-source solutions rank next in the statistics. They are written as PoCs or ready for use on a free basis. It’s common among malicious actors to modify open-source code for themselves.
For example:
- Swift keylogger – A keylogger for mac written in Swift using HID
- Keylogger OS X – A very simple keylogger for self-quantifying on Mac OS X
- Keylogger – A no-frills keylogger for macOS
Trojan keyloggers
Occupying last place in the distribution of keyloggers are those that infiltrate users’ devices as trojans or are installed through a backdoor.
A prime example is trojan Ventir, which was quite well-known in 2014:
- Name: MacOS:Ventir-A / Application.MAC.OSX.KextLogger.H
- sha256: 59539ff9af82c0e4e73809a954cf2776636774e6c42c281f3b0e5f1656e93679
- VirusTotal First Submission: 2014-09-09
This is a modular trojan that, upon infiltrating the operating system, installs an open-source keylogger called LogKext. LogKext was created in 2007.
KextLogger (LogKext) is described as a “product of FSB software” on GitHub. It’s important to note that this is not related to the Russian FSB but rather bears a coincidental name resemblance.
Sources reference:
- Google Code Archive – Long-term storage for Google Code Project Hosting.
- GitHub – SlEePlEs5/logKext: An update to fsb’s logKext tool. Runs on 10.9 Mavericks!
Keylogger code encapsulation
VirusTotal is a service that scans files for malware and is capable of establishing dependencies between files (such as what was contained within an archive), extracting readable information from binary files, displaying the type of malware, and much more. And if you’ve ever seen malware with a high number of detections on VirusTotal, you also may have noticed that antivirus companies name them in different ways.
In most cases, this is associated with independent research and nomenclature. However, there are situations where different types of malware share similar parts. In the case of keyloggers, one can observe a certain encapsulation of code from one family to another or notice the borrowing of specific characteristic elements. This phenomenon can be observed for many years within a particular family.
Keylogger examples with details
Let’s take the following keyloggers as examples: AoboKeylogger, LogKext, Observer Keylogger, and Amac Keylogger.
LogKext, developed back in 2007, is utilized in AoboKeylogger.
- VirusTotal sha256: cb463fb496b86f562861bda367d1e9af50e93805efcab4a6fbcbcb72ecb2f82a
- VirusTotal Name: LogKextInstaller
- The string in the binary file contains a reference to LogKext and AoboKeylogger: “/aobokeylogger/build/KeyLogger.build/Release/LogKextInstaller.build”.
- sting in binary file: “logKext-10%d.pkg”
- Virus Total First Submission: 2015-01-20
The provided sample of the Observer Keylogger below contains references to LogKext. However, the package in which Observer Keylogger was distributed is called amac.app (Amac Keylogger). It turns out that the website with the address https://www[.]observer[.]pw is currently promoting the Observer Keylogger for Android, featuring content in the Russian language and links to a Russian technical support phone number.
- Virustotal sha256: 7d51e2cac40b606fcae81432dd4fb55a4b1461da18baebf67a791963c8d2f900
- VirusTotal Compressed Parents: amac-keylogger-std.dmg
- sting in binary file: “Keylogger observer must run as root.“
- sting in binary file: “/Library/Preferences/com.fsb.logKext“. This is a reference to LogKext and the developer company.
- Virus Total First Submission: 2014-12-29
The provided example demonstrates that the open-source LogKext is encapsulated within three other keyloggers that are marketed as paid solutions.
User interactions with keyloggers
Modern MacOS keyloggers require specific permissions to capture keystrokes, namely Accessibility and/or Input Monitoring access. This is managed by the TCC (Transparency, Consent, and Control) mechanism, which controls applications’ access to system resources.
A keylogger will request user permissions before its initial attempt to capture keystrokes. So let’s consider some examples from the Keylogger Library/API Usage section, how they interact with the user through the GUI, and how they demonstrate different behaviors.
Examples:
Application: CGEventSandbox.app (MacOS API: CGEvent)
- Environment: MacOS Ventura 13.5.1
- Requires Accessibility and Input Monitoring access.
- A Keylogger using CGEvent will request access to Accessibility but not to Input Monitoring. The developer must additionally implement functionality that requests permissions for Input Monitoring. If such functionality does not exist, the user must manually grant access.
Note that if you revoke permissions from the application and do not restart the process, the keylogger will continue recording keyboard inputs until the process is restarted.
Application KeyloggerGlobalMonitor.app (MacOS API: NSEvent)
- Environment: MacOS Ventura 13.5.1
- Requires Accessibility access.
- When using NSEvent, the developer should additionally implement functionality to request Accessibility permissions. If such functionality is absent, a user must manually grant access.
Application IOHIDManager.app (MacOS API: IOHIDManager)
- Environment: MacOS Ventura 13.5.1
- Requires Input Monitoring access.
- When making the initial API call using IOHIDManager, the keylogger will automatically request access to Input Monitoring.
Keylogger detection
The detection of a keylogger can be divided into 2 approaches: static and dynamic.
Static detection
In this case, a great choice for quick file scanning would be YARA, a tool for detecting malware using predefined rules. For example, we can create a generic YARA rule built on samples from various keylogger families that will search for keystroke-capturing functionality. With its help, we can scan the system.
This method is also valid for keylogger searching in large databases of files for analysis. However, it may have a high false positive detection rate.
YARA example
rule KeyLogger_gen
{
meta:
description = "Find something similar to a keylogger"
strings:
// Self examples (IOHIDManager.app)
// Self examples (CGEventSandbox.app)
// Self examples (KeyloggerGlobalMonitor.app)
$a0 = "CodingKeys"
$a1 = "KeyDow"
$a2 = "IOHIDManager"
$a3 = "CGEvent"
$a4 = "NSEvent"
// sha256: 647f4e77c1f26f9f8ac5ce1eabe074bc0db0602252efd5fce306024b291653ac (Elite Keylogger)
$b0 = "Cocoa"
$b1 = "keyCode"
$b2 = "keyEventWithType"
// sha256: e58a1ea0d86fe7402572df8db5539cee7de6d64432d6d827008e0276c9b2c121 (PerfectKeyLogger)
// sha256: 83ca6fbb0cec6aabc47fe529f58d5a43f9d86bb34f5edab7eefc9067f350ddf8 (Amac Keylogger)
// sha256: a1eaa3085be6e3df038b2581b8524acb26174fb9e87fbf00cf82fee30b0f1b97 (BlueBlood)
// sha256: 2764afdba8a4d4e0867fb1c3a4fb70d5b10f520e69197029566d4152135a06ce (Spyrix)
// sha256: 7c794a9b32845ca71f2f1744a36dbec65fbb92a42348eae86bfd0c549586b370 (MacOSKeylogger)
// sha256: 19960403787517576105f9c54ac12da96d8ed4ecec6789e00d390a948f24ba48 (KidLogger)
// sha256: 98e3c260f17a01adea1a9a165efa6ef552be5ab2efc03f2dfb7cb380a70d25ea (Spytech Keystroke Spy)
// sha256: 1996ddc461861c59034fae84a4db45788d9f3b3e809389d36800d194dab138bd (BackTrack)
// sha256: ec12b3e3bc3f421ec473c675a882945ec4edf43d6efcb35f52ae338daf060e64 (iMonitor)
$c0 = "Carbon"
$c2 = /[Kk]eyCode/
$c3 = "CGEventTapCreate"
$c4 = "RegisterEventHotKey"
$c5 = "KeyboardEvent"
$c6 = "KeyboardMouse"
$c7 = "keyDown"
$c8 = "keyUp"
$c9 = "CGEventKeyboardGetUnicodeString"
// sha256: fa4831e971439e223a83257c6ec9a81a881a9281d8e0929e160799449a8586be (LogKext)
$d0 = "IOHIKeyboard"
$d1 = "KeyloggerEngineIOService"
// sha256: 7d51e2cac40b606fcae81432dd4fb55a4b1461da18baebf67a791963c8d2f900 (ObserverKeylogger)
$d2 = "keystrokes_"
// sha256: 71940ec0d8a2be2ead9f0d16d3ec135173033983e1cd9a83390094e2a1641817 (Keyboard Spy Logger)
// sha256: ad68d941b4ccae8ef811118e54f6321dcea8456a95127a283f62af31f3cf7875 (Telepath)
// sha256: ad9705340e0b2e1265946a5587fbf681e3b515dd32430990b6b7ff08a262ddb0 (Refog)
$e0 = "Cocoa"
$e1 = /[Kk]eyCode/
$e2 = /[Kk]eyDown/
$e3 = /[Kk]eyUp/
$e4 = "NSEvent"
$e5 = "onKeyEvent"
// sha256: c143c003769fbf784348b33a6c7ff46798cf42fef8a243d0ecc0a610c39b94ba (AgentBob)
$f0 = "Carbon"
$f1 = "keyboardAsyncKeyDown"
$f2 = "Event_KeyDown"
$f3 = "Keyboard.KeyName"
$f4 = "HandleKeyDown"
// sha256: cfa59bc92c42baf695b512aaa50b286a959ca57fe240a20512a5c729d0706650 (SwiftKeylogger)
$g0 = "IOHIDManagerCreate" //IOKit.hid
$g1 = "IOHIDManagerRegisterInputValueCallback"
// sha256: b4e6adc4edfe2ba67c76f3855c14190a0144f2023e8f97c68e804b5ac6ee88b3 (KeyloggerOSX)
$h0 = "keyCodeToReadableString"
$h1 = "CGEventTapCreate"
// sha256: 0d781da3a2a85ca8d58531a4e5741add21c63ac42f90ef2eb08615a8e25d637e (PythonKeyLogger)
$i0 = "pynput"
$i1 = "keyboard"
$i2 = "Listener"
// sha256: a8595b9819be7325b7aa6002e04425632dc500126594782a90983921b03e4ccc (PythonKeyLogger)
$k0 = "NSEvent"
$k1 = "NSKey"
condition:
($a0 and $a1 and 3 of ($a*)) or
3 of ($b*) or 3 of ($c*) or 2 of ($d*) or
4 of ($e*) or 3 of ($f*) or 2 of ($g*) or
2 of ($h*) or 3 of ($i*) or 2 of ($k*)
}
If we need to achieve the most accurate detection of a specific keylogger without false positives, we should create a rule targeted at more distinctive elements.
Here’s an example for Telepath.
Another YARA example
rule KeyLogger_Telepath_1
{
meta:
description = "Telepath rule"
strings:
// sha256: ad68d941b4ccae8ef811118e54f6321dcea8456a95127a283f62af31f3cf7875 (Telepath)
$t0 = "Telepath"
$t1 = "keyCode"
$t2 = "keyDown"
$t3 = "mouseMoved"
$t4 = "CaptureSession"
condition:
all of them
}
Usage example
$> yara -rs target-rule.yar ./KeyLogger/
KeyLogger_Telepath_1 ./KeyLoggers/Telepath/ad68d941b4ccae8ef811118e54f6321dcea8456a95127a283f62af31f3cf7875.macho
...
0xde72:$t0: Telepath
0xfd44:$t1: keyCode
0xd619:$t2: keyDown
0xd5be:$t3: mouseMoved
...
0x10d1b:$t4: CaptureSession
Dynamic detection
In most cases, a keylogger will perform two main actions:
- Gain access to Accessibility and/or Input Monitoring via TCC
- Consistently record or transmit keystroke information
These are two actions that can be done for dynamic detection.
TCC Keylogger IoC
One option is to run the developed examples from the Keylogger Library/API Usage section. After granting all the necessary permissions to the test applications, examine the records that appear in the TCC.db database and the tccd logs in the Console application, particularly fields 1, 2, and 4 from the access table in the TCC.db database.
- service name
- client that access to service
- client_type: Bundle Identifier(0) or an absolute path(1)
- auth_value: denied(0), unknown(1), allowed(2), limited(3).
In this article by Keith Johnson, you will find a lot of valuable information about TCC.
Application: CGEventSandbox.app (MacOS API: CGEvent)
- Environment: MacOS Ventura 13.5.1
- Requires Accessibility and Input Monitoring access.
- After granting access, there will be 2 entries in TCC.db:
$> sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "select * from access;" | grep -i CGEventSandbox
kTCCServicePostEvent|com.bilous.CGEventSandbox|0|2|4|2|??||0|UNUSED||0|1694007093
kTCCServiceListenEvent|com.bilous.CGEventSandbox|0|2|4|1|??||0|UNUSED||0|1694007331
- auth_value: (2) indicates that access has been granted.
- kTCCServicePostEvent is a permission for sending events, including keyboard events.
- kTCCServiceListenEvent is a permission for monitoring events, including keyboard events.
A review of the tccd logs in the Console application will show that during each CGEventSandbox.app launch, there are logs indicating access requests to kTCCServicePostEvent and kTCCServiceListenEvent.
Application: KeyloggerGlobalMonitor.app (MacOS API: NSEvent)
- Environment: MacOS Ventura 13.5.1
- Requires Accessibility permissions
- After granting access, there will be 1 entry in TCC.db
$> sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "select * from access;" | grep KeyloggerGlobalMonitor
kTCCServiceAccessibility|com.bilous.KeyloggerGlobalMonitor|0|2|4|1|??||0|UNUSED||0|1694021492
- kTCCServiceAccessibility allows an application to control a computer
- In the dccd logs, we can observe that two accesses were granted: kTCCServiceListenEvent and kTCCServiceAccessibility
Application: IOHIDManager.app (MacOS API: IOHIDManager)
- Environment: MacOS Ventura 13.5.1
- Requires Input Monitoring permissions
- After granting access, there will be 1 entry in TCC.db
$> sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "select * from access;" | grep IOHIGManager
kTCCServiceListenEvent|com.bilous.IOHIGManager|0|2|4|1|??||0|UNUSED||0|1694025481
- kTCCServiceListenEvent is a permission for monitoring events, including keyboard events
- Logs from tccd display access to kTCCServiceListenEvent
ESF KeyLogger IoC
Any keylogger needs to store keystroke information or immediately transmit it over the network. To detect such behaviors, one can leverage the Apple Endpoint Security Framework (ESF), which enables the monitoring of system events to identify potential malicious activity.
You can view all available events by following the link: es_event_type_t | Apple Developer Documentation.
The tools ESFPlayground and MonitorUI can be used for visualizing ESF events. To test keylogger behavior, simply run any of the developed examples from the Keylogger Library/API Usage section.
Note: Keylogger Library/API Usage examples exhibit the same behavior as they use the same functionality for storing information.
In the illustration, we can see filtered ESF events for the application with PID (Process ID) CGEventSandbox.app: 12119. The information storage in a file happens immediately after processing a keyboard event, so we observe a distinctive “flood” in ESF logs during short intervals.
Let’s consider an option to store information in an SQLite database instead of a file. To do this, make some modifications to the KeyloggerGlobalMonitor.app application to log keyboard strokes into the database.
The behavior is similar to the previous example.
The described keylogger behavior is quite primitive. To reduce detection, keylogger developers can make it more sophisticated. For example, by taking the following steps:
- Add an internal cache buffer within the application
- Record keyboard keystrokes into the buffer
- After a certain time interval, unload the content from the buffer into local storage or cloud storage
The verdict on keyloggers
While the process of keylogging can be used for legitimate purposes, and many keylogger products do operate with the users’ consent, they are commonly used for nefarious purposes by bad actors.
Keyloggers can be one of many weapons in the arsenal of increasingly sophisticated cybercriminals. Not only are unsolicited keyloggers a danger to your private information online, but they can serve as a gateway to more dangerous malware. As always, the best course of action is to arm yourself with knowledge and take the proper steps to stay safe.
Co-authors
Oleksii Yasynskyi, Moonlock Lab
Oleksandr Bilous, Setapp
Links
- UPX main page: UPX • Main Page
- GitHub PyInstaller Extractor: GitHub – extremecoders-re/pyinstxtractor: PyInstaller Extractor
- GitHub Python bytecode disassembler and decompiler: GitHub – zrax/pycdc: C++ python bytecode disassembler and decompiler
- GitHub Python bytecode decompiler: GitHub – rocky/python-uncompyle6: A cross-version Python bytecode decompiler
- Apple Developer Documentation – TapCreate: tapCreate(tap:place:options:eventsOfInterest:callback:userInfo:) | Apple Developer Documentation
- Apple Developer Documentation – AddGlobalMonitorForEvents: addGlobalMonitorForEvents(matching:handler:) | Apple Developer Documentation
- Apple Developer Documentation – IOHIDManager: IOHIDManager.h | Apple Developer Documentation
- Apple Developer Documentation – com.apple.security.device.usb: com.apple.security.device.usb | Apple Developer Documentation
- Apple Developer Documentation – IOHIKeyboard: IOHIKeyboard | Apple Developer Documentation
- Apple Developer Documentation – pynput: Handling the keyboard — pynput 1.7.6 documentation
- YARA documentation: Welcome to YARA’s documentation! — yara 4.3.2 documentation
- Keith Johnson, “A deep dive into macOS TCC.db“: A deep dive into macOS TCC.db | Rainforest QA
- Apple Developer Documentation, Endpoint Security: Endpoint Security | Apple Developer Documentation
- The Mitten Mac, “The ESF Playground“: The ESF Playground
- Apple Developer Documentation, es_event_type_t: es_event_type_t | Apple Developer Documentation