macos - 使用 kIOHIDOptionsTypeSeizeDevice 时,击键不会被阻止,并且仍会传递到操作系统

标签 macos keyboard hid iokit

我的目标是使用 IOHID 阻止击键到达操作系统(由于其他原因无法使用 CGEvent)。根据kIOHIDOptionsTypeSeizeDevice的文档:

Used to open exclusive communication with the device. This will prevent the system and other clients from receiving events from the device.

#import "TestKeys.h"
#import <IOKit/hid/IOHIDManager.h>
#import <IOKit/hid/IOHIDUsageTables.h>

@implementation TestKeys

#define KEYS 2

static void Handle_InputCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value)
{
    IOHIDElementRef elem = IOHIDValueGetElement(value);

    uint16_t scancode = IOHIDElementGetUsage(elem);
    
    if (scancode < 4 || scancode > 231) {
        return;
    }
    
    NSLog(@"Key event received: %d", scancode);
}

static void Handle_DeviceMatchingCallback(void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
    NSLog(@"Connected");
}

static void Handle_RemovalCallback(void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
    NSLog(@"Removed");
}

-(void)start
{
    IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone);
    
    if (CFGetTypeID(manager) != IOHIDManagerGetTypeID()) {
        exit(1);
    }

    int usagePage = kHIDPage_GenericDesktop;
    int usage = kHIDUsage_GD_Keyboard;
    
    CFStringRef keys[KEYS] = {
        CFSTR(kIOHIDDeviceUsagePageKey),
        CFSTR(kIOHIDDeviceUsageKey),
    };
    
    CFNumberRef values[KEYS] = {
        CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usagePage),
        CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usage),
    };
    
    CFDictionaryRef matchingDict = CFDictionaryCreate(kCFAllocatorDefault,
                                                      (const void **) keys, (const void **) values, KEYS,
                                                      &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    for (int i=0; i<KEYS; i++) {
        CFRelease(keys[i]);
        CFRelease(values[i]);
    }
    
    IOHIDManagerSetDeviceMatching(manager, matchingDict);
    CFRelease(matchingDict);
    
    IOHIDManagerRegisterDeviceMatchingCallback(manager, Handle_DeviceMatchingCallback, NULL);
    IOHIDManagerRegisterDeviceRemovalCallback(manager, Handle_RemovalCallback, NULL);
    IOHIDManagerRegisterInputValueCallback(manager, Handle_InputCallback, NULL);
    
    IOHIDManagerOpen(manager, kIOHIDOptionsTypeSeizeDevice);

    IOHIDManagerScheduleWithRunLoop(manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
@end

此代码运行并设法从操作系统全局查看和打印所有击键,但似乎 kIOHIDOptionsTypeSeizeDevice 被忽略,因为击键仍在传递到 macOS。

编辑:IOReturn result = 添加到代码中会暴露错误 -536870207,该错误会转换为 kIOReturnNotPrivileged。然后我将 Xcode 方案更改为 root 并且能够阻止键盘按键。

这引出了下一个问题,如何将此代码添加到显然不以 root 权限运行的开发者 ID 应用程序中?

最佳答案

很高兴我们在评论中将您的 HID 问题追踪到权限问题。

要在生产/部署中以 root 身份运行代码,您需要将一个单独的工具设置为 Launch Daemon ,并将其配置为在您的主应用向其发送 IPC(通常是 XPC)消息时按需启动。

首先,您有 2 个主要选项来设置启动守护进程:

  • 将守护程序二进制文件嵌入到您的 .app 中,并从应用代码中调用 SMJobBless 将其安装到系统中。这将要求用户输入管理员密码。
  • 一个安装程序 .pkg,将您的守护程序二进制文件放置在固定位置(例如在 /Library/Application Support/YourAppName/ 下方),并放置相应的 launchd plist/Library/LaunchDaemons中。

请注意,在设置启动守护程序时,您需要非常小心,以避免打开安全漏洞。 This series of articles是关于该做什么和该避免什么的很好的深入指南。

另请注意,App Store 不允许使用 SMJobBless,因此此类功能对于在那里分发的应用来说根本不可能。

关于macos - 使用 kIOHIDOptionsTypeSeizeDevice 时,击键不会被阻止,并且仍会传递到操作系统,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70673002/

相关文章:

ios - ios 11 beta 7 的 UIKeyboardWillShowNotification 问题

ios - iPhone/iPad 主页按钮的 HID 报告/扫描代码

c - 尝试将蓝牙套接字绑定(bind)到 PSM 17 和 19 在 MeeGo 上返回 EACCESS

java - 如何处理/决定在 KEY_PRESSED 和 KEY_RELEASED 之间输入关键字符的文本字段的速度?

python - OS X 蓝牙编程

ios - os_log : Unexpected value for integer in Console. 应用程序和 `log stream`

ios - 在 native 、phonegap/Cordova 构建的 iOS 应用程序中更改键盘外观

C# - 禁用键盘输入文本框

c# - 为什么我的 GTK# 编译二进制文件(使用 MonoDevelop 创建)不能在 Windows 上运行?

macos - 我可以问 dtrace 启用了哪些探测器吗?