int operate_on_backtrace(ULONG_PTR retaddr, ULONG_PTR sp, int(*func)(ULONG_PTR)) { int ret; PVOID backtrace[HOOK_BACKTRACE_DEPTH]; lasterror_t lasterror; WORD frames; WORD i; get_lasterrors(&lasterror); hook_disable(); frames = our_stackwalk(retaddr, sp, backtrace, HOOK_BACKTRACE_DEPTH); for (i = 0; i < frames; i++) { if (!addr_in_our_dll_range((ULONG_PTR)backtrace[i])) break; } if (((PUCHAR)backtrace[i])[0] == 0xeb && ((PUCHAR)backtrace[i])[1] == 0x08) i++; for (; i < frames; i++) { ret = func((ULONG_PTR)backtrace[i]); if (ret) goto out; } out: hook_enable(); set_lasterrors(&lasterror); return ret; }
void set_hooks() { // the hooks contain executable code as well, so they have to be RWX DWORD old_protect; VirtualProtect(g_hooks, sizeof(g_hooks), PAGE_EXECUTE_READWRITE, &old_protect); hook_disable(); // now, hook each api :) for (int i = 0; i < ARRAYSIZE(g_hooks); i++) { if(g_hooks[i].allow_hook_recursion != FALSE) { hook_api(&g_hooks[i], HOOKTYPE); } else { hook_api(&g_hooks[i], HOOKTYPE); } } hook_enable(); }
int hook_enable() { // Lock the thread control mutex. This will be unlocked when the // thread has finished starting, or when it has fully stopped. #ifdef _WIN32 WaitForSingleObject(hook_control_mutex, INFINITE); #else pthread_mutex_lock(&hook_control_mutex); #endif // Set the initial status. int status = UIOHOOK_FAILURE; #ifndef _WIN32 // Create the thread attribute. pthread_attr_t hook_thread_attr; pthread_attr_init(&hook_thread_attr); // Get the policy and priority for the thread attr. int policy; pthread_attr_getschedpolicy(&hook_thread_attr, &policy); int priority = sched_get_priority_max(policy); #endif #if defined(_WIN32) DWORD hook_thread_id; DWORD *hook_thread_status = malloc(sizeof(DWORD)); hook_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) hook_thread_proc, hook_thread_status, 0, &hook_thread_id); if (hook_thread != INVALID_HANDLE_VALUE) { #else int *hook_thread_status = malloc(sizeof(int)); if (pthread_create(&hook_thread, &hook_thread_attr, hook_thread_proc, hook_thread_status) == 0) { #endif #if defined(_WIN32) // Attempt to set the thread priority to time critical. if (SetThreadPriority(hook_thread, THREAD_PRIORITY_TIME_CRITICAL) == 0) { logger_proc(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %li for thread %#p! (%#lX)\n", __FUNCTION__, __LINE__, (long) THREAD_PRIORITY_TIME_CRITICAL, hook_thread , (unsigned long) GetLastError()); } #elif (defined(__APPLE__) && defined(__MACH__)) || _POSIX_C_SOURCE >= 200112L // Some POSIX revisions do not support pthread_setschedprio so we will // use pthread_setschedparam instead. struct sched_param param = { .sched_priority = priority }; if (pthread_setschedparam(hook_thread, SCHED_OTHER, ¶m) != 0) { logger_proc(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %i for thread 0x%lX!\n", __FUNCTION__, __LINE__, priority, (unsigned long) hook_thread); } #else // Raise the thread priority using glibc pthread_setschedprio. if (pthread_setschedprio(hook_thread, priority) != 0) { logger_proc(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %i for thread 0x%lX!\n", __FUNCTION__, __LINE__, priority, (unsigned long) hook_thread); } #endif // Wait for the thread to indicate that it has passed the // initialization portion by blocking until either a EVENT_HOOK_ENABLED // event is received or the thread terminates. // NOTE This unlocks the hook_control_mutex while we wait. #ifdef _WIN32 WaitForSingleObject(hook_control_cond, INFINITE); #else pthread_cond_wait(&hook_control_cond, &hook_control_mutex); #endif #ifdef _WIN32 if (WaitForSingleObject(hook_running_mutex, 0) != WAIT_TIMEOUT) { #else if (pthread_mutex_trylock(&hook_running_mutex) == 0) { #endif // Lock Successful; The hook is not running but the hook_control_cond // was signaled! This indicates that there was a startup problem! // Get the status back from the thread. #ifdef _WIN32 WaitForSingleObject(hook_thread, INFINITE); GetExitCodeThread(hook_thread, hook_thread_status); #else pthread_join(hook_thread, (void **) &hook_thread_status); status = *hook_thread_status; #endif } else { // Lock Failure; The hook is currently running and wait was signaled // indicating that we have passed all possible start checks. We can // always assume a successful startup at this point. status = UIOHOOK_SUCCESS; } free(hook_thread_status); logger_proc(LOG_LEVEL_DEBUG, "%s [%u]: Thread Result: (%#X).\n", __FUNCTION__, __LINE__, status); } else { status = UIOHOOK_ERROR_THREAD_CREATE; } // Make sure the control mutex is unlocked. #ifdef _WIN32 ReleaseMutex(hook_control_mutex); #else pthread_mutex_unlock(&hook_control_mutex); #endif return status; } int main() { // Lock the thread control mutex. This will be unlocked when the // thread has finished starting, or when it has fully stopped. #ifdef _WIN32 // Create event handles for the thread hook. hook_running_mutex = CreateMutex(NULL, FALSE, TEXT("hook_running_mutex")); hook_control_mutex = CreateMutex(NULL, FALSE, TEXT("hook_control_mutex")); hook_control_cond = CreateEvent(NULL, TRUE, FALSE, TEXT("hook_control_cond")); #else pthread_mutex_init(&hook_running_mutex, NULL); pthread_mutex_init(&hook_control_mutex, NULL); pthread_cond_init(&hook_control_cond, NULL); #endif // Set the logger callback for library output. hook_set_logger_proc(&logger_proc); // Set the event callback for uiohook events. hook_set_dispatch_proc(&dispatch_proc); // Start the hook and block. // NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed. int status = hook_enable(); switch (status) { case UIOHOOK_SUCCESS: // We no longer block, so we need to explicitly wait for the thread to die. #ifdef _WIN32 WaitForSingleObject(hook_thread, INFINITE); #else #if defined(__APPLE__) && defined(__MACH__) // NOTE Darwin requires that you start your own runloop from main. CFRunLoopRun(); #endif pthread_join(hook_thread, NULL); #endif break; // System level errors. case UIOHOOK_ERROR_OUT_OF_MEMORY: logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)\n", status); break; // X11 specific errors. case UIOHOOK_ERROR_X_OPEN_DISPLAY: logger_proc(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)\n", status); break; case UIOHOOK_ERROR_X_RECORD_NOT_FOUND: logger_proc(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)\n", status); break; case UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE: logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)\n", status); break; case UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT: logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)\n", status); break; case UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT: logger_proc(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)\n", status); break; // Windows specific errors. case UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX: logger_proc(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)\n", status); break; // Darwin specific errors. case UIOHOOK_ERROR_AXAPI_DISABLED: logger_proc(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)\n", status); break; case UIOHOOK_ERROR_CREATE_EVENT_PORT: logger_proc(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)\n", status); break; case UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE: logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)\n", status); break; case UIOHOOK_ERROR_GET_RUNLOOP: logger_proc(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)\n", status); break; case UIOHOOK_ERROR_CREATE_OBSERVER: logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)\n", status); break; // Default error. case UIOHOOK_FAILURE: default: logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)\n", status); break; } #ifdef _WIN32 // Create event handles for the thread hook. CloseHandle(hook_thread); CloseHandle(hook_running_mutex); CloseHandle(hook_control_mutex); CloseHandle(hook_control_cond); #else pthread_mutex_destroy(&hook_running_mutex); pthread_mutex_destroy(&hook_control_mutex); pthread_cond_destroy(&hook_control_cond); #endif return status; }