使用 kIOHIDOptionsTypeSeizeDevice 时不会阻止击键,并且仍会传递给 OS
Keystrokes are not blocked when using kIOHIDOptionsTypeSeizeDevice and are still passed to the OS
我的目标是使用 IOHID 阻止击键到达 OS(由于其他原因不能使用 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
此代码 运行s 并设法从 OS 全局查看和打印所有击键,但似乎 kIOHIDOptionsTypeSeizeDevice
被忽略,因为击键仍在传递到 mac OS.
编辑:
将 IOReturn result =
添加到代码中会暴露错误 -536870207
,该错误会转换为 kIOReturnNotPrivileged
。然后我将 Xcode 方案更改为 root 并且能够阻止键盘键。
这引出了下一个问题,我如何将此代码添加到显然 运行 没有 root 权限的 Developer ID 应用程序?
很高兴我们将您的 HID 问题追踪到评论中的权限问题。
要在 production/deployment 中以 root 身份 运行 编码,您需要将单独的工具设置为 Launch Daemon,并将其配置为在您的主程序时按需启动应用向它发送一条 IPC(通常是 XPC)消息。
您有 2 个主要选项来首先设置启动守护程序:
- 在您的 .app 中嵌入守护程序二进制文件,并从您的应用程序代码中调用
SMJobBless
以将其安装到系统中。这将要求用户输入管理员密码。
- 一个安装程序 .pkg,它将您的守护程序二进制文件放置在某个固定位置(例如
/Library/Application Support/YourAppName/
下方)并将相应的 launchd plist 放置在 /Library/LaunchDaemons
. 中
请注意,在设置启动守护程序时,您需要相当小心避免打开安全漏洞。 This series of articles 是关于该做什么和该避免什么的很好的深入指南。
另请注意,SMJobBless
不允许在 App Store 上使用,因此此类功能对于在那里分发的应用程序来说根本不可能。
我的目标是使用 IOHID 阻止击键到达 OS(由于其他原因不能使用 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
此代码 运行s 并设法从 OS 全局查看和打印所有击键,但似乎 kIOHIDOptionsTypeSeizeDevice
被忽略,因为击键仍在传递到 mac OS.
编辑:
将 IOReturn result =
添加到代码中会暴露错误 -536870207
,该错误会转换为 kIOReturnNotPrivileged
。然后我将 Xcode 方案更改为 root 并且能够阻止键盘键。
这引出了下一个问题,我如何将此代码添加到显然 运行 没有 root 权限的 Developer ID 应用程序?
很高兴我们将您的 HID 问题追踪到评论中的权限问题。
要在 production/deployment 中以 root 身份 运行 编码,您需要将单独的工具设置为 Launch Daemon,并将其配置为在您的主程序时按需启动应用向它发送一条 IPC(通常是 XPC)消息。
您有 2 个主要选项来首先设置启动守护程序:
- 在您的 .app 中嵌入守护程序二进制文件,并从您的应用程序代码中调用
SMJobBless
以将其安装到系统中。这将要求用户输入管理员密码。 - 一个安装程序 .pkg,它将您的守护程序二进制文件放置在某个固定位置(例如
/Library/Application Support/YourAppName/
下方)并将相应的 launchd plist 放置在/Library/LaunchDaemons
. 中
请注意,在设置启动守护程序时,您需要相当小心避免打开安全漏洞。 This series of articles 是关于该做什么和该避免什么的很好的深入指南。
另请注意,SMJobBless
不允许在 App Store 上使用,因此此类功能对于在那里分发的应用程序来说根本不可能。