Почему эта клавиатура, перехватывающая расширение ядра, не работает? - PullRequest
5 голосов
/ 11 января 2010

мои коллеги разработчики! Я очень надеюсь, что, по крайней мере, некоторые из вас не будут напуганы количеством текста, содержащегося в этом вопросе (я просто сделал все возможное, чтобы описательный текст был настолько гуманным, насколько это возможно). :)

Тем, кто думает, что я задал этот вопрос, чтобы написать вредоносную программу или что-то в этом роде. Я хочу написать приложение, которое позволит пользователям выбирать приложения для запуска после того, как ОС завершит запуск. Основная идея заключается в том, чтобы позволить пользователю выбирать эти приложения ДО завершения загрузки ОС, нажимая горячие клавиши, ранее привязанные к приложениям. Например, пользователь включает свой Mac, набирает SMTV и уходит, когда система заканчивает запуск, мое приложение восстанавливает ввод и запускает Safari, Mail, Tweetie и Vuze. Я новичок в SO, но я делаю все возможное, чтобы помочь другим, отвечая на их вопросы - я думаю, что могу ожидать того же в ответ. Проверьте мой профиль и мою активность, и после этого начните кричать о вредоносных программах.

Этот вопрос является продолжением вопроса Можно ли восстановить ввод с клавиатуры, выполненный во время загрузки Mac OS? .

Руководствуясь советом Пекки , я наткнулся на статью Перехват событий клавиатуры Кристиана Старкджоханна, в которой описывается, как ему и команде объективного развития удалось переназначение клавиши извлечения CDROM iBook с F12 на Shift + F12. Основная часть в том, что они на самом деле перехватили события клавиатуры, что мне и нужно. В конце Кристиан написал эту статью именно для таких разработчиков, как я, чтобы использовать идею iJect в качестве прототипа для аналогичной функциональности.

Для начала я решил создать простое расширение ядра, чтобы просто записывать ввод с клавиатуры пользователя на /var/log/kernel.log. Я запустил новый проект Generic Kernel Extension в XCode, следуя инструкциям Hello Kernel: создание расширения ядра с помощью Xcode , которое можно найти в Концепции расширения ядра Центра разработки Mac для создания проект 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, но фактически не перехватывает ни одно из событий клавиатуры. Я пытался сделать много вещей, чтобы заставить его работать, но без каких-либо ошибок или других проблем с ним, так что я не могу гуглить что-нибудь полезное и прошу вашей помощи.

1 Ответ

2 голосов
/ 26 января 2011

Проблема не в том, как вы переопределяете существующий экземпляр IOHIDSystem. Это прекрасно работает.

Проблема в том, что при открытии IOHIKeyboard передается функция обратного вызова в систему IOHIDS для обработки событий. Обратный вызов - это статическая частная функция IOHIDSystem, которая называется _keyboardEvent:

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

Затем обратный вызов вызывает функцию keyboardEvent в экземпляре IOHIDSystem:

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

Он не вызывает десять параметров один, который является виртуальным (и который вы переопределяете). Вместо этого вызывается не виртуальный параметр с 11 параметрами. Поэтому, даже если вы попытаетесь переопределить параметр 11, он не будет работать, так как вызов никогда не проходит через vtable.

...