Pregunta

¡Mis compañeros de desarrolladores! Espero mucho que al menos algunos de ustedes no se asusten por la cantidad de texto que contiene esta pregunta (simplemente hice todo lo posible para ser tan descriptivo como sea humanamente posible). :)

A aquellos que piensan que he hecho esta pregunta para escribir malware o algo así. Quiero escribir una aplicación que permita a los usuarios seleccionar aplicaciones que se inicien después de que el sistema operativo finalice el lanzamiento. La idea completa es permitir que el usuario seleccione estas aplicaciones antes de que el sistema operativo terminara de iniciarse presionando las teclas de acceso rápido previamente vinculado a las aplicaciones. Por ejemplo, el usuario enciende su Mac, tipos SMTV y desaparece, cuando el sistema termina de iniciar mi aplicación recupera la entrada y lanza Safari, Mail, Tweetie y Vuze. Soy nuevo en eso, pero hago todo lo posible para ayudar a otros respondiendo sus preguntas, creo que puedo esperar lo mismo a cambio. Revise mi perfil y mi actividad y después de eso comience a gritar sobre malware.

Esta pregunta es un seguimiento de la pregunta. ¿Es posible recuperar la entrada del teclado que se realizó mientras Mac OS comenzaba?.

Guiado por Consejo de Pekka, Me he topado con un artículo Interceptando eventos de teclado por Christian Starkjohann que describe cómo él y el Equipo de desarrollo objetivo logró reasignar la tecla CDROM de iBook de F12 a Shift+F12. La parte principal es que en realidad interceptado Eventos de teclado, que es lo que necesito. Al final, Christian ha escrito este artículo exactamente para que desarrolladores como yo usen la idea de Iject como un prototipo para una funcionalidad similar.

Para comenzar, decidí crear una extensión simple del núcleo para simplemente registrar la entrada del teclado del usuario a /var/log/kernel.log. Comencé un nuevo proyecto de extensión de núcleo genérico en Xcode, seguí las instrucciones del Hello Kernel: Creación de una extensión de kernel con Xcode tutorial encontrado en Conceptos de extensión del núcleo de Mac Dev Center Para crear un proyecto Hello World y luego rellenarlo con código tomado de fuentes de Iject. Aquí están los 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);
}

La extensión del núcleo resultante se carga/descarga correctamente por los programas KEXTLOAD/KEXTUNLOAD, pero en realidad no intercepta ninguno de los eventos de teclado. He intentado hacer muchas cosas para que funcione, pero sin ningún error u otros problemas con la forma en que no puedo buscar nada útil y pedirle su ayuda.

¿Fue útil?

Solución

El problema no es con la forma en que está anulando la instancia existente de IOHIDSystem. Eso funciona bien.

El problema es que cuando se abre iohikeyboard, se pasa una función de devolución de llamada al sistema IOHID para los eventos de procesamiento. La devolución de llamada es una función privada estática de IOHIDSystem, llamada _KeyBoardEvent:

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

La devolución de llamada luego llama a la función TeyboardEvent en la instancia de IOHIDSystem:

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

No llama al diez parámetro uno, que es virtual (y que está anulando). En cambio, lo que se llama es el 11 parámetro no virtual. Entonces, incluso si intentara anular el parámetro 11, no funcionaría ya que la llamada nunca pasa por el VTable.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top