| /* ********************************************************** |
| * Copyright (c) 2011-2014 Google, Inc. All rights reserved. |
| * Copyright (c) 2003-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| #ifndef TOOLS_H |
| #define TOOLS_H |
| |
| /* i#1424: avoid pulling in features from recent versions to keep compatibility. |
| * The core tries to stay at NT4 but some tests need 2K. |
| */ |
| #ifndef _WIN32_WINNT |
| # define _WIN32_WINNT _WIN32_WINNT_WIN2K |
| #endif |
| |
| #include "configure.h" |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <string.h> /* memcpy */ |
| #include <assert.h> |
| |
| #ifdef UNIX |
| # include <sys/mman.h> |
| # include <stdlib.h> /* abort */ |
| # include <errno.h> |
| # include <signal.h> |
| # include <ucontext.h> |
| # include <unistd.h> |
| #else |
| # include <windows.h> |
| # include <process.h> /* _beginthreadex */ |
| # define NTSTATUS DWORD |
| # define NT_SUCCESS(status) (status >= 0) |
| #endif |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #define BUFFER_SIZE_BYTES(buf) sizeof(buf) |
| #define BUFFER_SIZE_ELEMENTS(buf) (BUFFER_SIZE_BYTES(buf) / sizeof(buf[0])) |
| #define BUFFER_LAST_ELEMENT(buf) buf[BUFFER_SIZE_ELEMENTS(buf) - 1] |
| #define NULL_TERMINATE_BUFFER(buf) BUFFER_LAST_ELEMENT(buf) = 0 |
| |
| /* check if all bits in mask are set in var */ |
| #define TESTALL(mask, var) (((mask) & (var)) == (mask)) |
| /* check if any bit in mask is set in var */ |
| #define TESTANY(mask, var) (((mask) & (var)) != 0) |
| /* check if a single bit is set in var */ |
| #define TEST TESTANY |
| |
| #ifdef USE_DYNAMO |
| /* to avoid non-api tests depending on dr_api headers we rely on test |
| * including dr_api.h before tools.h (though then must include |
| * configure.h before either) |
| */ |
| # ifndef _DR_API_H_ |
| # error "must include dr_api.h before tools.h" |
| # endif |
| #else |
| #ifndef __cplusplus |
| typedef unsigned int bool; |
| #endif |
| # ifdef UNIX |
| typedef unsigned int uint; |
| typedef unsigned long long int uint64; |
| typedef long long int int64; |
| # else |
| typedef unsigned __int64 uint64; |
| typedef __int64 int64; |
| # define uint DWORD |
| # endif |
| # define PAGE_SIZE 0x00001000 |
| #endif |
| |
| #ifdef WINDOWS |
| # define IF_WINDOWS(x) x |
| # define IF_WINDOWS_ELSE(x,y) (x) |
| # ifndef USE_DYNAMO |
| # define INT64_FORMAT "I64" |
| # endif |
| #else |
| # define IF_WINDOWS(x) |
| # define IF_WINDOWS_ELSE(x,y) (y) |
| # ifndef USE_DYNAMO |
| # define INT64_FORMAT "ll" |
| # endif |
| #endif |
| |
| /* Function attributes. */ |
| #ifdef WINDOWS |
| # define EXPORT __declspec(dllexport) |
| # define IMPORT __declspec(dllimport) |
| # define NOINLINE __declspec(noinline) |
| #else /* UNIX */ |
| # define EXPORT __attribute__((visibility("default"))) |
| # define IMPORT extern |
| # define NOINLINE __attribute__((noinline)) |
| #endif |
| |
| |
| /* some tests include dr_api.h and tools.h, so avoid duplicating */ |
| #ifndef IF_X64 |
| # ifdef X64 |
| typedef uint64 ptr_uint_t; |
| typedef int64 ptr_int_t; |
| # define PFMT "%016"INT64_FORMAT"x" |
| # define SZFMT "%"INT64_FORMAT"d" |
| # define IF_X64(x) x |
| # define IF_X64_ELSE(x, y) x |
| # else |
| typedef uint ptr_uint_t; |
| typedef int ptr_int_t; |
| # define PFMT "%08x" |
| # define SZFMT "%d" |
| # define IF_X64(x) |
| # define IF_X64_ELSE(x, y) y |
| # endif |
| # define PFX "0x"PFMT |
| #endif |
| |
| /* convenience macros for secure string buffer operations */ |
| #define BUFFER_SIZE_BYTES(buf) sizeof(buf) |
| #define BUFFER_SIZE_ELEMENTS(buf) (BUFFER_SIZE_BYTES(buf) / sizeof(buf[0])) |
| #define BUFFER_LAST_ELEMENT(buf) buf[BUFFER_SIZE_ELEMENTS(buf) - 1] |
| #define NULL_TERMINATE_BUFFER(buf) BUFFER_LAST_ELEMENT(buf) = 0 |
| #define BUFFER_ROOM_LEFT_W(wbuf) (BUFFER_SIZE_ELEMENTS(wbuf) - wcslen(wbuf) - 1) |
| #define BUFFER_ROOM_LEFT(abuf) (BUFFER_SIZE_ELEMENTS(abuf) - strlen(abuf) - 1) |
| |
| #define PUSHF_MASK 0x00fcffff |
| |
| #define ALLOW_READ 0x01 |
| #define ALLOW_WRITE 0x02 |
| #define ALLOW_EXEC 0x04 |
| |
| typedef enum { |
| CODE_INC, |
| CODE_DEC, |
| CODE_SELF_MOD, |
| } Code_Snippet; |
| |
| typedef enum { |
| COPY_NORMAL, |
| COPY_CROSS_PAGE, |
| } Copy_Mode; |
| |
| #define ALIGN_BACKWARD(x, alignment) \ |
| (((ptr_uint_t)x) & (~((ptr_uint_t)(alignment)-1))) |
| #define ALIGN_FORWARD(x, alignment) \ |
| ((((ptr_uint_t)x) + (((ptr_uint_t)alignment)-1)) & \ |
| (~(((ptr_uint_t)alignment)-1))) |
| #define ALIGNED(x, alignment) ((((ptr_uint_t)x) & ((alignment)-1)) == 0) |
| |
| #ifndef __cplusplus |
| # ifndef true |
| # define true (1) |
| # define false (0) |
| # endif |
| #endif |
| |
| #if VERBOSE |
| # define VERBOSE_PRINT print |
| #else |
| /* FIXME: varargs for windows...for now since we don't care about efficiency we do this: */ |
| static void VERBOSE_PRINT(const char *fmt, ...) {} |
| #endif |
| |
| #ifdef WINDOWS |
| /* FIXME: share w/ core/win32/os_exports.h */ |
| # ifdef X64 |
| # define CXT_XIP Rip |
| # define CXT_XAX Rax |
| # define CXT_XCX Rcx |
| # define CXT_XDX Rdx |
| # define CXT_XBX Rbx |
| # define CXT_XSP Rsp |
| # define CXT_XBP Rbp |
| # define CXT_XSI Rsi |
| # define CXT_XDI Rdi |
| # define CXT_XFLAGS EFlags |
| # else |
| # define CXT_XIP Eip |
| # define CXT_XAX Eax |
| # define CXT_XCX Ecx |
| # define CXT_XDX Edx |
| # define CXT_XBX Ebx |
| # define CXT_XSP Esp |
| # define CXT_XBP Ebp |
| # define CXT_XSI Esi |
| # define CXT_XDI Edi |
| # define CXT_XFLAGS EFlags |
| # endif |
| #endif |
| |
| #ifdef UNIX |
| # ifdef X64 |
| # define SC_XIP rip |
| # else |
| # define SC_XIP eip |
| # endif |
| |
| # define ASSERT_NOERR(rc) do { \ |
| if (rc) { \ |
| print("%s:%d rc=%d errno=%d %s\n", \ |
| __FILE__, __LINE__, \ |
| rc, errno, strerror(errno)); \ |
| } \ |
| } while (0); |
| |
| typedef void (*handler_1_t)(int); |
| typedef void (*handler_3_t)(int, siginfo_t *, ucontext_t *); |
| |
| /* set up signal_handler as the handler for signal "sig" */ |
| void |
| intercept_signal(int sig, handler_3_t handler, bool sigstack); |
| #endif |
| |
| /* for cross-plaform siglongjmp */ |
| #ifdef UNIX |
| # define SIGJMP_BUF sigjmp_buf |
| # define SIGSETJMP(buf) sigsetjmp(buf, 1) |
| # define SIGLONGJMP(buf, count) siglongjmp(buf, count) |
| #else |
| # define SIGJMP_BUF jmp_buf |
| # define SIGSETJMP(buf) setjmp(buf) |
| # define SIGLONGJMP(buf, count) longjmp(buf, count) |
| #endif |
| |
| #ifdef WINDOWS |
| # define NOP __nop() |
| # define NOP_NOP_NOP __nop(); __nop(); __nop() |
| # define NOP_NOP_CALL(tgt) __nop(); __nop(); tgt() |
| #else /* UNIX */ |
| # define NOP asm("nop") |
| # define NOP_NOP_NOP asm("nop\n nop\n nop\n") |
| # define NOP_NOP_CALL(tgt) asm("nop\n nop\n call " #tgt) |
| #endif |
| |
| /* DynamoRIO prints directly by syscall to stderr, so we need to too to get |
| * right output, esp. with ctest -j where fprintf(stderr) is buffered. |
| * Likely to crash if the stack is unaligned due to possible floating point args |
| * in XMM registers. |
| */ |
| void |
| print(const char *fmt, ...); |
| |
| /* just to be sure */ |
| #define printf do_not_use_printf__use_print |
| |
| /* in tools_asm.asm */ |
| int code_self_mod(int iters); |
| /* these don't need asm, but must be adjacent to code_self_mod and in order */ |
| int code_inc(int foo); |
| int code_dec(int foo); |
| int dummy(void); |
| |
| /* This function implements a trampoline that portably gets its return address |
| * and tail calls to its first argument, which is a function pointer. All |
| * other parameters are untouched. It can be used like so: |
| * |
| * void bar(void); |
| * void foo(void **myretaddr, void *otherfunc) { |
| * *myretaddr = (void*)otherfunc; |
| * } |
| * int main(void) { |
| * call_with_retaddr((void*)foo, bar); |
| * } |
| * |
| * Which will cause foo to return to bar. This is useful in security tests |
| * that want to overwrite their return address. |
| */ |
| int call_with_retaddr(void *func, ...); |
| |
| static size_t |
| size(Code_Snippet func) |
| { |
| ptr_int_t val1 = 0, val2 = 0, ret_val; |
| switch(func) { |
| case CODE_INC: |
| val1 = (ptr_int_t)code_inc; |
| val2 = (ptr_int_t)code_dec; |
| break; |
| case CODE_DEC: |
| val1 = (ptr_int_t)code_dec; |
| val2 = (ptr_int_t)dummy; |
| break; |
| case CODE_SELF_MOD: |
| val1 = (ptr_int_t)code_self_mod; |
| val2 = (ptr_int_t)code_inc; |
| break; |
| default: |
| return 0; |
| } |
| /* support ILT indirection where these are jmps to the real code |
| * (adding /debug for i#567 causes ILT usage == table of jmps) |
| */ |
| if (*(unsigned char *)val1 == 0xe9/*jmp*/) |
| val1 = val1 + 5 + *(int*)(val1+1); /* resolve jmp target */ |
| if (*(unsigned char *)val2 == 0xe9/*jmp*/) |
| val2 = val2 + 5 + *(int*)(val2+1); /* resolve jmp target */ |
| ret_val = val2 - val1; |
| if (ret_val < 0) { |
| print("Code layout assumption violation"); |
| return 0; |
| } |
| return ret_val; |
| } |
| |
| static int |
| test(void *foo, int val) |
| { |
| return (*(int (*) (int))foo)(val); |
| } |
| |
| static int |
| call(Code_Snippet func, int val) |
| { |
| switch(func){ |
| case CODE_INC: |
| return code_inc(val); |
| case CODE_DEC: |
| return code_dec(val); |
| case CODE_SELF_MOD: |
| return code_self_mod(val); |
| default: |
| print("Failed to find func to run\n"); |
| } |
| return -1; |
| } |
| |
| static char* |
| page_align(char *buf) |
| { |
| return (char *)(((ptr_int_t)buf + PAGE_SIZE - 1) & ~(PAGE_SIZE-1)); |
| } |
| |
| static char * |
| copy_to_buf_normal(char *buf, size_t buf_len, size_t *copied_len, Code_Snippet func) |
| { |
| void *start; |
| size_t len = size(func); |
| switch(func) { |
| case CODE_INC: |
| start = (void *)code_inc; |
| break; |
| case CODE_DEC: |
| start = (void *)code_dec; |
| break; |
| case CODE_SELF_MOD: |
| start = (void *)code_self_mod; |
| break; |
| default: |
| print("Failed to copy func\n"); |
| } |
| if (*(unsigned char*)start == 0xe9/*jmp*/) { |
| /* handle ILT indirection by resolving jmp target */ |
| start = (unsigned char*)start + 5 + *(int*)((unsigned char*)start+1); |
| } |
| if (len > buf_len) { |
| print("Insufficient buffer for copy, have %d need %d\n", buf_len, len); |
| len = buf_len; |
| } |
| memcpy(buf, start, len); |
| if (copied_len != NULL) |
| *copied_len = len; |
| return buf; |
| } |
| |
| static char * |
| copy_to_buf_cross_page(char *buf, size_t buf_len, size_t *copied_len, Code_Snippet func) |
| { |
| char* buf_back = buf; |
| switch(func) { |
| case CODE_INC: |
| case CODE_DEC: |
| buf = (char *) ((ptr_int_t)page_align(buf) + PAGE_SIZE - 0x02); |
| buf_len = buf_len - ((ptr_int_t)buf - (ptr_int_t)buf_back) - PAGE_SIZE + 0x02; |
| break; |
| case CODE_SELF_MOD: |
| buf = (char *) ((ptr_int_t)page_align(buf) + PAGE_SIZE - 0x10); |
| buf_len = buf_len - ((ptr_int_t)buf - (ptr_int_t)buf_back) - PAGE_SIZE + 0x10; |
| break; |
| default: |
| ; |
| } |
| return copy_to_buf_normal(buf, buf_len, copied_len, func); |
| } |
| |
| static char * |
| copy_to_buf(char *buf, size_t buf_len, size_t *copied_len, Code_Snippet func, |
| Copy_Mode mode) |
| { |
| switch(mode) { |
| case COPY_NORMAL: |
| return copy_to_buf_normal(buf, buf_len, copied_len, func); |
| case COPY_CROSS_PAGE: |
| return copy_to_buf_cross_page(buf, buf_len, copied_len, func); |
| default: |
| print("Improper copy mode\n"); |
| } |
| *copied_len = 0; |
| return buf; |
| } |
| |
| #ifndef UNIX |
| /**************************************************************************** |
| * ntdll.dll interface |
| * cleaner to use ntdll.lib but too lazy to add to build process |
| * |
| * WARNING: the Native API is an undocumented API and |
| * could change without warning with a new version of Windows. |
| */ |
| static HANDLE ntdll_handle = NULL; |
| |
| #define GET_PROC_ADDR(func, type, name) do { \ |
| if (ntdll_handle == NULL) \ |
| ntdll_handle = GetModuleHandle((LPCTSTR)"ntdll.dll"); \ |
| if (func == NULL) { \ |
| assert(ntdll_handle != NULL); \ |
| func = (type) GetProcAddress((HMODULE)ntdll_handle, (LPCSTR)name); \ |
| assert(func != NULL); \ |
| } \ |
| } while (0) |
| |
| /* A wrapper to define kernel entry point in a static function */ |
| /* In C use only at the end of a block prologue! */ |
| #define GET_NTDLL(NtFunction, type) \ |
| typedef int (WINAPI *NtFunction##Type) type; \ |
| static NtFunction##Type NtFunction; \ |
| GET_PROC_ADDR(NtFunction, NtFunction##Type, #NtFunction); |
| |
| /* returns 0 if flush performed ok, not-zero otherwise */ |
| static int |
| NTFlush(char *buf, size_t len) |
| { |
| NTSTATUS status; |
| GET_NTDLL(NtFlushInstructionCache, |
| (IN HANDLE ProcessHandle, |
| IN PVOID BaseAddress OPTIONAL, |
| IN SIZE_T FlushSize)); |
| status = NtFlushInstructionCache(GetCurrentProcess(), buf, len); |
| if (!NT_SUCCESS(status)) { |
| print("Error using NTFlush method\n"); |
| } else { |
| return 0; |
| } |
| return -1; |
| } |
| |
| typedef enum _PROCESSINFOCLASS { |
| ProcessBasicInformation, |
| ProcessQuotaLimits, |
| ProcessIoCounters, |
| ProcessVmCounters, |
| ProcessTimes, |
| ProcessBasePriority, |
| ProcessRaisePriority, |
| ProcessDebugPort, |
| ProcessExceptionPort, |
| ProcessAccessToken, |
| ProcessLdtInformation, |
| ProcessLdtSize, |
| ProcessDefaultHardErrorMode, |
| ProcessIoPortHandlers, // Note: this is kernel mode only |
| ProcessPooledUsageAndLimits, |
| ProcessWorkingSetWatch, |
| ProcessUserModeIOPL, |
| ProcessEnableAlignmentFaultFixup, |
| ProcessPriorityClass, |
| ProcessWx86Information, |
| ProcessHandleCount, |
| ProcessAffinityMask, |
| ProcessPriorityBoost, |
| ProcessDeviceMap, |
| ProcessSessionInformation, |
| ProcessForegroundInformation, |
| ProcessWow64Information, |
| MaxProcessInfoClass |
| } PROCESSINFOCLASS; |
| |
| /* format of data returned by QueryInformationProcess ProcessVmCounters */ |
| typedef struct _VM_COUNTERS { |
| SIZE_T PeakVirtualSize; |
| SIZE_T VirtualSize; |
| ULONG PageFaultCount; |
| SIZE_T PeakWorkingSetSize; |
| SIZE_T WorkingSetSize; |
| SIZE_T QuotaPeakPagedPoolUsage; |
| SIZE_T QuotaPagedPoolUsage; |
| SIZE_T QuotaPeakNonPagedPoolUsage; |
| SIZE_T QuotaNonPagedPoolUsage; |
| SIZE_T PagefileUsage; |
| SIZE_T PeakPagefileUsage; |
| } VM_COUNTERS; |
| |
| static int |
| get_process_mem_stats(HANDLE h, VM_COUNTERS *info) |
| { |
| int i; |
| ULONG len = 0; |
| /* could share w/ other process info routines... */ |
| GET_NTDLL(NtQueryInformationProcess, |
| (IN HANDLE ProcessHandle, |
| IN PROCESSINFOCLASS ProcessInformationClass, |
| OUT PVOID ProcessInformation, |
| IN ULONG ProcessInformationLength, |
| OUT PULONG ReturnLength OPTIONAL)); |
| i = NtQueryInformationProcess((HANDLE) h, ProcessVmCounters, |
| info, sizeof(VM_COUNTERS), &len); |
| if (i != 0) { |
| /* function failed */ |
| memset(info, 0, sizeof(VM_COUNTERS)); |
| return 0; |
| } else |
| assert(len == sizeof(VM_COUNTERS)); |
| return 1; |
| } |
| #endif |
| |
| int |
| get_os_prot_word(int prot); |
| |
| char * |
| allocate_mem(int size, int prot); |
| |
| void |
| protect_mem(void *start, size_t len, int prot); |
| |
| void |
| protect_mem_check(void *start, size_t len, int prot, int expected); |
| |
| void * |
| reserve_memory(int size); |
| |
| static void |
| test_print(void *buf, int n) |
| { |
| print("%d\n", test(buf, n)); |
| } |
| |
| #ifdef UNIX |
| # define USE_USER32() |
| # ifdef NEED_HANDLER |
| # define INIT() intercept_signal(SIGSEGV, (handler_3_t) signal_handler, false) |
| |
| static void |
| signal_handler(int sig) |
| { |
| if (sig == SIGSEGV) { |
| print("Unhandled exception caught.\n"); |
| } else { |
| print("ERROR: Unexpected signal %d caught\n", sig); |
| } |
| exit(-1); |
| } |
| # else |
| # define INIT() |
| # endif /* NEED_HANDLER */ |
| #else |
| # define USE_USER32() do { if (argc > 5) MessageBeep(0); } while (0) |
| |
| # define INIT() set_global_filter() |
| |
| /* XXX: when updating here, update core/os_exports.h too */ |
| # define WINDOWS_VERSION_8_1 63 |
| # define WINDOWS_VERSION_8 62 |
| # define WINDOWS_VERSION_7 61 |
| # define WINDOWS_VERSION_VISTA 60 |
| # define WINDOWS_VERSION_2003 52 |
| # define WINDOWS_VERSION_XP 51 |
| # define WINDOWS_VERSION_2000 50 |
| # define WINDOWS_VERSION_NT 40 |
| |
| /* returns 0 on failure */ |
| int |
| get_windows_version(void); |
| |
| bool |
| is_wow64(HANDLE hProcess); |
| |
| static LONG WINAPI |
| our_exception_filter(struct _EXCEPTION_POINTERS * pExceptionInfo) |
| { |
| /* use EXCEPTION_CONTINUE_SEARCH to let it go all the way */ |
| if (pExceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { |
| print("ERROR: Unexpected exception 0x%x caught\n", pExceptionInfo->ExceptionRecord->ExceptionCode); |
| } |
| print("Unhandled exception caught.\n"); |
| return EXCEPTION_EXECUTE_HANDLER; |
| } |
| |
| static void |
| set_global_filter() |
| { |
| // Set the global unhandled exception filter to the exception filter |
| SetUnhandledExceptionFilter(our_exception_filter); |
| } |
| |
| #endif /* UNIX */ |
| |
| #ifdef UNIX |
| typedef int thread_handle; |
| typedef unsigned int (*fptr)(void *); |
| #else |
| typedef HANDLE thread_handle; |
| typedef unsigned int (__stdcall *fptr)(void *); |
| #endif |
| |
| #ifdef WINDOWS |
| /* Thread related functions */ |
| thread_handle |
| create_thread(fptr f); |
| void |
| suspend_thread(thread_handle th); |
| |
| void |
| resume_thread(thread_handle th); |
| |
| void |
| join_thread(thread_handle th); |
| |
| void |
| thread_yield(); |
| #endif |
| |
| #ifdef WINDOWS |
| static byte * |
| get_drmarker_field(uint offset) |
| { |
| /* read DR marker |
| * just hardcode the offsets for now |
| */ |
| byte *field; |
| HANDLE ntdll_handle = GetModuleHandle("ntdll.dll"); |
| byte *cbd = (byte *) GetProcAddress((HMODULE)ntdll_handle, "KiUserCallbackDispatcher"); |
| byte *drmarker, *landing_pad; |
| if (*cbd != 0xe9) /* no jmp there */ |
| return NULL; |
| |
| /* see win32/callback.c:emit_landing_pad_code() for details */ |
| landing_pad = *((int *)(cbd+1)) /* skip jmp opcode */ |
| + cbd + 5; /* relative */ |
| #ifdef X64 |
| drmarker = *((byte **)(landing_pad-8)); /* skip jmp opcode */ |
| #else |
| drmarker = *((byte **)(landing_pad+1)) /* skip jmp opcode */ |
| + (uint)landing_pad + 5; /* relative */ |
| #endif |
| drmarker = (byte *) ALIGN_BACKWARD((ptr_uint_t)drmarker, PAGE_SIZE); |
| /* FIXME: check magic fields */ |
| field = *((byte **)(drmarker + offset)); |
| return field; |
| } |
| |
| /* For hot patch testing only. Only defined for Windows - hot patching hasn't |
| * been implemented for Linux yet. |
| * TODO: need to parameterize it with number or string to annotate a patch |
| * point - needed to handle multiple patch points automatically for |
| * defs.cfg generation; another option would be to maintain a counter |
| * that increments with each use of the macro. |
| */ |
| #define INSERT_HOTP_PATCH_POINT() \ |
| __asm { \ |
| __asm jmp foo \ |
| __asm _emit '$' \ |
| __asm _emit 'p' \ |
| __asm _emit 'p' \ |
| __asm _emit 'o' \ |
| __asm _emit 'i' \ |
| __asm _emit 'n' \ |
| __asm _emit 't' \ |
| __asm _emit '$' \ |
| __asm foo: \ |
| } |
| #endif |
| |
| #ifdef UNIX |
| /* Forward decl for nanosleep. */ |
| struct timespec; |
| |
| bool find_dynamo_library(void); |
| |
| /* Staticly linked versions of libc routines that don't touch globals or errno. |
| */ |
| ptr_int_t nolibc_syscall(uint sysnum, uint num_args, ...); |
| void nolibc_print(const char *str); |
| void nolibc_print_int(int d); |
| void nolibc_nanosleep(struct timespec *req); |
| int nolibc_strlen(const char *str); |
| void *nolibc_mmap(void *addr, size_t length, int prot, int flags, int fd, |
| off_t offset); |
| void nolibc_munmap(void *addr, size_t length); |
| void nolibc_memset(void *dst, int val, size_t size); |
| #endif |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* TOOLS_H */ |