为啥这个键盘拦截内核扩展不起作用?

Posted

技术标签:

【中文标题】为啥这个键盘拦截内核扩展不起作用?【英文标题】:Why doesn't this keyboard intercepting kernel extension work?为什么这个键盘拦截内核扩展不起作用? 【发布时间】:2011-01-03 19:24:22 【问题描述】:

我的开发者伙伴们!我非常希望至少你们中的一些人不会被这个问题包含的文本数量吓到(我只是尽我所能尽可能人性化地进行描述)。 :)

致那些认为我问这个问题是为了编写恶意软件或其他东西的人。我想编写一个应用程序,允许用户在操作系统完成启动后选择要启动的应用程序。整个想法是允许用户在操作系统完成启动之前通过按下先前绑定到应用程序的热键来选择这些应用程序。例如,用户打开他的 Mac,键入 SMTV 并离开,当系统完成启动我的应用程序时,恢复输入并启动 Safari、Mail、Tweetie 和 Vuze。我是 SO 的新手,但我尽我所能通过回答他们的问题来帮助他们——我想我可以期待同样的回报。检查我的个人资料和活动,然后开始对恶意软件大喊大叫。

这个问题是问题Is it possible to recover keyboard input that was done while Mac OS was starting up?的后续问题。

在Pekka's advice 的指导下,我偶然发现了 Christian Starkjohann 的文章Intercepting Keyboard Events,其中描述了他和Objective Development team 如何成功地将 iBook 的 CDROM 弹出键从 F12 重新分配为 Shift+F12。主要部分是他们实际上拦截键盘事件,这正是我所需要的。最后,Christian 写这篇文章正是为了像我这样的开发人员使用 iJect 的想法作为类似功能的原型。

首先,我决定创建一个简单的内核扩展来简单地将用户的键盘输入记录到/var/log/kernel.log。我已经在 XCode 中启动了一个新的通用内核扩展项目,按照 Mac Dev Center's Kernel Extension Concepts 中的 Hello Kernel: Creating a Kernel Extension With Xcode 教程的说明创建了一个 Hello World 项目,然后用取自 iJect 源的代码填充它。结果如下:

TestKEXT.c

#include <sys/systm.h>
#include <mach/mach_types.h>


extern int HidHackLoad(void);
extern int HidHackUnload(void);


kern_return_t MacOSSCKEXT_start (kmod_info_t * ki, void * d) 
    return HidHackLoad() == 0 ? KERN_SUCCESS : KERN_FAILURE;



kern_return_t MacOSSCKEXT_stop (kmod_info_t * ki, void * d) 
    return HidHackUnload() == 0 ? KERN_SUCCESS : KERN_FAILURE;

HIDhack.h

#ifdef __cplusplus
extern "C" 
#endif

#include <mach/mach_types.h>
#include <sys/systm.h>

 extern int HidHackLoad(void);
 extern int HidHackUnload(void);

#ifdef __cplusplus

#endif

#include <IOKit/system.h>
#include <IOKit/assert.h>
#include <IOKit/hidsystem/IOHIDSystem.h>


class HIDHack : public IOHIDSystem 
public:
 virtual void keyboardEvent(unsigned   eventType,
          /* flags */            unsigned   flags,
          /* keyCode */          unsigned   key,
          /* charCode */         unsigned   charCode,
          /* charSet */          unsigned   charSet,
          /* originalCharCode */ unsigned   origCharCode,
          /* originalCharSet */  unsigned   origCharSet,
          /* keyboardType */     unsigned   keyboardType,
          /* repeat */           bool       repeat,
          /* atTime */           AbsoluteTime ts);

 virtual void keyboardSpecialEvent(unsigned   eventType,
           /* flags */        unsigned   flags,
           /* keyCode  */     unsigned   key,
           /* specialty */    unsigned   flavor,
           /* guid */         UInt64     guid,
           /* repeat */       bool       repeat,
           /* atTime */       AbsoluteTime ts);
;

HIDHack.cpp

#include "HIDHack.h"


static void *oldVtable = NULL;
static void *myVtable = NULL;


int HidHackLoad(void) 
 IOHIDSystem *p;
 HIDHack *sub;

 if (oldVtable != NULL) 
  printf("###0 KEXT is already loaded\n");
  return 1;
 
 if (myVtable == NULL) 
  sub = new HIDHack();
  myVtable = *(void **)sub;
  sub->free();
 
    p = IOHIDSystem::instance();
    oldVtable = *(void **)p;
    *(void **)p = myVtable;

 printf("###1 KEXT has been successfully loaded\n");

    return 0;


int HidHackUnload(void) 
 IOHIDSystem *p;

    if (oldVtable != NULL) 
        p = IOHIDSystem::instance();
  if (*(void **)p != myVtable) 
   printf("###2 KEXT is not loaded\n");

   return 1;
  
        *(void **)p = oldVtable;
        oldVtable = NULL;
    

 printf("###3 KEXT has been successfully unloaded\n");

 return 0;


void HIDHack::keyboardEvent(unsigned   eventType, unsigned   flags, unsigned   key, unsigned   charCode, unsigned   charSet, unsigned   origCharCode, unsigned   origCharSet, unsigned   keyboardType, bool repeat,
       AbsoluteTime ts) 
 printf("###4 hid event type %d flags 0x%x key %d kbdType %d\n", eventType, flags, key, keyboardType);

    IOHIDSystem::keyboardEvent(eventType, flags, key, charCode, charSet, origCharCode, origCharSet, keyboardType, repeat, ts);


void HIDHack::keyboardSpecialEvent(   unsigned   eventType,
          /* flags */        unsigned   flags,
          /* keyCode  */     unsigned   key,
          /* specialty */    unsigned   flavor,
          /* guid */         UInt64     guid,
          /* repeat */       bool       repeat,
          /* atTime */       AbsoluteTime ts) 
 printf("###5 special event type %d flags 0x%x key %d flavor %d\n", eventType, flags, key, flavor);

 IOHIDSystem::keyboardSpecialEvent(eventType, flags, key, flavor, guid, repeat, ts);

生成的内核扩展被 kextload/kextunload 程序成功加载/卸载,但实际上并没有拦截任何键盘事件。我已经尝试做很多事情来让它工作,但没有任何错误或其他问题,因为我无法谷歌搜索任何有用的东西并寻求你的帮助。

【问题讨论】:

嘿斯万特!非常感谢您帮助我学习语法。我不是母语人士,尽管我正在尽我最大的努力不要太激怒别人...... ;) 据我所见,恶意软件的呼声是没有根据的(即使想编写恶意软件的人可能会问类似的问题)。任何这种深度级别的程序可能都是恶意软件,这就是为什么操作系统的工作是在安装任何类似的程序之前询问用户。当然,Ivan,它有可能以这种方式拦截密码对话框的击键 - 我自己没想到 - 这可能是它不起作用的原因。也许操作系统足够聪明,可以防止拦截正是出于这个原因。 也许你是对的......尽管据我所知,整个技巧是基于 C++ 语言将地址类表替换为方法的能力,这在任何情况下都应该有效。 【参考方案1】:

问题不在于您如何覆盖现有的 IOHIDSystem 实例。效果很好。

问题是当IOHIKeyboard被打开时,会传递一个回调函数给IOHIDSystem来处理事件。回调是IOHIDSystem的一个静态私有函数,叫做_keyboardEvent:

    success = ((IOHIKeyboard*)source)->open(this, kioserviceSeize,0,
                (KeyboardEventCallback)        _keyboardEvent, 
                (KeyboardSpecialEventCallback) _keyboardSpecialEvent,
                (UpdateEventFlagsCallback)     _updateEventFlags);

回调然后调用IOHIDSystem实例中的keyboardEvent函数:

    self->keyboardEvent(eventType, flags, key, charCode, charSet,
                            origCharCode, origCharSet, keyboardType, repeat, ts, sender);

它不调用十参数一,它是虚拟的(并且您正在覆盖)。相反,调用的是 11 参数非虚拟参数。因此,即使您尝试覆盖第 11 个参数,它也不会起作用,因为调用永远不会通过 vtable。

【讨论】:

以上是关于为啥这个键盘拦截内核扩展不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥显示键盘后我的滚动不起作用?

为啥这个在 Common Lisp 中漂亮地打印宏扩展的宏不起作用?都有哪些替代工具?

为啥软键盘在对话框片段内的 Web 视图中不起作用?

UIScreenEdgePanGestureRecognizer 在自定义键盘扩展中不起作用

修改php.ini不起作用是为啥

为啥我的电脑开机密码设置了,不起作用?