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

5
尊敬的开发者们!我非常希望至少有一些人不会被这个问题所吓倒(我尽力用最详细的描述方式)。:)
对于那些认为我是在问如何编写恶意软件的人。我想编写一个应用程序,允许用户选择在操作系统启动完成后要启动的应用程序。整个想法是允许用户通过预先绑定到应用程序的热键在操作系统启动之前选择这些应用程序。例如,用户打开他的Mac电脑,输入SMTV并离开,当系统启动完成后,我的应用程序恢复输入并启动Safari、Mail、Tweetie和Vuze。我是SO的新手,但我尽力通过回答他们的问题来帮助别人-我认为我可以期待得到同样的帮助。请查看我的个人资料和活动记录,然后再开始大喊大叫关于恶意软件的事情。
这个问题是是否可能在Mac OS启动时恢复键盘输入?的后续问题。

Pekka的建议的指导下,我偶然发现了Christian Starkjohann的一篇文章截取键盘事件, 文章描述了他和Objective Development团队是如何成功地将iBook的CDROM弹出键从F12重新分配到Shift + F12的。主要部分是他们实际上截取了键盘事件,这正是我所需要的。最后,Christian撰写了这篇文章,正是为了像我这样的开发人员能够将iJect的想法作为类似功能的原型。

首先,我决定创建一个简单的内核扩展程序,将用户的键盘输入记录到/var/log/kernel.log。我在XCode中开始了一个新的通用内核扩展项目,并按照Hello Kernel: Creating a Kernel Extension With Xcode教程中在Mac Dev Center's Kernel Extension Concepts找到的指示创建了一个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程序成功加载/卸载,但实际上没有拦截任何键盘事件。我尝试了很多方法来让它工作,但由于没有错误或其他问题,我无法在谷歌上找到任何有用的信息,所以需要您的帮助。


嗨,Svante!非常感谢你帮我检查语法。虽然我不是母语使用者,但我会尽力不让人们感到过分烦恼... ;) - Ivan Karpan
1
从我所看到的情况来看,恶意软件的呼声是没有根据的(尽管想编写恶意软件的人可能会问类似的问题)。任何在这个深度上运行的程序都可能是恶意软件,这就是操作系统在安装任何类似程序之前要询问用户的原因。当然,伊万,用这种方式拦截密码对话框的按键输入是可能的 - 我自己没有想到这一点 - 这可能是它不起作用的原因。也许操作系统足够聪明,可以防止拦截,正是出于这个原因。 - Pekka
也许你是对的...尽管据我所知,整个技巧基于C++语言能够替换类地址表到方法的能力,这在任何情况下都应该有效。 - Ivan Karpan
1个回答

2

问题并不在于您如何覆盖现有的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);

它不会调用具有十个参数的虚拟函数(也就是你正在重写的函数)。相反,被调用的是具有十一个非虚拟参数的函数。因此,即使你尝试覆盖这个具有十一个参数的函数,它也不起作用,因为调用从未通过虚表进行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接