Pergunta

Meus colegas desenvolvedores! Espero muito que pelo menos alguns de vocês não fiquem assustados com a quantidade de texto que essa questão contém (eu simplesmente fiz o meu melhor para ser tão descritivo quanto humanamente possível). :)

Para aqueles que pensam que fiz essa pergunta para escrever malware ou algo assim. Quero escrever um aplicativo que permitirá que os usuários selecionem aplicativos a serem iniciados após o início do SO a lançamento. Toda a idéia é permitir que o usuário selecione esses aplicativos antes do lançamento do sistema operacional, pressionando as teclas de quente anteriormente para os aplicativos. Por exemplo, o usuário liga seu Mac, tipos SMTV e desaparece, quando o sistema terminar de lançar meu aplicativo recupera a entrada e inicia Safari, Mail, Tweetie e Vuze. Eu sou novo para isso, mas faço o possível para ajudar os outros, respondendo às perguntas deles - acho que posso esperar o mesmo em troca. Verifique meu perfil e minha atividade e depois disso comece a gritar sobre malware.

Esta pergunta é um acompanhamento da pergunta É possível recuperar a entrada do teclado que foi feita enquanto o Mac OS estava iniciando?.

Guiado por O conselho de Pekka, Eu tropecei em um artigo Interceptar eventos de teclado por Christian Starkjohann que descreve como ele e o Equipe de desenvolvimento objetivo conseguiu reatribuir a chave de ejeção CDROM do iBook de F12 para Shift+F12. A parte principal é que eles realmente interceptado Eventos de teclado, que é o que eu preciso. No final, Christian escreveu este artigo exatamente para desenvolvedores como eu usarem a idéia de identificar como um protótipo para funcionalidade semelhante.

Para começar, decidi criar uma extensão simples do kernel para simplesmente registrar a entrada do teclado do usuário para /var/log/kernel.log. Eu iniciei um novo projeto genérico de extensão do kernel no Xcode, segui as instruções do Olá Kernel: Criando uma extensão do kernel com Xcode tutorial encontrado em Conceitos de extensão do kernel do Mac Dev Center Para criar um projeto Hello World e depois encher com o código retirado de fontes IJEDO. Aqui estão os resultados:

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);
}

A extensão do kernel resultante é carregada/descarregada com sucesso por programas KextLoad/KextUnload, mas na verdade não intercepta nenhum dos eventos do teclado. Tentei fazer muitas coisas para funcioná -lo, mas sem erros ou outros problemas da maneira que não consigo pesquisar nada útil e peço sua ajuda.

Foi útil?

Solução

O problema não é com a forma como você está substituindo a instância existente do IoHidSystem. Isso funciona muito bem.

O problema é que, quando o iohikeyboard é aberto, ele é aprovado uma função de retorno de chamada para o sistema IOHIDSYMO para processamento de eventos. O retorno de chamada é uma função privada estática do IoHidsystem, chamada _KeyboardEvent:

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

O retorno de chamada chama a função do teclado no iohidsystem:

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

Ele não chama o parâmetro dez, que é virtual (e que você está substituindo). Em vez disso, o que está sendo chamado é o 11 parâmetro não virtual. Portanto, mesmo se você tentasse substituir o parâmetro 11, ele não funcionaria, pois a chamada nunca passa pela vtable.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top