├── hooklib ├── serial.h ├── meson.build ├── uart.h ├── uart.c └── serial.c ├── .gitignore ├── inject ├── debug.h ├── meson.build ├── options.h ├── debug.c ├── options.c └── main.c ├── hook ├── args.h ├── process.h ├── hr.h ├── peb.h ├── table.h ├── meson.build ├── pe.h ├── peb.c ├── hr.c ├── iohook.h ├── iobuf.h ├── com-proxy.h ├── args.c ├── process.c ├── table.c ├── pe.c ├── com-proxy.c ├── iobuf.c └── iohook.c ├── cross-mingw-32.txt ├── cross-mingw-64.txt ├── .editorconfig ├── meson.build ├── precompiled.h └── LICENSE /hooklib/serial.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void serial_hook_init(void); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | 3 | # Suggested names for build dirs 4 | _build32/ 5 | _build64/ 6 | -------------------------------------------------------------------------------- /inject/debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | HRESULT debug_main(HANDLE process, uint32_t pid); 8 | -------------------------------------------------------------------------------- /hook/args.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | HRESULT args_recover(int *argc, char ***argv); 6 | void args_free(int argc, char **argv); 7 | -------------------------------------------------------------------------------- /hook/process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef DWORD (CALLBACK *process_entry_t)(void); 6 | 7 | HRESULT process_hijack_startup( 8 | process_entry_t new_entry, 9 | process_entry_t *orig_entry); 10 | -------------------------------------------------------------------------------- /hook/hr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #define hr_propagate_win32(hr, r) (hr_propagate_win32_(hr), r) 8 | 9 | uint32_t hr_to_win32_error(HRESULT hr); 10 | void hr_propagate_win32_(HRESULT hr); 11 | -------------------------------------------------------------------------------- /cross-mingw-32.txt: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = '/usr/bin/i686-w64-mingw32-gcc' 3 | ar = '/usr/bin/i686-w64-mingw32-ar' 4 | strip = '/usr/bin/i686-w64-mingw32-strip' 5 | 6 | [host_machine] 7 | system = 'windows' 8 | cpu_family = 'x86' 9 | cpu = 'i686' 10 | endian = 'little' 11 | -------------------------------------------------------------------------------- /cross-mingw-64.txt: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = '/usr/bin/x86_64-w64-mingw32-gcc' 3 | ar = '/usr/bin/x86_64-w64-mingw32-ar' 4 | strip = '/usr/bin/x86_64-w64-mingw32-strip' 5 | 6 | [host_machine] 7 | system = 'windows' 8 | cpu_family = 'x86_64' 9 | cpu = 'x86_64' 10 | endian = 'little' 11 | -------------------------------------------------------------------------------- /inject/meson.build: -------------------------------------------------------------------------------- 1 | executable( 2 | 'inject', 3 | include_directories : inc, 4 | c_pch : '../precompiled.h', 5 | sources : [ 6 | 'debug.c', 7 | 'debug.h', 8 | 'main.c', 9 | 'options.c', 10 | 'options.h', 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{c,h}] 12 | indent_style = space 13 | indent_size = 4 14 | max_line_length = 79 15 | 16 | -------------------------------------------------------------------------------- /hook/peb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef LDR_DATA_TABLE_ENTRY peb_dll_t; 7 | 8 | const peb_dll_t *peb_dll_get_first(void); 9 | const peb_dll_t *peb_dll_get_next(const peb_dll_t *dll); 10 | HMODULE peb_dll_get_base(const peb_dll_t *dll); 11 | char *peb_dll_dup_name(const peb_dll_t *dll); 12 | -------------------------------------------------------------------------------- /hooklib/meson.build: -------------------------------------------------------------------------------- 1 | hooklib_lib = static_library( 2 | 'hooklib', 3 | include_directories : inc, 4 | c_pch : '../precompiled.h', 5 | sources : [ 6 | 'serial.c', 7 | 'serial.h', 8 | 'uart.c', 9 | 'uart.h', 10 | ], 11 | ) 12 | 13 | hooklib_dep = declare_dependency( 14 | link_with : hooklib_lib, 15 | include_directories : inc, 16 | ) 17 | -------------------------------------------------------------------------------- /hook/table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct hook_symbol { 9 | const char *name; 10 | uint16_t ordinal; 11 | void *patch; 12 | void **link; 13 | }; 14 | 15 | void hook_table_apply( 16 | HMODULE target, 17 | const char *depname, 18 | const struct hook_symbol *syms, 19 | size_t nsyms); 20 | 21 | void hook_table_revert( 22 | HMODULE target, 23 | const char *depname, 24 | const struct hook_symbol *syms, 25 | size_t nsyms); 26 | -------------------------------------------------------------------------------- /inject/options.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct options { 9 | bool help; 10 | bool wait; 11 | bool debug; 12 | bool debug_pause; 13 | int dll_pos; 14 | int orig_argc; 15 | char **orig_argv; 16 | int target_argc; 17 | char **target_argv; 18 | }; 19 | 20 | void options_help(FILE *f); 21 | HRESULT options_init(struct options *opt, int argc, char **argv); 22 | HRESULT options_target_cmdline(const struct options *opt, char **str); 23 | HRESULT options_next_dll(struct options *opt, const char **dll); 24 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('capnhook', 'c', version: '0.2.2') 2 | 3 | add_project_arguments( 4 | '-DCOBJMACROS', 5 | '-DWIN32_LEAN_AND_MEAN', 6 | language: 'c', 7 | ) 8 | 9 | if meson.get_compiler('c').get_argument_syntax() != 'msvc' 10 | add_project_arguments( 11 | '-ffunction-sections', 12 | '-fdata-sections', 13 | language: 'c', 14 | ) 15 | 16 | add_project_link_arguments( 17 | '-Wl,--gc-sections', 18 | '-static-libgcc', 19 | language: 'c', 20 | ) 21 | endif 22 | 23 | inc = include_directories('.') 24 | 25 | subdir('hook') 26 | subdir('hooklib') 27 | subdir('inject') 28 | -------------------------------------------------------------------------------- /hook/meson.build: -------------------------------------------------------------------------------- 1 | hook_lib = static_library( 2 | 'hook', 3 | include_directories : inc, 4 | c_pch : '../precompiled.h', 5 | sources : [ 6 | 'args.c', 7 | 'args.h', 8 | 'com-proxy.c', 9 | 'com-proxy.h', 10 | 'hr.c', 11 | 'hr.h', 12 | 'iobuf.c', 13 | 'iobuf.h', 14 | 'iohook.c', 15 | 'iohook.h', 16 | 'pe.c', 17 | 'pe.h', 18 | 'peb.c', 19 | 'peb.h', 20 | 'process.c', 21 | 'process.h', 22 | 'table.c', 23 | 'table.h', 24 | ], 25 | ) 26 | 27 | hook_dep = declare_dependency( 28 | link_with : hook_lib, 29 | include_directories : inc, 30 | ) 31 | -------------------------------------------------------------------------------- /hook/pe.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | typedef IMAGE_IMPORT_DESCRIPTOR pe_iid_t; 10 | 11 | struct pe_iat_entry { 12 | const char *name; 13 | uint16_t ordinal; 14 | void **ppointer; 15 | }; 16 | 17 | const pe_iid_t *pe_iid_get_first(HMODULE pe); 18 | const char *pe_iid_get_name(HMODULE pe, const pe_iid_t *iid); 19 | const pe_iid_t *pe_iid_get_next(HMODULE pe, const pe_iid_t *iid); 20 | HRESULT pe_iid_get_iat_entry( 21 | HMODULE pe, 22 | const pe_iid_t *iid, 23 | size_t n, 24 | struct pe_iat_entry *entry); 25 | void *pe_get_export(HMODULE pe, const char *name, uint16_t ord); 26 | void *pe_get_entry_point(HMODULE pe); 27 | HRESULT pe_patch(void *dest, const void *src, size_t nbytes); 28 | -------------------------------------------------------------------------------- /precompiled.h: -------------------------------------------------------------------------------- 1 | /* 2 | Making NTSTATUS available is slightly awkward. See: 3 | https://kirkshoop.github.io/2011/09/20/ntstatus.html 4 | */ 5 | 6 | /* Win32 user-mode API */ 7 | #define WIN32_NO_STATUS 8 | #include 9 | #undef WIN32_NO_STATUS 10 | #include 11 | #include 12 | #include 13 | 14 | /* Win32 kernel-mode definitions */ 15 | #ifdef __GNUC__ 16 | /* MinGW needs to include this for PHYSICAL_ADDRESS to be defined. 17 | The MS SDK throws a bunch of duplicate symbol errors instead. */ 18 | #include 19 | #else 20 | #include 21 | #endif 22 | #include 23 | #include 24 | #include 25 | 26 | /* ANSI C */ 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | -------------------------------------------------------------------------------- /hooklib/uart.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __GNUC__ 6 | #include 7 | #else 8 | #include 9 | #endif 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "hook/iobuf.h" 16 | #include "hook/iohook.h" 17 | 18 | struct uart { 19 | HANDLE fd; 20 | unsigned int port_no; 21 | SERIAL_BAUD_RATE baud; 22 | SERIAL_STATUS status; 23 | SERIAL_CHARS chars; 24 | SERIAL_HANDFLOW handflow; 25 | SERIAL_LINE_CONTROL line; 26 | SERIAL_TIMEOUTS timeouts; 27 | DWORD mask; 28 | struct iobuf written; 29 | struct iobuf readable; 30 | }; 31 | 32 | void uart_init(struct uart *uart, unsigned int port_no); 33 | void uart_fini(struct uart *uart); 34 | bool uart_match_irp(const struct uart *uart, const struct irp *irp); 35 | HRESULT uart_handle_irp(struct uart *uart, struct irp *irp); 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Decaf Code 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /hook/peb.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "hook/peb.h" 7 | 8 | static const PEB *peb_get(void) 9 | { 10 | #ifdef _M_AMD64 11 | return (const PEB *) __readgsqword(0x60); 12 | #else 13 | return (const PEB *) __readfsdword(0x30); 14 | #endif 15 | } 16 | 17 | const peb_dll_t *peb_dll_get_first(void) 18 | { 19 | const PEB *peb; 20 | const LIST_ENTRY *node; 21 | 22 | peb = peb_get(); 23 | node = peb->Ldr->InMemoryOrderModuleList.Flink; 24 | 25 | return CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 26 | } 27 | 28 | const peb_dll_t *peb_dll_get_next(const peb_dll_t *dll) 29 | { 30 | const PEB *peb; 31 | const LIST_ENTRY *node; 32 | 33 | assert(dll != NULL); 34 | 35 | peb = peb_get(); 36 | node = dll->InMemoryOrderLinks.Flink; 37 | 38 | if (node == peb->Ldr->InMemoryOrderModuleList.Flink) { 39 | return NULL; 40 | } 41 | 42 | return CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 43 | } 44 | 45 | HMODULE peb_dll_get_base(const peb_dll_t *dll) 46 | { 47 | assert(dll != NULL); 48 | 49 | return dll->DllBase; 50 | } 51 | -------------------------------------------------------------------------------- /hook/hr.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "hook/hr.h" 6 | 7 | uint32_t hr_to_win32_error(HRESULT hr) 8 | { 9 | if (SUCCEEDED(hr)) { 10 | return ERROR_SUCCESS; 11 | } else if (HRESULT_FACILITY(hr) == FACILITY_WIN32) { 12 | return HRESULT_CODE(hr); 13 | } else { 14 | switch (hr) { 15 | case E_ABORT: return ERROR_OPERATION_ABORTED; 16 | case E_ACCESSDENIED: return ERROR_ACCESS_DENIED; 17 | case E_FAIL: return ERROR_GEN_FAILURE; 18 | case E_HANDLE: return ERROR_INVALID_HANDLE; 19 | case E_INVALIDARG: return ERROR_INVALID_PARAMETER; 20 | case E_NOINTERFACE: return ERROR_INVALID_FUNCTION; 21 | case E_NOTIMPL: return ERROR_NOT_SUPPORTED; 22 | case E_OUTOFMEMORY: return ERROR_OUTOFMEMORY; 23 | case E_POINTER: return ERROR_INVALID_ADDRESS; 24 | case E_UNEXPECTED: return ERROR_INTERNAL_ERROR; 25 | default: return ERROR_INTERNAL_ERROR; 26 | } 27 | } 28 | } 29 | 30 | void hr_propagate_win32_(HRESULT hr) 31 | { 32 | SetLastError(hr_to_win32_error(hr)); 33 | } 34 | -------------------------------------------------------------------------------- /hook/iohook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "hook/iobuf.h" 9 | 10 | enum irp_op { 11 | IRP_OP_OPEN, 12 | IRP_OP_CLOSE, 13 | IRP_OP_READ, 14 | IRP_OP_WRITE, 15 | IRP_OP_IOCTL, 16 | IRP_OP_FSYNC, 17 | IRP_OP_SEEK, 18 | }; 19 | 20 | struct irp { 21 | enum irp_op op; 22 | size_t next_handler; 23 | HANDLE fd; 24 | OVERLAPPED *ovl; 25 | struct const_iobuf write; 26 | struct iobuf read; 27 | uint32_t ioctl; 28 | const wchar_t *open_filename; 29 | uint32_t open_access; 30 | uint32_t open_share; 31 | SECURITY_ATTRIBUTES *open_sa; 32 | uint32_t open_creation; 33 | uint32_t open_flags; 34 | HANDLE *open_tmpl; 35 | uint32_t seek_origin; 36 | int64_t seek_offset; 37 | uint64_t seek_pos; 38 | }; 39 | 40 | typedef HRESULT (*iohook_fn_t)(struct irp *irp); 41 | 42 | HANDLE iohook_open_dummy_fd(void) 43 | #ifdef __GNUC__ 44 | __attribute__((deprecated("Use iohook_open_nul_fd instead"))) 45 | #endif 46 | ; 47 | 48 | HRESULT iohook_open_nul_fd(HANDLE *fd); 49 | HRESULT iohook_push_handler(iohook_fn_t fn); 50 | HRESULT iohook_invoke_next(struct irp *irp); 51 | -------------------------------------------------------------------------------- /hook/iobuf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct iobuf { 9 | uint8_t *bytes; 10 | size_t nbytes; 11 | size_t pos; 12 | }; 13 | 14 | struct const_iobuf { 15 | const uint8_t *bytes; 16 | size_t nbytes; 17 | size_t pos; 18 | }; 19 | 20 | void iobuf_flip(struct const_iobuf *child, struct iobuf *parent); 21 | size_t iobuf_move(struct iobuf *dest, struct const_iobuf *src); 22 | size_t iobuf_shift(struct iobuf *dest, struct iobuf *src); 23 | 24 | HRESULT iobuf_read(struct const_iobuf *src, void *bytes, size_t nbytes); 25 | HRESULT iobuf_read_8(struct const_iobuf *src, uint8_t *value); 26 | HRESULT iobuf_read_be16(struct const_iobuf *src, uint16_t *value); 27 | HRESULT iobuf_read_be32(struct const_iobuf *src, uint32_t *value); 28 | HRESULT iobuf_read_be64(struct const_iobuf *src, uint64_t *value); 29 | HRESULT iobuf_read_le16(struct const_iobuf *src, uint16_t *value); 30 | HRESULT iobuf_read_le32(struct const_iobuf *src, uint32_t *value); 31 | HRESULT iobuf_read_le64(struct const_iobuf *src, uint64_t *value); 32 | 33 | HRESULT iobuf_write(struct iobuf *dest, const void *bytes, size_t nbytes); 34 | HRESULT iobuf_write_8(struct iobuf *dest, uint8_t value); 35 | HRESULT iobuf_write_be16(struct iobuf *dest, uint16_t value); 36 | HRESULT iobuf_write_be32(struct iobuf *dest, uint32_t value); 37 | HRESULT iobuf_write_be64(struct iobuf *dest, uint64_t value); 38 | HRESULT iobuf_write_le16(struct iobuf *dest, uint16_t value); 39 | HRESULT iobuf_write_le32(struct iobuf *dest, uint32_t value); 40 | HRESULT iobuf_write_le64(struct iobuf *dest, uint64_t value); 41 | -------------------------------------------------------------------------------- /hook/com-proxy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define com_proxy_downcast(self) ((struct com_proxy *) self) 7 | 8 | struct com_proxy { 9 | /* Pointer to vtable filled with trampolines. Edit these as you please. 10 | Each com_proxy has its own independent vtable. */ 11 | void *vptr; 12 | 13 | /* Interface pointer wrapped by this proxy. */ 14 | void *real; 15 | 16 | /* Context pointer for use by hook code. */ 17 | void *ctx; 18 | 19 | /* Optional cleanup callback, will be called during com_proxy deallocation 20 | to clean up *ctx. */ 21 | void (*cleanup_ctx)(void *ctx); 22 | 23 | /* Dynamically generated x86 trampoline code. The initial vtable entries 24 | all point into code located here. */ 25 | uint8_t *tramps; 26 | }; 27 | 28 | /* Wrap a COM interface pointer in a proxy. This is an object that acts just 29 | like the object that it wraps but has a freely editable vtable, which you 30 | can modify in order to intercept a subset of the interface's method calls. 31 | 32 | By default, all the vtable slots contain dynamically generated trampolines 33 | which pass method calls onwards to the corresponding methods in the 34 | underlying object's vtable. 35 | 36 | NOTE! This does not AddRef the underlying interface. 37 | 38 | NOTE! This function wraps COM POINTERS, not COM OBJECTS (since the latter 39 | is, in general, impossible). Consequences of this distinction include the 40 | following: 41 | 42 | 1. Do not wrap IUnknown pointers with this function. This will break the 43 | IUnknown::QueryInterface contract. This refers to _the_ unique 44 | IUnknown* for the object, not for any other interface (which necessarily 45 | extends IUnknown). Wrapping the unique IUnknown* for an object will 46 | cause it to no longer be unique. 47 | 48 | 2. Callers can "jailbreak" your wrapper using IUnknown::QueryInterface 49 | unless you provide a custom QueryInterface implementation to prevent them 50 | from doing so. */ 51 | 52 | HRESULT com_proxy_wrap( 53 | struct com_proxy **out, 54 | void *real, 55 | size_t vtbl_size); 56 | -------------------------------------------------------------------------------- /hook/args.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "hook/args.h" 10 | 11 | /* This does not handle escaped double quotes inside args correctly yet */ 12 | 13 | static HRESULT args_push( 14 | int *argc, 15 | char ***argv, 16 | const char *begin, 17 | const char *end) 18 | { 19 | int tmp_argc; 20 | char **tmp_argv; 21 | size_t nchars; 22 | char *str; 23 | 24 | nchars = end - begin; 25 | str = malloc(nchars + 1); 26 | 27 | if (str == NULL) { 28 | goto fail; 29 | } 30 | 31 | memcpy(str, begin, nchars); 32 | str[nchars] = '\0'; 33 | 34 | tmp_argc = *argc + 1; 35 | tmp_argv = realloc(*argv, tmp_argc * sizeof(char **)); 36 | 37 | if (tmp_argv == NULL) { 38 | goto fail; 39 | } 40 | 41 | tmp_argv[tmp_argc - 1] = str; 42 | 43 | *argv = tmp_argv; 44 | *argc = tmp_argc; 45 | 46 | return S_OK; 47 | 48 | fail: 49 | free(str); 50 | 51 | return E_OUTOFMEMORY; 52 | } 53 | 54 | HRESULT args_recover(int *argc_out, char ***argv_out) 55 | { 56 | int argc; 57 | char **argv; 58 | const char *begin; 59 | const char *pos; 60 | bool quote; 61 | HRESULT hr; 62 | 63 | assert(argc_out != NULL); 64 | assert(argv_out != NULL); 65 | 66 | *argc_out = 0; 67 | *argv_out = NULL; 68 | 69 | argc = 0; 70 | argv = NULL; 71 | quote = false; 72 | 73 | for (begin = pos = GetCommandLine() ; *pos ; pos++) { 74 | switch (*pos) { 75 | case '"': 76 | if (!quote) { 77 | quote = true; 78 | begin = pos + 1; 79 | } else { 80 | hr = args_push(&argc, &argv, begin, pos); 81 | 82 | if (FAILED(hr)) { 83 | goto fail; 84 | } 85 | 86 | quote = false; 87 | begin = NULL; 88 | } 89 | 90 | break; 91 | 92 | case ' ': 93 | if (!quote && begin != NULL) { 94 | args_push(&argc, &argv, begin, pos); 95 | begin = NULL; 96 | } 97 | 98 | break; 99 | 100 | default: 101 | if (begin == NULL) { 102 | begin = pos; 103 | } 104 | 105 | break; 106 | } 107 | } 108 | 109 | if (begin != NULL && !quote) { 110 | hr = args_push(&argc, &argv, begin, pos); 111 | 112 | if (FAILED(hr)) { 113 | goto fail; 114 | } 115 | } 116 | 117 | *argc_out = argc; 118 | *argv_out = argv; 119 | 120 | return S_OK; 121 | 122 | fail: 123 | args_free(argc, argv); 124 | 125 | return hr; 126 | } 127 | 128 | void args_free(int argc, char **argv) 129 | { 130 | int i; 131 | 132 | for (i = 0 ; i < argc ; i++) { 133 | free(argv[i]); 134 | } 135 | 136 | free(argv); 137 | } 138 | -------------------------------------------------------------------------------- /inject/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static HRESULT debug_wstr(HANDLE process, const OUTPUT_DEBUG_STRING_INFO *odsi); 10 | static bool debug_str(HANDLE process, const OUTPUT_DEBUG_STRING_INFO *odsi); 11 | 12 | HRESULT debug_main(HANDLE process, uint32_t pid) 13 | { 14 | DEBUG_EVENT ev; 15 | DWORD status; 16 | HRESULT hr; 17 | BOOL ok; 18 | 19 | for (;;) { 20 | ok = WaitForDebugEvent(&ev, INFINITE); 21 | 22 | if (!ok) { 23 | hr = HRESULT_FROM_WIN32(GetLastError()); 24 | fprintf(stderr, "WaitForDebugEvent failed: %x\n", (int) hr); 25 | 26 | return hr; 27 | } 28 | 29 | switch (ev.dwDebugEventCode) { 30 | case CREATE_PROCESS_DEBUG_EVENT: 31 | CloseHandle(ev.u.CreateProcessInfo.hFile); 32 | 33 | break; 34 | 35 | case EXIT_PROCESS_DEBUG_EVENT: 36 | if (ev.dwProcessId == pid) { 37 | return S_OK; 38 | } 39 | 40 | break; 41 | 42 | case LOAD_DLL_DEBUG_EVENT: 43 | CloseHandle(ev.u.LoadDll.hFile); 44 | 45 | break; 46 | 47 | case OUTPUT_DEBUG_STRING_EVENT: 48 | if (ev.dwProcessId == pid) { 49 | if (ev.u.DebugString.fUnicode) { 50 | hr = debug_wstr(process, &ev.u.DebugString); 51 | } else { 52 | hr = debug_str(process, &ev.u.DebugString); 53 | } 54 | 55 | if (FAILED(hr)) { 56 | return hr; 57 | } 58 | } 59 | 60 | break; 61 | } 62 | 63 | if (ev.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { 64 | status = DBG_CONTINUE; 65 | } else { 66 | status = DBG_EXCEPTION_NOT_HANDLED; 67 | } 68 | 69 | ok = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, status); 70 | 71 | if (!ok) { 72 | hr = HRESULT_FROM_WIN32(GetLastError()); 73 | fprintf(stderr, "ContinueDebugEvent failed: %x\n", (int) hr); 74 | 75 | return hr; 76 | } 77 | } 78 | } 79 | 80 | static HRESULT debug_wstr(HANDLE process, const OUTPUT_DEBUG_STRING_INFO *odsi) 81 | { 82 | char *str; 83 | wchar_t *wstr; 84 | int nbytes_w; 85 | int nbytes_a; 86 | int result; 87 | HRESULT hr; 88 | BOOL ok; 89 | 90 | str = NULL; 91 | nbytes_w = odsi->nDebugStringLength * sizeof(wchar_t); 92 | wstr = malloc(nbytes_w); 93 | 94 | if (wstr == NULL) { 95 | hr = E_OUTOFMEMORY; 96 | 97 | goto end; 98 | } 99 | 100 | ok = ReadProcessMemory( 101 | process, 102 | odsi->lpDebugStringData, 103 | wstr, 104 | nbytes_w, 105 | NULL); 106 | 107 | if (!ok) { 108 | hr = HRESULT_FROM_WIN32(GetLastError()); 109 | fprintf(stderr, 110 | "%s: ReadProcessMemory failed: %x\n", 111 | __func__, 112 | (int) hr); 113 | 114 | goto end; 115 | } 116 | 117 | nbytes_a = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL); 118 | str = malloc(nbytes_a); 119 | 120 | if (str == NULL) { 121 | hr = E_OUTOFMEMORY; 122 | 123 | goto end; 124 | } 125 | 126 | result = WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, nbytes_a, NULL,NULL); 127 | 128 | if (result == 0) { 129 | hr = HRESULT_FROM_WIN32(GetLastError()); 130 | fprintf(stderr, "WideCharToMultiByte failed: %x\n", (int) hr); 131 | 132 | goto end; 133 | } 134 | 135 | fputs(str, stdout); 136 | 137 | hr = S_OK; 138 | 139 | end: 140 | free(str); 141 | free(wstr); 142 | 143 | return hr; 144 | } 145 | 146 | static bool debug_str(HANDLE process, const OUTPUT_DEBUG_STRING_INFO *odsi) 147 | { 148 | char *str; 149 | HRESULT hr; 150 | BOOL ok; 151 | 152 | str = malloc(odsi->nDebugStringLength); 153 | 154 | if (str == NULL) { 155 | hr = E_OUTOFMEMORY; 156 | 157 | goto end; 158 | } 159 | 160 | ok = ReadProcessMemory( 161 | process, 162 | odsi->lpDebugStringData, 163 | str, 164 | odsi->nDebugStringLength, 165 | NULL); 166 | 167 | if (!ok) { 168 | hr = HRESULT_FROM_WIN32(GetLastError()); 169 | fprintf(stderr, "%s: ReadProcessMemory failed: %x", __func__, (int) hr); 170 | 171 | goto end; 172 | } 173 | 174 | fputs(str, stdout); 175 | 176 | hr = S_OK; 177 | 178 | end: 179 | free(str); 180 | 181 | return hr; 182 | } 183 | -------------------------------------------------------------------------------- /hook/process.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "hook/hr.h" 9 | #include "hook/pe.h" 10 | #include "hook/process.h" 11 | 12 | static bool thread_match_startup( 13 | const CONTEXT *ctx, 14 | void *ntstart, 15 | void *exe_entry) 16 | { 17 | #ifdef _M_AMD64 18 | return ctx->Rip == (DWORD64) ntstart && 19 | ctx->Rcx == (DWORD64) exe_entry; 20 | #else 21 | return ctx->Eip == (DWORD) ntstart && 22 | ctx->Eax == (DWORD) exe_entry; 23 | #endif 24 | } 25 | 26 | static void thread_patch_startup( 27 | process_entry_t new_entry, 28 | process_entry_t *orig_entry, 29 | CONTEXT *ctx) 30 | { 31 | #ifdef _M_AMD64 32 | *orig_entry = (void *) ctx->Rcx; 33 | ctx->Rcx = (DWORD64) new_entry; 34 | #else 35 | *orig_entry = (void *) ctx->Eax; 36 | ctx->Eax = (DWORD) new_entry; 37 | #endif 38 | } 39 | 40 | static HRESULT process_hijack_try_thread( 41 | process_entry_t new_entry, 42 | process_entry_t *orig_entry, 43 | DWORD thread_id) 44 | { 45 | CONTEXT ctx; 46 | HMODULE exe; 47 | HMODULE ntdll; 48 | void *exe_entry; 49 | void *ntstart; 50 | HANDLE thread; 51 | HRESULT hr; 52 | BOOL ok; 53 | 54 | thread = NULL; 55 | 56 | exe = GetModuleHandleW(NULL); 57 | 58 | if (exe == NULL) { 59 | /* uhhhh... */ 60 | hr = E_UNEXPECTED; 61 | 62 | goto end; 63 | } 64 | 65 | ntdll = GetModuleHandleW(L"ntdll.dll"); 66 | 67 | if (ntdll == NULL) { 68 | /* Another 2 + 2 = 5 situation */ 69 | hr = E_UNEXPECTED; 70 | 71 | goto end; 72 | } 73 | 74 | exe_entry = pe_get_entry_point(exe); 75 | ntstart = GetProcAddress(ntdll, "RtlUserThreadStart"); 76 | 77 | if (ntstart == NULL) { 78 | /* TODO Deal with WinXP, for the poor souls still stuck on that OS. 79 | XP starts threads at LdrInitializeThunk instead (I think) */ 80 | hr = E_NOTIMPL; 81 | 82 | goto end; 83 | } 84 | 85 | thread = OpenThread( 86 | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, 87 | FALSE, 88 | thread_id); 89 | 90 | if (thread == NULL) { 91 | hr = HRESULT_FROM_WIN32(GetLastError()); 92 | 93 | goto end; 94 | } 95 | 96 | memset(&ctx, 0, sizeof(ctx)); 97 | #ifdef _M_AMD64 98 | ctx.ContextFlags = CONTEXT_AMD64 | CONTEXT_FULL; 99 | #else 100 | ctx.ContextFlags = CONTEXT_i386 | CONTEXT_FULL; 101 | #endif 102 | 103 | ok = GetThreadContext(thread, &ctx); 104 | 105 | if (!ok) { 106 | hr = HRESULT_FROM_WIN32(GetLastError()); 107 | 108 | goto end; 109 | } 110 | 111 | if (thread_match_startup(&ctx, ntstart, exe_entry)) { 112 | thread_patch_startup(new_entry, orig_entry, &ctx); 113 | ok = SetThreadContext(thread, &ctx); 114 | 115 | if (!ok) { 116 | hr = HRESULT_FROM_WIN32(GetLastError()); 117 | 118 | goto end; 119 | } 120 | 121 | return S_OK; 122 | } else { 123 | return S_FALSE; 124 | } 125 | 126 | end: 127 | if (thread != NULL) { 128 | CloseHandle(thread); 129 | } 130 | 131 | return hr; 132 | } 133 | 134 | HRESULT process_hijack_startup( 135 | process_entry_t new_entry, 136 | process_entry_t *orig_entry) 137 | { 138 | THREADENTRY32 thread; 139 | HANDLE snap; 140 | DWORD pid; 141 | HRESULT fault; 142 | HRESULT hr; 143 | BOOL ok; 144 | 145 | assert(new_entry != NULL); 146 | assert(orig_entry != NULL); 147 | 148 | pid = GetCurrentProcessId(); 149 | snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 150 | 151 | if (snap == INVALID_HANDLE_VALUE) { 152 | hr = HRESULT_FROM_WIN32(GetLastError()); 153 | 154 | goto end; 155 | } 156 | 157 | thread.dwSize = sizeof(thread); 158 | ok = Thread32First(snap, &thread); 159 | 160 | if (!ok) { 161 | hr = HRESULT_FROM_WIN32(GetLastError()); 162 | 163 | goto end; 164 | } 165 | 166 | /* Return this if we don't find anything suitable */ 167 | fault = E_FAIL; 168 | 169 | do { 170 | if (thread.th32OwnerProcessID != pid) { 171 | continue; 172 | } 173 | 174 | hr = process_hijack_try_thread( 175 | new_entry, 176 | orig_entry, 177 | thread.th32ThreadID); 178 | 179 | if (hr == S_OK) { 180 | /* Main thread successfully hijacked, finish up */ 181 | goto end; 182 | } else if (FAILED(hr)) { 183 | /* Latch this error code, but don't abort, keep trying. */ 184 | fault = hr; 185 | } 186 | } while (Thread32Next(snap, &thread)); 187 | 188 | hr = fault; 189 | 190 | end: 191 | if (snap != INVALID_HANDLE_VALUE) { 192 | CloseHandle(snap); 193 | } 194 | 195 | return hr; 196 | } 197 | -------------------------------------------------------------------------------- /inject/options.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "inject/options.h" 8 | 9 | void options_help(FILE *f) 10 | { 11 | assert(f != NULL); 12 | 13 | fputs( "Usage: inject [options] program args...\n" 14 | "All options must precede the program name.\n" 15 | "\n" 16 | "The following options are understood:\n" 17 | "\n" 18 | "-h\tPrint this message.\n" 19 | "\n" 20 | "-d\tAttach to target as a debugger and print debug messages.\n" 21 | "\n" 22 | "-p\tPause the target until a debugger attaches to it.\n" 23 | " \tCannot be used with -d.\n" 24 | "\n" 25 | "-w\tWait for target to terminate.\n" 26 | " \tCannot be used with -d.\n" 27 | "\n" 28 | "-k dll\tInject the named DLL into the target process.\n" 29 | " \tCan be specified more than once.\n" 30 | "\n", 31 | f); 32 | } 33 | 34 | HRESULT options_init(struct options *opt, int argc, char **argv) 35 | { 36 | int nconsumed; 37 | const char *arg; 38 | int i; 39 | 40 | assert(opt != NULL); 41 | 42 | memset(opt, 0, sizeof(*opt)); 43 | nconsumed = 1; 44 | 45 | for (i = 1 ; i < argc && argv[i][0] == '-' ; i++) { 46 | arg = argv[i]; 47 | nconsumed++; 48 | 49 | switch (arg[1]) { 50 | case 'h': 51 | opt->help = true; 52 | 53 | break; 54 | 55 | case 'd': 56 | if (opt->debug_pause || opt->wait) { 57 | return E_FAIL; 58 | } 59 | 60 | opt->debug = true; 61 | 62 | break; 63 | 64 | case 'p': 65 | if (opt->debug) { 66 | return E_FAIL; 67 | } 68 | 69 | opt->debug_pause = true; 70 | 71 | break; 72 | 73 | case 'w': 74 | if (opt->debug) { 75 | return E_FAIL; 76 | } 77 | 78 | opt->wait = true; 79 | 80 | break; 81 | 82 | case 'k': 83 | if (i + 1 >= argc) { 84 | return E_FAIL; 85 | } 86 | 87 | /* These get pulled by options_next_dll. Consume its argument as 88 | well though. */ 89 | 90 | nconsumed++; 91 | i++; 92 | 93 | break; 94 | 95 | default: 96 | return E_FAIL; 97 | } 98 | } 99 | 100 | if (nconsumed == argc) { 101 | return E_FAIL; 102 | } 103 | 104 | opt->orig_argc = argc; 105 | opt->orig_argv = argv; 106 | opt->target_argc = argc - nconsumed; 107 | opt->target_argv = argv + nconsumed; 108 | opt->dll_pos = 1; 109 | 110 | return S_OK; 111 | } 112 | 113 | HRESULT options_target_cmdline(const struct options *opt, char **out) 114 | { 115 | char *str; 116 | char *pos; 117 | size_t nchars; 118 | size_t len; 119 | int i; 120 | 121 | assert(opt != NULL); 122 | assert(out != NULL); 123 | 124 | *out = NULL; 125 | 126 | /* Measure string. Each element requires an opening quote, a closing quote 127 | and either a trailing space or a trailing NUL. */ 128 | 129 | nchars = 3 * opt->target_argc; 130 | 131 | for (i = 0 ; i < opt->target_argc ; i++) { 132 | nchars += strlen(opt->target_argv[i]); 133 | } 134 | 135 | str = malloc(nchars); 136 | 137 | if (str == NULL) { 138 | return E_OUTOFMEMORY; 139 | } 140 | 141 | /* Construct string. This doesn't escape quotes within individual args yet 142 | but ugh I'll fix that later if it really becomes necessary, it's a pain 143 | to deal with. */ 144 | 145 | for (i = 0, pos = str ; i < opt->target_argc ; i++) { 146 | len = strlen(opt->target_argv[i]); 147 | 148 | *pos++ = '"'; 149 | memcpy(pos, opt->target_argv[i], len); 150 | pos += len; 151 | *pos++ = '"'; 152 | 153 | if (i + 1 < opt->target_argc) { 154 | *pos++ = ' '; 155 | } else { 156 | *pos++ = '\0'; 157 | } 158 | } 159 | 160 | *out = str; 161 | 162 | return S_OK; 163 | } 164 | 165 | HRESULT options_next_dll(struct options *opt, const char **out) 166 | { 167 | const char *arg; 168 | 169 | assert(opt != NULL); 170 | assert(opt->orig_argv != NULL); 171 | assert(out != NULL); 172 | 173 | *out = NULL; 174 | 175 | while (opt->dll_pos < opt->orig_argc) { 176 | arg = opt->orig_argv[opt->dll_pos]; 177 | 178 | if (arg[0] != '-') { 179 | break; 180 | } 181 | 182 | opt->dll_pos++; 183 | 184 | if (arg[1] == 'k' && opt->dll_pos < opt->orig_argc) { 185 | *out = opt->orig_argv[opt->dll_pos++]; 186 | 187 | return S_OK; 188 | } 189 | } 190 | 191 | return S_FALSE; 192 | } 193 | 194 | -------------------------------------------------------------------------------- /hook/table.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "hook/pe.h" 9 | #include "hook/peb.h" 10 | #include "hook/table.h" 11 | 12 | static const char apiset_prefix[] = "api-ms-win-core-"; 13 | static const size_t apiset_prefix_len = sizeof(apiset_prefix) - 1; 14 | 15 | static void hook_table_apply_to_all( 16 | const char *depname, 17 | const struct hook_symbol *syms, 18 | size_t nsyms); 19 | 20 | static void hook_table_apply_to_iid( 21 | HMODULE target, 22 | const pe_iid_t *iid, 23 | const struct hook_symbol *syms, 24 | size_t nsyms); 25 | 26 | static bool hook_table_match_module( 27 | HMODULE target, 28 | const char *iid_name, 29 | const char *depname); 30 | 31 | static bool hook_table_match_proc( 32 | const struct pe_iat_entry *iate, 33 | const struct hook_symbol *sym); 34 | 35 | static void hook_table_apply_to_all( 36 | const char *depname, 37 | const struct hook_symbol *syms, 38 | size_t nsyms) 39 | { 40 | const peb_dll_t *dll; 41 | HMODULE pe; 42 | 43 | for ( dll = peb_dll_get_first() ; 44 | dll != NULL ; 45 | dll = peb_dll_get_next(dll)) { 46 | pe = peb_dll_get_base(dll); 47 | 48 | if (pe == NULL) { 49 | continue; /* ?? Happens sometimes. */ 50 | } 51 | 52 | hook_table_apply(pe, depname, syms, nsyms); 53 | } 54 | } 55 | 56 | void hook_table_apply( 57 | HMODULE target, 58 | const char *depname, 59 | const struct hook_symbol *syms, 60 | size_t nsyms) 61 | { 62 | const pe_iid_t *iid; 63 | const char *iid_name; 64 | 65 | assert(depname != NULL); 66 | assert(syms != NULL || nsyms == 0); 67 | 68 | if (target == NULL) { 69 | /* Call out, which will then call us back repeatedly. Awkward, but 70 | viewed from the outside it's good for usability. */ 71 | 72 | hook_table_apply_to_all(depname, syms, nsyms); 73 | } else { 74 | for ( iid = pe_iid_get_first(target) ; 75 | iid != NULL ; 76 | iid = pe_iid_get_next(target, iid)) { 77 | iid_name = pe_iid_get_name(target, iid); 78 | 79 | if (hook_table_match_module(target, iid_name, depname)) { 80 | hook_table_apply_to_iid(target, iid, syms, nsyms); 81 | } 82 | } 83 | } 84 | } 85 | 86 | static void hook_table_apply_to_iid( 87 | HMODULE target, 88 | const pe_iid_t *iid, 89 | const struct hook_symbol *syms, 90 | size_t nsyms) 91 | { 92 | struct pe_iat_entry iate; 93 | size_t i; 94 | size_t j; 95 | const struct hook_symbol *sym; 96 | 97 | i = 0; 98 | 99 | while (pe_iid_get_iat_entry(target, iid, i++, &iate) == S_OK) { 100 | for (j = 0 ; j < nsyms ; j++) { 101 | sym = &syms[j]; 102 | 103 | if (hook_table_match_proc(&iate, sym)) { 104 | if (sym->link != NULL && *sym->link == NULL) { 105 | *sym->link = *iate.ppointer; 106 | } 107 | 108 | pe_patch(iate.ppointer, &sym->patch, sizeof(sym->patch)); 109 | } 110 | } 111 | } 112 | } 113 | 114 | static bool hook_table_match_module( 115 | HMODULE target, 116 | const char *iid_name, 117 | const char *depname) 118 | { 119 | HMODULE kernel32; 120 | int result; 121 | 122 | /* OK, first do a straightforward match on the imported DLL name versus 123 | the hook table DLL name. If it succeeds then we're done. */ 124 | 125 | result = _stricmp(iid_name, depname); 126 | 127 | if (result == 0) { 128 | return true; 129 | } 130 | 131 | /* If it failed then we have to check if this hook table targets kernel32. 132 | We have to do some special processing around API sets in that case, so 133 | stop here if kernel32 is not the subject of this hook table. */ 134 | 135 | if (_stricmp(depname, "kernel32.dll") != 0) { 136 | return false; 137 | } 138 | 139 | /* There isn't really any good test for whether a DLL import wants a 140 | concrete DLL or an abstract DLL providing a particular Windows API-set, 141 | so we use a hacky check against the prefix. If the imported DLL name 142 | looks like an apiset then we'll allow kernel32 hook tables to apply. */ 143 | 144 | result = _strnicmp(iid_name, apiset_prefix, apiset_prefix_len); 145 | 146 | if (result != 0) { 147 | return false; 148 | } 149 | 150 | /* ... EXCEPT for the case where we're hooking all DLLs loaded into the 151 | process and we are currently examining kernel32 itself. In that case 152 | there's some weird reference loops issues I don't entirely understand 153 | right now. To avoid those, we just don't apply kernel32 hook tables to 154 | kernel32 itself. */ 155 | 156 | kernel32 = GetModuleHandleW(L"kernel32.dll"); 157 | 158 | if (target == kernel32) { 159 | return false; 160 | } 161 | 162 | return true; 163 | } 164 | 165 | static bool hook_table_match_proc( 166 | const struct pe_iat_entry *iate, 167 | const struct hook_symbol *sym) 168 | { 169 | if ( sym->name != NULL && 170 | iate->name != NULL && 171 | strcmp(sym->name, iate->name) == 0) { 172 | return true; 173 | } 174 | 175 | if (sym->ordinal != 0 && sym->ordinal == iate->ordinal) { 176 | return true; 177 | } 178 | 179 | return false; 180 | } 181 | -------------------------------------------------------------------------------- /hook/pe.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "hook/pe.h" 11 | 12 | static void *pe_offset(void *ptr, size_t off); 13 | static const void *pe_offsetc(const void *ptr, size_t off); 14 | static const IMAGE_NT_HEADERS *pe_get_nt_header(HMODULE pe); 15 | 16 | static void *pe_offset(void *ptr, size_t off) 17 | { 18 | uint8_t *base; 19 | 20 | if (off == 0) { 21 | return NULL; 22 | } 23 | 24 | base = ptr; 25 | 26 | return base + off; 27 | } 28 | 29 | static const void *pe_offsetc(const void *ptr, size_t off) 30 | { 31 | const uint8_t *base; 32 | 33 | if (off == 0) { 34 | return NULL; 35 | } 36 | 37 | base = ptr; 38 | 39 | return base + off; 40 | } 41 | 42 | static const IMAGE_NT_HEADERS *pe_get_nt_header(HMODULE pe) 43 | { 44 | const IMAGE_DOS_HEADER *dh; 45 | const IMAGE_NT_HEADERS *nth; 46 | 47 | dh = (IMAGE_DOS_HEADER *) pe; 48 | nth = pe_offsetc(pe, dh->e_lfanew); 49 | 50 | return nth; 51 | } 52 | 53 | const pe_iid_t *pe_iid_get_first(HMODULE pe) 54 | { 55 | const IMAGE_NT_HEADERS *nth; 56 | const IMAGE_DATA_DIRECTORY *idd; 57 | const IMAGE_IMPORT_DESCRIPTOR *iid; 58 | 59 | assert(pe != NULL); 60 | 61 | nth = pe_get_nt_header(pe); 62 | idd = &nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 63 | iid = pe_offsetc(pe, idd->VirtualAddress); 64 | 65 | if (iid == NULL || iid->Name == 0) { 66 | return NULL; 67 | } 68 | 69 | return iid; 70 | } 71 | 72 | const char *pe_iid_get_name(HMODULE pe, const pe_iid_t *iid) 73 | { 74 | assert(pe != NULL); 75 | assert(iid != NULL); 76 | 77 | return pe_offsetc(pe, iid->Name); 78 | } 79 | 80 | const pe_iid_t *pe_iid_get_next(HMODULE pe, const pe_iid_t *iid) 81 | { 82 | const IMAGE_IMPORT_DESCRIPTOR *iid_next; 83 | 84 | assert(pe != NULL); 85 | assert(iid != NULL); 86 | 87 | iid_next = iid + 1; 88 | 89 | if (iid_next->Name != 0) { 90 | return iid_next; 91 | } else { 92 | return NULL; 93 | } 94 | } 95 | 96 | HRESULT pe_iid_get_iat_entry( 97 | HMODULE pe, 98 | const pe_iid_t *iid, 99 | size_t n, 100 | struct pe_iat_entry *entry) 101 | { 102 | const IMAGE_IMPORT_BY_NAME *import; 103 | intptr_t *import_rvas; 104 | void **pointers; 105 | 106 | assert(pe != NULL); 107 | assert(iid != NULL); 108 | assert(entry != NULL); 109 | 110 | import_rvas = pe_offset(pe, iid->OriginalFirstThunk); 111 | 112 | if (import_rvas[n] == 0) { 113 | /* End of imports */ 114 | memset(entry, 0, sizeof(*entry)); 115 | 116 | return S_FALSE; 117 | } 118 | 119 | if (import_rvas[n] & INTPTR_MIN) { 120 | /* Ordinal import */ 121 | entry->name = NULL; 122 | entry->ordinal = (uint16_t) import_rvas[n]; 123 | } else { 124 | /* Named import */ 125 | import = pe_offsetc(pe, import_rvas[n]); 126 | entry->name = (const char *) import->Name; /* Not an RVA */ 127 | entry->ordinal = 0; 128 | } 129 | 130 | pointers = pe_offset(pe, iid->FirstThunk); 131 | entry->ppointer = &pointers[n]; 132 | 133 | return S_OK; 134 | } 135 | 136 | void *pe_get_export(HMODULE pe, const char *name, uint16_t ord) 137 | { 138 | const IMAGE_NT_HEADERS *nth; 139 | const IMAGE_DATA_DIRECTORY *idd; 140 | const IMAGE_EXPORT_DIRECTORY *ied; 141 | const uint32_t *name_rvas; 142 | const uint32_t *target_rvas; 143 | const char *name_va; 144 | DWORD i; 145 | 146 | assert(pe != NULL); 147 | 148 | nth = pe_get_nt_header(pe); 149 | idd = &nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 150 | ied = pe_offsetc(pe, idd->VirtualAddress); 151 | 152 | name_rvas = pe_offsetc(pe, ied->AddressOfNames); 153 | target_rvas = pe_offsetc(pe, ied->AddressOfFunctions); 154 | 155 | if (name != NULL) { 156 | for (i = 0 ; i < ied->NumberOfNames ; i++) { 157 | if (name_rvas[i] == 0) { 158 | /* Ordinal-only export, cannot match against this */ 159 | continue; 160 | } 161 | 162 | name_va = pe_offsetc(pe, name_rvas[i]); 163 | 164 | if (strcmp(name_va, name) != 0) { 165 | /* Name did not match */ 166 | continue; 167 | } 168 | 169 | return pe_offset(pe, target_rvas[i]); 170 | } 171 | 172 | return NULL; 173 | } else if (ord - ied->Base < ied->NumberOfFunctions) { 174 | return pe_offset(pe, target_rvas[ord - ied->Base]); 175 | } else { 176 | return NULL; 177 | } 178 | } 179 | 180 | void *pe_get_entry_point(HMODULE pe) 181 | { 182 | const IMAGE_NT_HEADERS *nth; 183 | 184 | assert(pe != NULL); 185 | 186 | nth = pe_get_nt_header(pe); 187 | 188 | return pe_offset(pe, nth->OptionalHeader.AddressOfEntryPoint); 189 | } 190 | 191 | HRESULT pe_patch(void *dest, const void *src, size_t nbytes) 192 | { 193 | DWORD old_protect; 194 | BOOL ok; 195 | 196 | assert(dest != NULL); 197 | assert(src != NULL); 198 | 199 | ok = VirtualProtect( 200 | dest, 201 | nbytes, 202 | PAGE_EXECUTE_READWRITE, 203 | &old_protect); 204 | 205 | if (!ok) { 206 | return HRESULT_FROM_WIN32(GetLastError()); 207 | } 208 | 209 | memcpy(dest, src, nbytes); 210 | 211 | ok = VirtualProtect( 212 | dest, 213 | nbytes, 214 | old_protect, 215 | &old_protect); 216 | 217 | if (!ok) { 218 | return HRESULT_FROM_WIN32(GetLastError()); 219 | } 220 | 221 | return S_OK; 222 | } 223 | -------------------------------------------------------------------------------- /hook/com-proxy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "hook/com-proxy.h" 9 | 10 | static void com_proxy_free(struct com_proxy *proxy); 11 | static HRESULT STDMETHODCALLTYPE com_proxy_query_interface( 12 | IUnknown *unk, 13 | REFIID iid, 14 | void **iface); 15 | static ULONG STDMETHODCALLTYPE com_proxy_addref(IUnknown *unk); 16 | static ULONG STDMETHODCALLTYPE com_proxy_release(IUnknown *unk); 17 | 18 | #ifdef __amd64 19 | 20 | /***** 64-BIT TRAMPOLINE *****/ 21 | 22 | #define SLOT_OFFSET 0x0A 23 | static const uint8_t com_proxy_tramp[] = { 24 | /* mov rcx, [rcx+8] ; Get this->real */ 25 | 0x48, 0x8B, 0x49, 0x08, 26 | 27 | /* mov rax, [rcx] ; Get this->vtbl */ 28 | 0x48, 0x8B, 0x01, 29 | 30 | /* mov rax, [rax+XX] ; Get vtbl->slot_XX */ 31 | 0x48, 0x8B, 0x80, -1, -1, -1, -1, 32 | 33 | /* jmp rax ; Continue to slot_XX */ 34 | 0xFF, 0xE0, 35 | }; 36 | 37 | #else 38 | 39 | /***** 32-BIT TRAMPOLINE *****/ 40 | 41 | #define SLOT_OFFSET 0x0F 42 | static const uint8_t com_proxy_tramp[] = { 43 | /* mov eax, [esp+4] ; Get this */ 44 | 0x8B, 0x44, 0x24, 0x04, 45 | 46 | /* mov eax, [eax+4] ; Get this->real */ 47 | 0x8B, 0x40, 0x04, 48 | 49 | /* mov [esp+4], eax ; Replace this with this->real on stack */ 50 | 0x89, 0x44, 0x24, 0x04, 51 | 52 | /* mov ecx, [eax] ; Get this->vtbl */ 53 | 0x8B, 0x08, 54 | 55 | /* mov ecx, [ecx+XX] ; Get vtbl->slot_XX */ 56 | 0x8B, 0x89, -1, -1, -1, -1, 57 | 58 | /* jmp ecx ; Continue to slot_XX */ 59 | 0xFF, 0xE1 60 | }; 61 | 62 | #endif 63 | 64 | HRESULT com_proxy_wrap( 65 | struct com_proxy **out, 66 | void *real, 67 | size_t vtbl_size) 68 | { 69 | struct com_proxy *proxy; 70 | void **vtbl; 71 | uint8_t *cur_tramp; 72 | size_t nslots; 73 | size_t i; 74 | HRESULT hr; 75 | 76 | assert(out != NULL); 77 | assert(real != NULL); 78 | 79 | *out = NULL; 80 | 81 | proxy = calloc(1, sizeof(*proxy)); 82 | 83 | if (proxy == NULL) { 84 | hr = E_OUTOFMEMORY; 85 | 86 | goto end; 87 | } 88 | 89 | proxy->vptr = malloc(vtbl_size); 90 | 91 | if (proxy->vptr == NULL) { 92 | hr = E_OUTOFMEMORY; 93 | 94 | goto end; 95 | } 96 | 97 | nslots = vtbl_size / sizeof(void *); 98 | 99 | proxy->tramps = VirtualAlloc( 100 | NULL, 101 | sizeof(com_proxy_tramp) * nslots, 102 | MEM_RESERVE | MEM_COMMIT, 103 | PAGE_EXECUTE_READWRITE); 104 | 105 | if (proxy->tramps == NULL) { 106 | hr = E_OUTOFMEMORY; 107 | 108 | goto end; 109 | } 110 | 111 | proxy->real = real; 112 | 113 | /* Set up proxied IUnknown impl */ 114 | 115 | vtbl = proxy->vptr; 116 | vtbl[0] = com_proxy_query_interface; 117 | vtbl[1] = com_proxy_addref; 118 | vtbl[2] = com_proxy_release; 119 | 120 | /* Populate trampoline code for remaining vtbl entries */ 121 | 122 | for (i = 3 /* Skip IUnknown */ ; i < nslots ; i++) { 123 | cur_tramp = proxy->tramps + i * sizeof(com_proxy_tramp); 124 | 125 | /* Copy template */ 126 | memcpy(cur_tramp, com_proxy_tramp, sizeof(com_proxy_tramp)); 127 | 128 | /* Patch XX into vtbl lookup (see definition of tramp) */ 129 | *((uint32_t *) (cur_tramp + SLOT_OFFSET)) = i * sizeof(void *); 130 | 131 | /* Set vtable entry */ 132 | vtbl[i] = cur_tramp; 133 | } 134 | 135 | *out = proxy; 136 | proxy = NULL; 137 | hr = S_OK; 138 | 139 | end: 140 | com_proxy_free(proxy); 141 | 142 | return hr; 143 | } 144 | 145 | static void com_proxy_free(struct com_proxy *proxy) 146 | { 147 | if (proxy == NULL) { 148 | return; 149 | } 150 | 151 | if (proxy->cleanup_ctx != NULL) { 152 | proxy->cleanup_ctx(proxy->ctx); 153 | } 154 | 155 | if (proxy->tramps != NULL) { 156 | VirtualFree(proxy->tramps, 0, MEM_RELEASE); 157 | } 158 | 159 | free(proxy->vptr); 160 | free(proxy); 161 | } 162 | 163 | static HRESULT STDMETHODCALLTYPE com_proxy_query_interface( 164 | IUnknown *unk, 165 | REFIID iid, 166 | void **iface) 167 | { 168 | struct com_proxy *proxy; 169 | IUnknown *obj; 170 | 171 | assert(unk != NULL); 172 | 173 | proxy = (struct com_proxy *) unk; 174 | obj = proxy->real; /* Not necessarily this object's canonical IUnknown */ 175 | 176 | /* To some extent, COM is designed to support shennanigans like these. 177 | We can safely pass the call straight through to the underlying 178 | interface pointer because of the following: 179 | 180 | "It is specifically not the case that queries for interfaces other 181 | than IUnknown (even the same interface through the same pointer) 182 | must return the same pointer value." 183 | 184 | - MSDN documentation for IUnknown::QueryInterface() 185 | 186 | Of course, pretty much everyone screws up COM's conventions (probably 187 | including me in this very module to be honest), so if someone ends up 188 | relying on broken assumptions then this could well get a lot more 189 | complicated. */ 190 | 191 | return IUnknown_QueryInterface(obj, iid, iface); 192 | } 193 | 194 | static ULONG STDMETHODCALLTYPE com_proxy_addref(IUnknown *unk) 195 | { 196 | struct com_proxy *proxy; 197 | IUnknown *obj; 198 | 199 | assert(unk != NULL); 200 | 201 | proxy = (struct com_proxy *) unk; 202 | obj = proxy->real; 203 | 204 | return IUnknown_AddRef(obj); 205 | } 206 | 207 | static ULONG STDMETHODCALLTYPE com_proxy_release(IUnknown *unk) 208 | { 209 | struct com_proxy *proxy; 210 | IUnknown *real; 211 | ULONG result; 212 | 213 | assert(unk != NULL); 214 | 215 | proxy = (struct com_proxy *) unk; 216 | real = proxy->real; 217 | result = IUnknown_Release(real); 218 | 219 | if (!result) { 220 | /* Last ref to underlying object released */ 221 | com_proxy_free(proxy); 222 | } 223 | 224 | return result; 225 | } 226 | -------------------------------------------------------------------------------- /inject/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "inject/debug.h" 9 | #include "inject/options.h" 10 | 11 | static HRESULT inject_dll(HANDLE process, const char *dll_name); 12 | static HRESULT inject_pause(HANDLE process); 13 | static HRESULT inject_resume(HANDLE thread); 14 | 15 | int main(int argc, char **argv) 16 | { 17 | struct options opt; 18 | char *cmdline; 19 | const char *hook_dll; 20 | PROCESS_INFORMATION pi; 21 | STARTUPINFO si; 22 | HRESULT hr; 23 | BOOL ok; 24 | 25 | hr = options_init(&opt, argc, argv); 26 | 27 | if (FAILED(hr) || opt.help) { 28 | options_help(stderr); 29 | 30 | return EXIT_FAILURE; 31 | } 32 | 33 | cmdline = NULL; 34 | hr = options_target_cmdline(&opt, &cmdline); 35 | 36 | if (FAILED(hr)) { 37 | goto end; 38 | } 39 | 40 | memset(&pi, 0, sizeof(pi)); 41 | memset(&si, 0, sizeof(si)); 42 | si.cb = sizeof(si); 43 | 44 | ok = CreateProcessA( 45 | NULL, 46 | cmdline, 47 | NULL, 48 | NULL, 49 | FALSE, 50 | CREATE_SUSPENDED, 51 | NULL, 52 | NULL, 53 | &si, 54 | &pi); 55 | 56 | if (!ok) { 57 | hr = HRESULT_FROM_WIN32(GetLastError()); 58 | fprintf(stderr, "Failed to launch executable: %x\n", (int) hr); 59 | 60 | goto end; 61 | } 62 | 63 | while (options_next_dll(&opt, &hook_dll) == S_OK) { 64 | hr = inject_dll(pi.hProcess, hook_dll); 65 | 66 | if (FAILED(hr)) { 67 | goto end; 68 | } 69 | } 70 | 71 | if (opt.debug_pause) { 72 | hr = inject_pause(pi.hProcess); 73 | 74 | if (FAILED(hr)) { 75 | goto end; 76 | } 77 | } 78 | 79 | if (opt.debug) { 80 | ok = DebugActiveProcess(pi.dwProcessId); 81 | 82 | if (!ok) { 83 | hr = HRESULT_FROM_WIN32(GetLastError()); 84 | fprintf(stderr, "DebugActiveProcess failed: %x\n", (int) hr); 85 | 86 | goto end; 87 | } 88 | } 89 | 90 | hr = inject_resume(pi.hThread); 91 | 92 | if (FAILED(hr)) { 93 | goto end; 94 | } 95 | 96 | if (opt.debug) { 97 | hr = debug_main(pi.hProcess, pi.dwProcessId); 98 | } 99 | 100 | if (opt.wait) { 101 | WaitForSingleObject(pi.hProcess, INFINITE); 102 | } 103 | 104 | end: 105 | if (pi.hProcess != NULL) { 106 | if (FAILED(hr)) { 107 | TerminateProcess(pi.hProcess, EXIT_FAILURE); 108 | } 109 | 110 | CloseHandle(pi.hProcess); 111 | } 112 | 113 | if (pi.hThread != NULL) { 114 | CloseHandle(pi.hThread); 115 | } 116 | 117 | free(cmdline); 118 | 119 | return FAILED(hr) ? EXIT_FAILURE : EXIT_SUCCESS; 120 | } 121 | 122 | static HRESULT inject_dll(HANDLE process, const char *dll_name) 123 | { 124 | size_t nchars; 125 | void *remote_addr; 126 | HANDLE remote_thread; 127 | DWORD found; 128 | DWORD result; 129 | HRESULT hr; 130 | BOOL ok; 131 | 132 | remote_addr = NULL; 133 | remote_thread = NULL; 134 | 135 | found = SearchPathA(NULL, dll_name, NULL, 0, NULL, NULL); 136 | 137 | if (found == 0) { 138 | hr = HRESULT_FROM_WIN32(GetLastError()); 139 | fprintf(stderr, "%s: Hook DLL not found: %x\n", dll_name, (int) hr); 140 | 141 | goto end; 142 | } 143 | 144 | nchars = strlen(dll_name); 145 | 146 | remote_addr = VirtualAllocEx( 147 | process, 148 | NULL, 149 | nchars + 1, 150 | MEM_RESERVE | MEM_COMMIT, 151 | PAGE_READWRITE); 152 | 153 | if (remote_addr == NULL) { 154 | hr = HRESULT_FROM_WIN32(GetLastError()); 155 | fprintf(stderr, "VirtualAllocEx failed: %x\n", (int) hr); 156 | 157 | goto end; 158 | } 159 | 160 | ok = WriteProcessMemory( 161 | process, 162 | remote_addr, 163 | dll_name, 164 | nchars + 1, 165 | NULL); 166 | 167 | if (!ok) { 168 | hr = HRESULT_FROM_WIN32(GetLastError()); 169 | fprintf(stderr, "WriteProcessMemory failed: %x\n", (int) hr); 170 | 171 | goto end; 172 | } 173 | 174 | remote_thread = CreateRemoteThread( 175 | process, 176 | NULL, 177 | 0, 178 | (LPTHREAD_START_ROUTINE) LoadLibraryA, 179 | remote_addr, 180 | 0, 181 | NULL); 182 | 183 | if (remote_thread == NULL) { 184 | hr = HRESULT_FROM_WIN32(GetLastError()); 185 | fprintf(stderr, "CreateRemoteThread failed: %x\n", (int) hr); 186 | 187 | goto end; 188 | } 189 | 190 | result = WaitForSingleObject(remote_thread, INFINITE); 191 | 192 | if (result != WAIT_OBJECT_0) { 193 | hr = HRESULT_FROM_WIN32(GetLastError()); 194 | fprintf(stderr, "WaitForSingleObject failed: %x\n", (int) hr); 195 | 196 | goto end; 197 | } 198 | 199 | /* Reusing `result` var for something unrelated.. ech. sloppy. */ 200 | ok = GetExitCodeThread(remote_thread, &result); 201 | 202 | if (!ok) { 203 | hr = HRESULT_FROM_WIN32(GetLastError()); 204 | fprintf(stderr, "GetExitCodeThread failed: %x\n", (int) hr); 205 | 206 | goto end; 207 | } 208 | 209 | if (result == 0) { 210 | hr = E_FAIL; 211 | fprintf(stderr, 212 | "%s: DLL failed to load inside target process\n", 213 | dll_name); 214 | 215 | goto end; 216 | } 217 | 218 | hr = S_OK; 219 | 220 | end: 221 | if (remote_thread != NULL) { 222 | CloseHandle(remote_thread); 223 | } 224 | 225 | if (remote_addr != NULL) { 226 | ok = VirtualFreeEx(process, remote_addr, 0, MEM_RELEASE); 227 | 228 | if (!ok) { 229 | fprintf(stderr, "VirtualFreeEx failed\n"); 230 | } 231 | } 232 | 233 | return hr; 234 | } 235 | 236 | static HRESULT inject_pause(HANDLE process) 237 | { 238 | HRESULT hr; 239 | BOOL present; 240 | BOOL ok; 241 | 242 | printf("Waiting for debugger to attach.\n"); 243 | 244 | do { 245 | Sleep(1000); 246 | ok = CheckRemoteDebuggerPresent(process, &present); 247 | 248 | if (!ok) { 249 | hr = HRESULT_FROM_WIN32(GetLastError()); 250 | fprintf(stderr, "CheckRemoteDebuggerPresent failed: %x\n", (int)hr); 251 | 252 | return hr; 253 | } 254 | } while (!present); 255 | 256 | printf("Debugger attached, resuming\n"); 257 | 258 | return S_OK; 259 | } 260 | 261 | static HRESULT inject_resume(HANDLE thread) 262 | { 263 | DWORD result; 264 | HRESULT hr; 265 | 266 | result = ResumeThread(thread); 267 | 268 | if (result == -1) { 269 | hr = HRESULT_FROM_WIN32(GetLastError()); 270 | fprintf(stderr, "Failed to resume target thread: %x\n", (int) hr); 271 | 272 | return hr; 273 | } 274 | 275 | return S_OK; 276 | } 277 | 278 | 279 | -------------------------------------------------------------------------------- /hooklib/uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __GNUC__ 4 | #include 5 | #else 6 | #include 7 | #endif 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "hook/iobuf.h" 16 | #include "hook/iohook.h" 17 | 18 | #include "hooklib/uart.h" 19 | 20 | static HRESULT uart_handle_open(struct uart *uart, struct irp *irp); 21 | static HRESULT uart_handle_close(struct uart *uart, struct irp *irp); 22 | static HRESULT uart_handle_read(struct uart *uart, struct irp *irp); 23 | static HRESULT uart_handle_write(struct uart *uart, struct irp *irp); 24 | static HRESULT uart_handle_ioctl(struct uart *uart, struct irp *irp); 25 | 26 | void uart_init(struct uart *uart, unsigned int port_no) 27 | { 28 | assert(uart != NULL); 29 | assert(port_no > 0); 30 | 31 | uart->fd = NULL; 32 | uart->port_no = port_no; 33 | 34 | uart->baud.BaudRate = 115200; 35 | 36 | memset(&uart->status, 0, sizeof(uart->status)); 37 | 38 | uart->chars.EofChar = 0x00; 39 | uart->chars.ErrorChar = 0x00; 40 | uart->chars.BreakChar = 0x00; 41 | uart->chars.EventChar = 0x00; 42 | uart->chars.XonChar = 0x11; 43 | uart->chars.XoffChar = 0x13; 44 | 45 | memset(&uart->handflow, 0, sizeof(uart->handflow)); 46 | 47 | uart->line.StopBits = STOP_BIT_1; 48 | uart->line.Parity = NO_PARITY; 49 | uart->line.WordLength = 8; 50 | 51 | memset(&uart->timeouts, 0, sizeof(uart->timeouts)); 52 | 53 | uart->mask = 0; 54 | 55 | memset(&uart->written, 0, sizeof(uart->written)); 56 | memset(&uart->readable, 0, sizeof(uart->readable)); 57 | } 58 | 59 | void uart_fini(struct uart *uart) 60 | { 61 | struct irp irp; 62 | 63 | assert(uart != NULL); 64 | 65 | if (uart->fd != NULL) { 66 | memset(&irp, 0, sizeof(irp)); 67 | irp.op = IRP_OP_CLOSE; 68 | irp.fd = uart->fd; 69 | 70 | /* Not much we can do if this fails */ 71 | iohook_invoke_next(&irp); 72 | } 73 | } 74 | 75 | bool uart_match_irp(const struct uart *uart, const struct irp *irp) 76 | { 77 | const wchar_t *path; 78 | unsigned int port_no; 79 | wchar_t wc; 80 | size_t i; 81 | 82 | if (irp->op == IRP_OP_OPEN) { 83 | /* Win32 device nodes can unfortunately be identified using a variety 84 | of different syntax */ 85 | 86 | path = irp->open_filename; 87 | 88 | if ( wcsncmp(path, L"\\\\.\\", 4) == 0 || 89 | wcsncmp(path, L"\\\\?\\", 4) == 0 || 90 | wcsncmp(path, L"\\??\\", 4) == 0 ) { 91 | /* NT style */ 92 | 93 | path = path + 4; 94 | 95 | if ( (path[0] & ~0x20) != L'C' || 96 | (path[1] & ~0x20) != L'O' || 97 | (path[2] & ~0x20) != L'M' ) { 98 | return false; 99 | } 100 | 101 | port_no = 0; 102 | 103 | for (i = 3 ; path[i] ; i++) { 104 | wc = path[i]; 105 | 106 | if (wc < L'0' || wc > L'9') { 107 | return false; 108 | } 109 | 110 | port_no *= 10; 111 | port_no += wc - L'0'; 112 | } 113 | } else { 114 | /* DOS style. Only COM1 through COM9 are supported. */ 115 | 116 | if ( (path[0] & ~0x20) != L'C' || 117 | (path[1] & ~0x20) != L'O' || 118 | (path[2] & ~0x20) != L'M' || 119 | (path[3] < L'1' || path[3] > L'9') ) { 120 | return false; 121 | } 122 | 123 | /* DOS-style COM port names are allowed to have an optional trailing 124 | colon, as if this wasn't complicated enough. */ 125 | 126 | if ( (path[4] != L'\0') && 127 | (path[4] != L':' || path[5] != L'\0') ) { 128 | return false; 129 | } 130 | 131 | port_no = path[3] - L'0'; 132 | } 133 | 134 | return port_no == uart->port_no; 135 | } else { 136 | /* All other IRPs are matched by checking the file descriptor. */ 137 | 138 | return irp->fd == uart->fd; 139 | } 140 | } 141 | 142 | HRESULT uart_handle_irp(struct uart *uart, struct irp *irp) 143 | { 144 | assert(uart != NULL); 145 | assert(irp != NULL); 146 | 147 | switch (irp->op) { 148 | case IRP_OP_OPEN: return uart_handle_open(uart, irp); 149 | case IRP_OP_CLOSE: return uart_handle_close(uart, irp); 150 | case IRP_OP_READ: return uart_handle_read(uart, irp); 151 | case IRP_OP_WRITE: return uart_handle_write(uart, irp); 152 | case IRP_OP_IOCTL: return uart_handle_ioctl(uart, irp); 153 | case IRP_OP_FSYNC: return S_OK; 154 | default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); 155 | } 156 | } 157 | 158 | static HRESULT uart_handle_open(struct uart *uart, struct irp *irp) 159 | { 160 | HRESULT hr; 161 | 162 | if (uart->fd != NULL) { 163 | /* Windows only allows one handle to be open for each COM port at a 164 | time. Strangely enough it returns an Access Denied error in this 165 | situation instead of something more appropriate. */ 166 | 167 | return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); 168 | } 169 | 170 | /* Transform this open call so that it opens the NUL device, then pass it 171 | on. This gives us a real Win32 HANDLE distinct from all other open 172 | HANDLEs. */ 173 | 174 | irp->open_filename = L"NUL"; 175 | irp->open_access = GENERIC_READ | GENERIC_WRITE; 176 | irp->open_share = FILE_SHARE_READ | FILE_SHARE_WRITE; 177 | irp->open_sa = NULL; 178 | irp->open_creation = OPEN_EXISTING; 179 | irp->open_flags = FILE_FLAG_OVERLAPPED; 180 | irp->open_tmpl = NULL; 181 | 182 | hr = iohook_invoke_next(irp); 183 | 184 | if (SUCCEEDED(hr)) { 185 | uart->fd = irp->fd; 186 | } 187 | 188 | return hr; 189 | } 190 | 191 | static HRESULT uart_handle_close(struct uart *uart, struct irp *irp) 192 | { 193 | uart->fd = NULL; 194 | 195 | return iohook_invoke_next(irp); 196 | } 197 | 198 | static HRESULT uart_handle_read(struct uart *uart, struct irp *irp) 199 | { 200 | /* This does a memmove() under the covers. Less efficient than a ring 201 | buffer, but I don't expect this to matter in the common case where the 202 | entire buffer gets drained, particularly since UARTs are decidedly 203 | low-speed devices anyway. */ 204 | 205 | iobuf_shift(&irp->read, &uart->readable); 206 | 207 | return S_OK; 208 | } 209 | 210 | static HRESULT uart_handle_write(struct uart *uart, struct irp *irp) 211 | { 212 | iobuf_move(&uart->written, &irp->write); 213 | 214 | return S_OK; 215 | } 216 | 217 | static HRESULT uart_handle_ioctl(struct uart *uart, struct irp *irp) 218 | { 219 | switch (irp->ioctl) { 220 | case IOCTL_SERIAL_GET_BAUD_RATE: 221 | return iobuf_write(&irp->read, &uart->baud, sizeof(uart->baud)); 222 | 223 | case IOCTL_SERIAL_GET_CHARS: 224 | return iobuf_write(&irp->read, &uart->chars, sizeof(uart->chars)); 225 | 226 | case IOCTL_SERIAL_GET_COMMSTATUS: 227 | uart->status.AmountInInQueue = uart->readable.pos; 228 | uart->status.AmountInOutQueue = uart->written.pos; 229 | 230 | return iobuf_write(&irp->read, &uart->status, sizeof(uart->status)); 231 | 232 | case IOCTL_SERIAL_GET_HANDFLOW: 233 | return iobuf_write(&irp->read, &uart->handflow, sizeof(uart->handflow)); 234 | 235 | case IOCTL_SERIAL_GET_LINE_CONTROL: 236 | return iobuf_write(&irp->read, &uart->line, sizeof(uart->line)); 237 | 238 | case IOCTL_SERIAL_GET_TIMEOUTS: 239 | return iobuf_write(&irp->read, &uart->timeouts, sizeof(uart->timeouts)); 240 | 241 | case IOCTL_SERIAL_GET_WAIT_MASK: 242 | return iobuf_write(&irp->read, &uart->mask, sizeof(uart->mask)); 243 | 244 | case IOCTL_SERIAL_SET_BAUD_RATE: 245 | return iobuf_read(&irp->write, &uart->baud, sizeof(uart->baud)); 246 | 247 | case IOCTL_SERIAL_SET_CHARS: 248 | return iobuf_read(&irp->write, &uart->chars, sizeof(uart->chars)); 249 | 250 | case IOCTL_SERIAL_SET_HANDFLOW: 251 | return iobuf_read(&irp->write, &uart->handflow, sizeof(uart->handflow)); 252 | 253 | case IOCTL_SERIAL_SET_LINE_CONTROL: 254 | return iobuf_read(&irp->write, &uart->line, sizeof(uart->line)); 255 | 256 | case IOCTL_SERIAL_SET_TIMEOUTS: 257 | return iobuf_read(&irp->write, &uart->timeouts, sizeof(uart->timeouts)); 258 | 259 | case IOCTL_SERIAL_SET_WAIT_MASK: 260 | return iobuf_read(&irp->write, &uart->mask, sizeof(uart->mask)); 261 | 262 | /* These can be safely ignored */ 263 | case IOCTL_SERIAL_SET_BREAK_ON: 264 | case IOCTL_SERIAL_SET_BREAK_OFF: 265 | case IOCTL_SERIAL_CLR_DTR: 266 | case IOCTL_SERIAL_CLR_RTS: 267 | case IOCTL_SERIAL_SET_DTR: 268 | case IOCTL_SERIAL_SET_RTS: 269 | case IOCTL_SERIAL_SET_XOFF: 270 | case IOCTL_SERIAL_SET_XON: 271 | 272 | case IOCTL_SERIAL_PURGE: 273 | case IOCTL_SERIAL_SET_QUEUE_SIZE: 274 | return S_OK; 275 | 276 | default: 277 | return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /hook/iobuf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "hook/iobuf.h" 5 | 6 | void iobuf_flip(struct const_iobuf *child, struct iobuf *parent) 7 | { 8 | assert(child != NULL); 9 | assert(parent != NULL); 10 | 11 | child->bytes = parent->bytes; 12 | child->pos = 0; 13 | child->nbytes = parent->pos; 14 | } 15 | 16 | size_t iobuf_move(struct iobuf *dest, struct const_iobuf *src) 17 | { 18 | size_t dest_avail; 19 | size_t src_avail; 20 | size_t chunksz; 21 | 22 | assert(dest != NULL); 23 | assert(dest->bytes != NULL || dest->nbytes == 0); 24 | assert(dest->pos <= dest->nbytes); 25 | 26 | assert(src != NULL); 27 | assert(src->bytes != NULL || src->nbytes == 0); 28 | assert(src->pos <= src->nbytes); 29 | 30 | dest_avail = dest->nbytes - dest->pos; 31 | src_avail = src->nbytes - src->pos; 32 | chunksz = dest_avail < src_avail ? dest_avail : src_avail; 33 | 34 | memcpy(&dest->bytes[dest->pos], &src->bytes[src->pos], chunksz); 35 | 36 | dest->pos += chunksz; 37 | src->pos += chunksz; 38 | 39 | return chunksz; 40 | } 41 | 42 | size_t iobuf_shift(struct iobuf *dest, struct iobuf *src) 43 | { 44 | struct const_iobuf span; 45 | 46 | assert(dest != NULL); 47 | assert(src != NULL); 48 | 49 | iobuf_flip(&span, src); 50 | iobuf_move(dest, &span); 51 | 52 | memmove(src->bytes, &src->bytes[span.pos], span.nbytes - span.pos); 53 | src->pos -= span.pos; 54 | 55 | return span.pos; 56 | } 57 | 58 | HRESULT iobuf_read(struct const_iobuf *src, void *bytes, size_t nbytes) 59 | { 60 | assert(src != NULL); 61 | assert(bytes != NULL || nbytes == 0); 62 | 63 | if (src->pos + nbytes > src->nbytes) { 64 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 65 | } 66 | 67 | memcpy(bytes, &src->bytes[src->pos], nbytes); 68 | src->pos += nbytes; 69 | 70 | return S_OK; 71 | } 72 | 73 | HRESULT iobuf_read_8(struct const_iobuf *src, uint8_t *out) 74 | { 75 | assert(src != NULL); 76 | assert(out != NULL); 77 | 78 | if (src->pos + sizeof(uint8_t) > src->nbytes) { 79 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 80 | } 81 | 82 | *out = src->bytes[src->pos++]; 83 | 84 | return S_OK; 85 | } 86 | 87 | HRESULT iobuf_read_be16(struct const_iobuf *src, uint16_t *out) 88 | { 89 | uint16_t value; 90 | 91 | assert(src != NULL); 92 | assert(out != NULL); 93 | 94 | if (src->pos + sizeof(uint16_t) > src->nbytes) { 95 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 96 | } 97 | 98 | value = src->bytes[src->pos++] << 8; 99 | value |= src->bytes[src->pos++]; 100 | 101 | *out = value; 102 | 103 | return S_OK; 104 | } 105 | 106 | HRESULT iobuf_read_be32(struct const_iobuf *src, uint32_t *out) 107 | { 108 | uint32_t value; 109 | 110 | assert(src != NULL); 111 | assert(out != NULL); 112 | 113 | if (src->pos + sizeof(uint32_t) > src->nbytes) { 114 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 115 | } 116 | 117 | value = src->bytes[src->pos++] << 24; 118 | value |= src->bytes[src->pos++] << 16; 119 | value |= src->bytes[src->pos++] << 8; 120 | value |= src->bytes[src->pos++]; 121 | 122 | *out = value; 123 | 124 | return S_OK; 125 | } 126 | 127 | HRESULT iobuf_read_be64(struct const_iobuf *src, uint64_t *out) 128 | { 129 | uint64_t value; 130 | 131 | assert(src != NULL); 132 | assert(out != NULL); 133 | 134 | if (src->pos + sizeof(uint64_t) > src->nbytes) { 135 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 136 | } 137 | 138 | value = ((uint64_t) src->bytes[src->pos++]) << 56; 139 | value |= ((uint64_t) src->bytes[src->pos++]) << 48; 140 | value |= ((uint64_t) src->bytes[src->pos++]) << 40; 141 | value |= ((uint64_t) src->bytes[src->pos++]) << 32; 142 | value |= ((uint64_t) src->bytes[src->pos++]) << 24; 143 | value |= ((uint64_t) src->bytes[src->pos++]) << 16; 144 | value |= ((uint64_t) src->bytes[src->pos++]) << 8; 145 | value |= ((uint64_t) src->bytes[src->pos++]); 146 | 147 | *out = value; 148 | 149 | return S_OK; 150 | } 151 | 152 | HRESULT iobuf_read_le16(struct const_iobuf *src, uint16_t *out) 153 | { 154 | uint16_t value; 155 | 156 | assert(src != NULL); 157 | assert(out != NULL); 158 | 159 | if (src->pos + sizeof(uint16_t) > src->nbytes) { 160 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 161 | } 162 | 163 | value = src->bytes[src->pos++]; 164 | value |= src->bytes[src->pos++] << 8; 165 | 166 | *out = value; 167 | 168 | return S_OK; 169 | } 170 | 171 | HRESULT iobuf_read_le32(struct const_iobuf *src, uint32_t *out) 172 | { 173 | uint32_t value; 174 | 175 | assert(src != NULL); 176 | assert(out != NULL); 177 | 178 | if (src->pos + sizeof(uint32_t) > src->nbytes) { 179 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 180 | } 181 | 182 | value = src->bytes[src->pos++]; 183 | value |= src->bytes[src->pos++] << 8; 184 | value |= src->bytes[src->pos++] << 16; 185 | value |= src->bytes[src->pos++] << 24; 186 | 187 | *out = value; 188 | 189 | return S_OK; 190 | } 191 | 192 | HRESULT iobuf_read_le64(struct const_iobuf *src, uint64_t *out) 193 | { 194 | uint64_t value; 195 | 196 | assert(src != NULL); 197 | assert(out != NULL); 198 | 199 | if (src->pos + sizeof(uint64_t) > src->nbytes) { 200 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 201 | } 202 | 203 | value = ((uint64_t) src->bytes[src->pos++]); 204 | value |= ((uint64_t) src->bytes[src->pos++]) << 8; 205 | value |= ((uint64_t) src->bytes[src->pos++]) << 16; 206 | value |= ((uint64_t) src->bytes[src->pos++]) << 24; 207 | value |= ((uint64_t) src->bytes[src->pos++]) << 32; 208 | value |= ((uint64_t) src->bytes[src->pos++]) << 40; 209 | value |= ((uint64_t) src->bytes[src->pos++]) << 48; 210 | value |= ((uint64_t) src->bytes[src->pos++]) << 56; 211 | 212 | *out = value; 213 | 214 | return S_OK; 215 | } 216 | 217 | HRESULT iobuf_write(struct iobuf *dest, const void *bytes, size_t nbytes) 218 | { 219 | assert(dest != NULL); 220 | assert(bytes != NULL || nbytes == 0); 221 | 222 | if (dest->pos + nbytes > dest->nbytes) { 223 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 224 | } 225 | 226 | memcpy(&dest->bytes[dest->pos], bytes, nbytes); 227 | dest->pos += nbytes; 228 | 229 | return S_OK; 230 | } 231 | 232 | HRESULT iobuf_write_8(struct iobuf *dest, uint8_t value) 233 | { 234 | assert(dest != NULL); 235 | 236 | if (dest->pos + sizeof(uint8_t) > dest->nbytes) { 237 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 238 | } 239 | 240 | dest->bytes[dest->pos++] = value; 241 | 242 | return S_OK; 243 | } 244 | 245 | HRESULT iobuf_write_be16(struct iobuf *dest, uint16_t value) 246 | { 247 | assert(dest != NULL); 248 | 249 | if (dest->pos + sizeof(uint16_t) > dest->nbytes) { 250 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 251 | } 252 | 253 | dest->bytes[dest->pos++] = value >> 8; 254 | dest->bytes[dest->pos++] = value; 255 | 256 | return S_OK; 257 | } 258 | 259 | HRESULT iobuf_write_be32(struct iobuf *dest, uint32_t value) 260 | { 261 | assert(dest != NULL); 262 | 263 | if (dest->pos + sizeof(uint32_t) > dest->nbytes) { 264 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 265 | } 266 | 267 | dest->bytes[dest->pos++] = value >> 24; 268 | dest->bytes[dest->pos++] = value >> 16; 269 | dest->bytes[dest->pos++] = value >> 8; 270 | dest->bytes[dest->pos++] = value; 271 | 272 | return S_OK; 273 | } 274 | 275 | HRESULT iobuf_write_be64(struct iobuf *dest, uint64_t value) 276 | { 277 | assert(dest != NULL); 278 | 279 | if (dest->pos + sizeof(uint64_t) > dest->nbytes) { 280 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 281 | } 282 | 283 | dest->bytes[dest->pos++] = value >> 56; 284 | dest->bytes[dest->pos++] = value >> 48; 285 | dest->bytes[dest->pos++] = value >> 40; 286 | dest->bytes[dest->pos++] = value >> 32; 287 | dest->bytes[dest->pos++] = value >> 24; 288 | dest->bytes[dest->pos++] = value >> 16; 289 | dest->bytes[dest->pos++] = value >> 8; 290 | dest->bytes[dest->pos++] = value; 291 | 292 | return S_OK; 293 | } 294 | 295 | HRESULT iobuf_write_le16(struct iobuf *dest, uint16_t value) 296 | { 297 | assert(dest != NULL); 298 | 299 | if (dest->pos + sizeof(uint16_t) > dest->nbytes) { 300 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 301 | } 302 | 303 | dest->bytes[dest->pos++] = value; 304 | dest->bytes[dest->pos++] = value >> 8; 305 | 306 | return S_OK; 307 | } 308 | 309 | HRESULT iobuf_write_le32(struct iobuf *dest, uint32_t value) 310 | { 311 | assert(dest != NULL); 312 | 313 | if (dest->pos + sizeof(uint32_t) > dest->nbytes) { 314 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 315 | } 316 | 317 | dest->bytes[dest->pos++] = value; 318 | dest->bytes[dest->pos++] = value >> 8; 319 | dest->bytes[dest->pos++] = value >> 16; 320 | dest->bytes[dest->pos++] = value >> 24; 321 | 322 | return S_OK; 323 | } 324 | 325 | HRESULT iobuf_write_le64(struct iobuf *dest, uint64_t value) 326 | { 327 | assert(dest != NULL); 328 | 329 | if (dest->pos + sizeof(uint64_t) > dest->nbytes) { 330 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 331 | } 332 | 333 | dest->bytes[dest->pos++] = value; 334 | dest->bytes[dest->pos++] = value >> 8; 335 | dest->bytes[dest->pos++] = value >> 16; 336 | dest->bytes[dest->pos++] = value >> 24; 337 | dest->bytes[dest->pos++] = value >> 32; 338 | dest->bytes[dest->pos++] = value >> 40; 339 | dest->bytes[dest->pos++] = value >> 48; 340 | dest->bytes[dest->pos++] = value >> 56; 341 | 342 | return S_OK; 343 | } 344 | -------------------------------------------------------------------------------- /hooklib/serial.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __GNUC__ 4 | #include 5 | #else 6 | #include 7 | #endif 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "hook/hr.h" 16 | #include "hook/iohook.h" 17 | #include "hook/table.h" 18 | 19 | #include "hooklib/serial.h" 20 | 21 | /* RS232 API hooks */ 22 | 23 | static BOOL WINAPI my_ClearCommBreak(HANDLE fd); 24 | static BOOL WINAPI my_ClearCommError( 25 | HANDLE fd, 26 | uint32_t *errors, 27 | COMSTAT *status); 28 | static BOOL WINAPI my_EscapeCommFunction(HANDLE fd, uint32_t func); 29 | static BOOL WINAPI my_GetCommMask(HANDLE fd, uint32_t *out); 30 | static BOOL WINAPI my_GetCommState(HANDLE fd, DCB *dcb); 31 | static BOOL WINAPI my_GetCommTimeouts(HANDLE fd, COMMTIMEOUTS *dest); 32 | static BOOL WINAPI my_PurgeComm(HANDLE fd, uint32_t flags); 33 | static BOOL WINAPI my_SetCommMask(HANDLE fd, uint32_t mask); 34 | static BOOL WINAPI my_SetCommState(HANDLE fd, const DCB *dcb); 35 | static BOOL WINAPI my_SetCommTimeouts(HANDLE fd, COMMTIMEOUTS *timeouts); 36 | static BOOL WINAPI my_SetupComm(HANDLE fd, uint32_t in_q, uint32_t out_q); 37 | static BOOL WINAPI my_SetCommBreak(HANDLE fd); 38 | 39 | static struct hook_symbol serial_syms[] = { 40 | { 41 | .name = "ClearCommError", 42 | .patch = my_ClearCommError, 43 | }, { 44 | .name = "EscapeCommFunction", 45 | .patch = my_EscapeCommFunction, 46 | }, { 47 | .name = "GetCommMask", 48 | .patch = my_GetCommMask, 49 | }, { 50 | .name = "GetCommState", 51 | .patch = my_GetCommState, 52 | }, { 53 | .name = "GetCommTimeouts", 54 | .patch = my_GetCommTimeouts, 55 | }, { 56 | .name = "PurgeComm", 57 | .patch = my_PurgeComm, 58 | }, { 59 | .name = "SetCommMask", 60 | .patch = my_SetCommMask, 61 | }, { 62 | .name = "SetCommState", 63 | .patch = my_SetCommState, 64 | }, { 65 | .name = "SetCommTimeouts", 66 | .patch = my_SetCommTimeouts, 67 | }, { 68 | .name = "SetupComm", 69 | .patch = my_SetupComm, 70 | }, { 71 | .name = "SetCommBreak", 72 | .patch = my_SetCommBreak, 73 | }, { 74 | .name = "ClearCommBreak", 75 | .patch = my_ClearCommBreak, 76 | }, 77 | }; 78 | 79 | static bool serial_hook_initted; 80 | 81 | void serial_hook_init(void) 82 | { 83 | if (serial_hook_initted) { 84 | return; 85 | } 86 | 87 | hook_table_apply(NULL, "kernel32.dll", serial_syms, _countof(serial_syms)); 88 | serial_hook_initted = true; 89 | } 90 | 91 | static BOOL WINAPI my_ClearCommError( 92 | HANDLE fd, 93 | uint32_t *errors, 94 | COMSTAT *status) 95 | { 96 | struct irp irp; 97 | SERIAL_STATUS llstatus; 98 | HRESULT hr; 99 | 100 | memset(&irp, 0, sizeof(irp)); 101 | irp.op = IRP_OP_IOCTL; 102 | irp.fd = fd; 103 | irp.ioctl = IOCTL_SERIAL_GET_COMMSTATUS; 104 | irp.read.bytes = (uint8_t *) &llstatus; 105 | irp.read.nbytes = sizeof(llstatus); 106 | 107 | hr = iohook_invoke_next(&irp); 108 | 109 | if (FAILED(hr)) { 110 | return hr_propagate_win32(hr, FALSE); 111 | } 112 | 113 | /* Here we just translate between two structures that carry essentially the 114 | same information, because Windows. */ 115 | 116 | if (errors != NULL) { 117 | *errors = 0; 118 | 119 | if (llstatus.Errors & SERIAL_ERROR_QUEUEOVERRUN) { 120 | *errors |= CE_OVERRUN; 121 | } 122 | 123 | if (llstatus.Errors & SERIAL_ERROR_OVERRUN) { 124 | *errors |= CE_RXOVER; 125 | } 126 | 127 | if (llstatus.Errors & SERIAL_ERROR_BREAK) { 128 | *errors |= CE_BREAK; 129 | } 130 | 131 | if (llstatus.Errors & SERIAL_ERROR_PARITY) { 132 | *errors |= CE_RXPARITY; 133 | } 134 | 135 | if (llstatus.Errors & SERIAL_ERROR_FRAMING) { 136 | *errors |= CE_FRAME; 137 | } 138 | } 139 | 140 | if (status != NULL) { 141 | memset(status, 0, sizeof(*status)); 142 | 143 | if (llstatus.HoldReasons & SERIAL_TX_WAITING_FOR_CTS) { 144 | status->fCtsHold = 1; 145 | } 146 | 147 | if (llstatus.HoldReasons & SERIAL_TX_WAITING_FOR_DSR) { 148 | status->fDsrHold = 1; 149 | } 150 | 151 | if (llstatus.HoldReasons & SERIAL_TX_WAITING_FOR_DCD) { 152 | status->fRlsdHold = 1; 153 | } 154 | 155 | if (llstatus.HoldReasons & SERIAL_TX_WAITING_FOR_XON) { 156 | status->fXoffHold = 1; 157 | } 158 | 159 | if (llstatus.HoldReasons & SERIAL_TX_WAITING_ON_BREAK) { 160 | /* hrm. No corresponding (documented field). */ 161 | } 162 | 163 | if (llstatus.HoldReasons & SERIAL_TX_WAITING_XOFF_SENT) { 164 | status->fXoffSent = 1; 165 | } 166 | 167 | if (llstatus.EofReceived) { 168 | status->fEof = 1; 169 | } 170 | 171 | if (llstatus.WaitForImmediate) { 172 | status->fTxim = 1; 173 | } 174 | 175 | status->cbInQue = llstatus.AmountInInQueue; 176 | status->cbOutQue = llstatus.AmountInOutQueue; 177 | } 178 | 179 | return TRUE; 180 | } 181 | 182 | static BOOL WINAPI my_EscapeCommFunction(HANDLE fd, uint32_t cmd) 183 | { 184 | struct irp irp; 185 | uint32_t ioctl; 186 | HRESULT hr; 187 | 188 | switch (cmd) { 189 | case CLRBREAK: ioctl = IOCTL_SERIAL_SET_BREAK_OFF; break; 190 | case CLRDTR: ioctl = IOCTL_SERIAL_CLR_DTR; break; 191 | case CLRRTS: ioctl = IOCTL_SERIAL_CLR_RTS; break; 192 | case SETBREAK: ioctl = IOCTL_SERIAL_SET_BREAK_ON; break; 193 | case SETDTR: ioctl = IOCTL_SERIAL_SET_DTR; break; 194 | case SETRTS: ioctl = IOCTL_SERIAL_SET_RTS; break; 195 | case SETXOFF: ioctl = IOCTL_SERIAL_SET_XOFF; break; 196 | case SETXON: ioctl = IOCTL_SERIAL_SET_XON; break; 197 | default: 198 | SetLastError(ERROR_INVALID_PARAMETER); 199 | 200 | return FALSE; 201 | } 202 | 203 | memset(&irp, 0, sizeof(irp)); 204 | irp.op = IRP_OP_IOCTL; 205 | irp.fd = fd; 206 | irp.ioctl = ioctl; 207 | 208 | hr = iohook_invoke_next(&irp); 209 | 210 | if (FAILED(hr)) { 211 | return hr_propagate_win32(hr, FALSE); 212 | } 213 | 214 | return TRUE; 215 | } 216 | 217 | static BOOL WINAPI my_GetCommMask(HANDLE fd, uint32_t *out) 218 | { 219 | struct irp irp; 220 | uint32_t mask; 221 | HRESULT hr; 222 | 223 | if (out == NULL) { 224 | SetLastError(ERROR_INVALID_PARAMETER); 225 | 226 | return FALSE; 227 | } 228 | 229 | memset(&irp, 0, sizeof(irp)); 230 | irp.op = IRP_OP_IOCTL; 231 | irp.fd = fd; 232 | irp.ioctl = IOCTL_SERIAL_GET_WAIT_MASK; 233 | irp.read.bytes = (uint8_t *) &mask; 234 | irp.read.nbytes = sizeof(mask); 235 | 236 | hr = iohook_invoke_next(&irp); 237 | 238 | if (FAILED(hr)) { 239 | return hr_propagate_win32(hr, FALSE); 240 | } 241 | 242 | SetLastError(ERROR_SUCCESS); 243 | 244 | return TRUE; 245 | } 246 | 247 | static BOOL WINAPI my_GetCommState(HANDLE fd, DCB *dcb) 248 | { 249 | struct irp irp; 250 | SERIAL_BAUD_RATE baud; 251 | SERIAL_CHARS chars; 252 | SERIAL_HANDFLOW handflow; 253 | SERIAL_LINE_CONTROL line; 254 | HRESULT hr; 255 | 256 | /* Validate params. Despite what MSDN has to say on the matter, the 257 | DCBlength field is not validated (and is indeed overwritten) by the real 258 | implementation of this function. */ 259 | 260 | if (dcb == NULL) { 261 | SetLastError(ERROR_INVALID_PARAMETER); 262 | 263 | return FALSE; 264 | } 265 | 266 | /* Issue ioctls */ 267 | 268 | memset(&irp, 0, sizeof(irp)); 269 | irp.op = IRP_OP_IOCTL; 270 | irp.fd = fd; 271 | irp.ioctl = IOCTL_SERIAL_GET_BAUD_RATE; 272 | irp.read.bytes = (uint8_t *) &baud; 273 | irp.read.nbytes = sizeof(baud); 274 | memset(&baud, 0, sizeof(baud)); 275 | 276 | hr = iohook_invoke_next(&irp); 277 | 278 | if (FAILED(hr)) { 279 | return hr_propagate_win32(hr, FALSE); 280 | } 281 | 282 | memset(&irp, 0, sizeof(irp)); 283 | irp.op = IRP_OP_IOCTL; 284 | irp.fd = fd; 285 | irp.ioctl = IOCTL_SERIAL_GET_HANDFLOW; 286 | irp.read.bytes = (uint8_t *) &handflow; 287 | irp.read.nbytes = sizeof(handflow); 288 | memset(&handflow, 0, sizeof(handflow)); 289 | 290 | hr = iohook_invoke_next(&irp); 291 | 292 | if (FAILED(hr)) { 293 | return hr_propagate_win32(hr, FALSE); 294 | } 295 | 296 | memset(&irp, 0, sizeof(irp)); 297 | irp.op = IRP_OP_IOCTL; 298 | irp.fd = fd; 299 | irp.ioctl = IOCTL_SERIAL_GET_LINE_CONTROL; 300 | irp.read.bytes = (uint8_t *) &line; 301 | irp.read.nbytes = sizeof(line); 302 | memset(&line, 0, sizeof(line)); 303 | 304 | hr = iohook_invoke_next(&irp); 305 | 306 | if (FAILED(hr)) { 307 | return hr_propagate_win32(hr, FALSE); 308 | } 309 | 310 | memset(&irp, 0, sizeof(irp)); 311 | irp.op = IRP_OP_IOCTL; 312 | irp.fd = fd; 313 | irp.ioctl = IOCTL_SERIAL_GET_CHARS; 314 | irp.read.bytes = (uint8_t *) &chars; 315 | irp.read.nbytes = sizeof(chars); 316 | memset(&chars, 0, sizeof(chars)); 317 | 318 | hr = iohook_invoke_next(&irp); 319 | 320 | if (FAILED(hr)) { 321 | return hr_propagate_win32(hr, FALSE); 322 | } 323 | 324 | /* Populate output struct */ 325 | 326 | memset(dcb, 0, sizeof(*dcb)); 327 | dcb->DCBlength = sizeof(*dcb); 328 | dcb->fBinary = 1; 329 | dcb->BaudRate = baud.BaudRate; 330 | /* Populate fParity somehow? */ 331 | 332 | if (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) { 333 | dcb->fOutxCtsFlow = 1; 334 | } 335 | 336 | if (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) { 337 | dcb->fOutxDsrFlow = 1; 338 | } 339 | 340 | if (handflow.ControlHandShake & SERIAL_DTR_CONTROL) { 341 | dcb->fDtrControl = DTR_CONTROL_ENABLE; 342 | } 343 | 344 | if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) { 345 | dcb->fDtrControl = DTR_CONTROL_HANDSHAKE; 346 | } 347 | 348 | if (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) { 349 | dcb->fDsrSensitivity = 1; 350 | } 351 | 352 | if (handflow.ControlHandShake & SERIAL_XOFF_CONTINUE) { 353 | dcb->fTXContinueOnXoff = 1; 354 | } 355 | 356 | if (handflow.ControlHandShake & SERIAL_RTS_CONTROL) { 357 | dcb->fRtsControl = RTS_CONTROL_ENABLE; 358 | } 359 | 360 | if (handflow.ControlHandShake & SERIAL_RTS_HANDSHAKE) { 361 | dcb->fRtsControl = RTS_CONTROL_HANDSHAKE; 362 | } 363 | 364 | if (handflow.ControlHandShake & SERIAL_ERROR_ABORT) { 365 | dcb->fAbortOnError = 1; 366 | } 367 | 368 | if (handflow.ControlHandShake & SERIAL_ERROR_CHAR) { 369 | dcb->fErrorChar = 1; 370 | } 371 | 372 | if (handflow.ControlHandShake & SERIAL_NULL_STRIPPING) { 373 | dcb->fNull = 1; 374 | } 375 | 376 | dcb->XonLim = handflow.XonLimit; 377 | dcb->XoffLim = handflow.XoffLimit; 378 | dcb->ByteSize = line.WordLength; 379 | dcb->Parity = line.Parity; 380 | dcb->StopBits = line.StopBits; 381 | dcb->XonChar = chars.XonChar; 382 | dcb->XoffChar = chars.XoffChar; 383 | dcb->ErrorChar = chars.ErrorChar; 384 | dcb->EofChar = chars.EofChar; 385 | dcb->EvtChar = chars.EventChar; 386 | 387 | SetLastError(ERROR_SUCCESS); 388 | 389 | return TRUE; 390 | } 391 | 392 | static BOOL WINAPI my_GetCommTimeouts(HANDLE fd, COMMTIMEOUTS *dest) 393 | { 394 | struct irp irp; 395 | SERIAL_TIMEOUTS src; 396 | HRESULT hr; 397 | 398 | if (dest == NULL) { 399 | SetLastError(ERROR_INVALID_PARAMETER); 400 | 401 | return FALSE; 402 | } 403 | 404 | memset(&irp, 0, sizeof(irp)); 405 | irp.op = IRP_OP_IOCTL; 406 | irp.fd = fd; 407 | irp.ioctl = IOCTL_SERIAL_GET_TIMEOUTS; 408 | irp.read.bytes = (uint8_t *) &src; 409 | irp.read.nbytes = sizeof(src); 410 | 411 | hr = iohook_invoke_next(&irp); 412 | 413 | if (FAILED(hr)) { 414 | return hr_propagate_win32(hr, FALSE); 415 | } 416 | 417 | dest->ReadIntervalTimeout = src.ReadIntervalTimeout; 418 | dest->ReadTotalTimeoutMultiplier = src.ReadTotalTimeoutMultiplier; 419 | dest->ReadTotalTimeoutConstant = src.ReadTotalTimeoutConstant; 420 | dest->WriteTotalTimeoutMultiplier = src.WriteTotalTimeoutMultiplier; 421 | dest->WriteTotalTimeoutConstant = src.WriteTotalTimeoutConstant; 422 | 423 | SetLastError(ERROR_SUCCESS); 424 | 425 | return TRUE; 426 | } 427 | 428 | static BOOL WINAPI my_PurgeComm(HANDLE fd, uint32_t flags) 429 | { 430 | struct irp irp; 431 | HRESULT hr; 432 | 433 | memset(&irp, 0, sizeof(irp)); 434 | irp.op = IRP_OP_IOCTL; 435 | irp.fd = fd; 436 | irp.ioctl = IOCTL_SERIAL_PURGE; 437 | irp.write.bytes = (uint8_t *) &flags; 438 | irp.write.nbytes = sizeof(flags); 439 | 440 | hr = iohook_invoke_next(&irp); 441 | 442 | if (FAILED(hr)) { 443 | return hr_propagate_win32(hr, FALSE); 444 | } 445 | 446 | SetLastError(ERROR_SUCCESS); 447 | 448 | return TRUE; 449 | } 450 | 451 | static BOOL WINAPI my_SetCommMask(HANDLE fd, uint32_t mask) 452 | { 453 | struct irp irp; 454 | HRESULT hr; 455 | 456 | memset(&irp, 0, sizeof(irp)); 457 | irp.op = IRP_OP_IOCTL; 458 | irp.fd = fd; 459 | irp.ioctl = IOCTL_SERIAL_SET_WAIT_MASK; 460 | irp.write.bytes = (uint8_t *) &mask; 461 | irp.write.nbytes = sizeof(mask); 462 | 463 | hr = iohook_invoke_next(&irp); 464 | 465 | if (FAILED(hr)) { 466 | return hr_propagate_win32(hr, FALSE); 467 | } 468 | 469 | SetLastError(ERROR_SUCCESS); 470 | 471 | return TRUE; 472 | } 473 | 474 | static BOOL WINAPI my_SetCommState(HANDLE fd, const DCB *dcb) 475 | { 476 | struct irp irp; 477 | SERIAL_BAUD_RATE baud; 478 | SERIAL_CHARS chars; 479 | SERIAL_HANDFLOW handflow; 480 | SERIAL_LINE_CONTROL line; 481 | HRESULT hr; 482 | 483 | if (dcb == NULL || dcb->DCBlength != sizeof(*dcb)) { 484 | /* This struct has evolved in the past, but those were the Windows 95 485 | days. So we only support the latest size of this struct. */ 486 | SetLastError(ERROR_INVALID_PARAMETER); 487 | 488 | return FALSE; 489 | } 490 | 491 | memset(&baud, 0, sizeof(baud)); 492 | baud.BaudRate = dcb->BaudRate; 493 | 494 | memset(&handflow, 0, sizeof(handflow)); 495 | 496 | if (dcb->fOutxCtsFlow) { 497 | handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE; 498 | } 499 | 500 | if (dcb->fOutxDsrFlow) { 501 | handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE; 502 | } 503 | 504 | switch (dcb->fDtrControl) { 505 | case DTR_CONTROL_DISABLE: 506 | break; 507 | 508 | case DTR_CONTROL_ENABLE: 509 | handflow.ControlHandShake |= SERIAL_DTR_CONTROL; 510 | 511 | break; 512 | 513 | case DTR_CONTROL_HANDSHAKE: 514 | handflow.ControlHandShake |= SERIAL_DTR_HANDSHAKE; 515 | 516 | break; 517 | 518 | default: 519 | SetLastError(ERROR_INVALID_PARAMETER); 520 | 521 | return FALSE; 522 | } 523 | 524 | if (dcb->fDsrSensitivity) { 525 | handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY; 526 | } 527 | 528 | if (dcb->fTXContinueOnXoff) { 529 | handflow.ControlHandShake |= SERIAL_XOFF_CONTINUE; 530 | } 531 | 532 | switch (dcb->fRtsControl) { 533 | case RTS_CONTROL_DISABLE: 534 | break; 535 | 536 | case RTS_CONTROL_ENABLE: 537 | handflow.ControlHandShake |= SERIAL_RTS_CONTROL; 538 | 539 | break; 540 | 541 | case RTS_CONTROL_HANDSHAKE: 542 | handflow.ControlHandShake |= SERIAL_RTS_HANDSHAKE; 543 | 544 | break; 545 | 546 | default: 547 | SetLastError(ERROR_INVALID_PARAMETER); 548 | 549 | return FALSE; 550 | } 551 | 552 | memset(&line, 0, sizeof(line)); 553 | line.WordLength = dcb->ByteSize; 554 | line.Parity = dcb->Parity; 555 | line.StopBits = dcb->StopBits; 556 | 557 | memset(&chars, 0, sizeof(chars)); 558 | chars.XonChar = dcb->XonChar; 559 | chars.XoffChar = dcb->XoffChar; 560 | chars.ErrorChar = dcb->ErrorChar; 561 | chars.EofChar = dcb->EofChar; 562 | chars.EventChar = dcb->EvtChar; 563 | 564 | /* Parameters populated and validated, commit new settings */ 565 | 566 | memset(&irp, 0, sizeof(irp)); 567 | irp.op = IRP_OP_IOCTL; 568 | irp.fd = fd; 569 | irp.ioctl = IOCTL_SERIAL_SET_BAUD_RATE; 570 | irp.write.bytes = (uint8_t *) &baud; 571 | irp.write.nbytes = sizeof(baud); 572 | 573 | hr = iohook_invoke_next(&irp); 574 | 575 | if (FAILED(hr)) { 576 | return hr_propagate_win32(hr, FALSE); 577 | } 578 | 579 | memset(&irp, 0, sizeof(irp)); 580 | irp.op = IRP_OP_IOCTL; 581 | irp.fd = fd; 582 | irp.ioctl = IOCTL_SERIAL_SET_HANDFLOW; 583 | irp.write.bytes = (uint8_t *) &handflow; 584 | irp.write.nbytes = sizeof(handflow); 585 | 586 | hr = iohook_invoke_next(&irp); 587 | 588 | if (FAILED(hr)) { 589 | return hr_propagate_win32(hr, FALSE); 590 | } 591 | 592 | memset(&irp, 0, sizeof(irp)); 593 | irp.op = IRP_OP_IOCTL; 594 | irp.fd = fd; 595 | irp.ioctl = IOCTL_SERIAL_SET_LINE_CONTROL; 596 | irp.write.bytes = (uint8_t *) &line; 597 | irp.write.nbytes = sizeof(line); 598 | 599 | hr = iohook_invoke_next(&irp); 600 | 601 | if (FAILED(hr)) { 602 | return hr_propagate_win32(hr, FALSE); 603 | } 604 | 605 | memset(&irp, 0, sizeof(irp)); 606 | irp.op = IRP_OP_IOCTL; 607 | irp.fd = fd; 608 | irp.ioctl = IOCTL_SERIAL_SET_CHARS; 609 | irp.write.bytes = (uint8_t *) &chars; 610 | irp.write.nbytes = sizeof(chars); 611 | 612 | hr = iohook_invoke_next(&irp); 613 | 614 | if (FAILED(hr)) { 615 | return hr_propagate_win32(hr, FALSE); 616 | } 617 | 618 | SetLastError(ERROR_SUCCESS); 619 | 620 | return TRUE; 621 | } 622 | 623 | static BOOL WINAPI my_SetCommTimeouts(HANDLE fd, COMMTIMEOUTS *src) 624 | { 625 | struct irp irp; 626 | SERIAL_TIMEOUTS dest; 627 | HRESULT hr; 628 | 629 | if (src == NULL) { 630 | SetLastError(ERROR_INVALID_PARAMETER); 631 | 632 | return FALSE; 633 | } 634 | 635 | dest.ReadIntervalTimeout = src->ReadIntervalTimeout; 636 | dest.ReadTotalTimeoutMultiplier = src->ReadTotalTimeoutMultiplier; 637 | dest.ReadTotalTimeoutConstant = src->ReadTotalTimeoutConstant; 638 | dest.WriteTotalTimeoutMultiplier = src->WriteTotalTimeoutMultiplier; 639 | dest.WriteTotalTimeoutConstant = src->WriteTotalTimeoutConstant; 640 | 641 | memset(&irp, 0, sizeof(irp)); 642 | irp.op = IRP_OP_IOCTL; 643 | irp.fd = fd; 644 | irp.ioctl = IOCTL_SERIAL_SET_TIMEOUTS; 645 | irp.write.bytes = (uint8_t *) &dest; 646 | irp.write.nbytes = sizeof(dest); 647 | 648 | hr = iohook_invoke_next(&irp); 649 | 650 | if (FAILED(hr)) { 651 | return hr_propagate_win32(hr, FALSE); 652 | } 653 | 654 | SetLastError(ERROR_SUCCESS); 655 | 656 | return TRUE; 657 | } 658 | 659 | static BOOL WINAPI my_SetupComm(HANDLE fd, uint32_t in_q, uint32_t out_q) 660 | { 661 | struct irp irp; 662 | SERIAL_QUEUE_SIZE qs; 663 | HRESULT hr; 664 | 665 | qs.InSize = in_q; 666 | qs.OutSize = out_q; 667 | 668 | memset(&irp, 0, sizeof(irp)); 669 | irp.op = IRP_OP_IOCTL; 670 | irp.fd = fd; 671 | irp.ioctl = IOCTL_SERIAL_SET_QUEUE_SIZE; 672 | irp.write.bytes = (uint8_t *) &qs; 673 | irp.write.nbytes = sizeof(qs); 674 | 675 | hr = iohook_invoke_next(&irp); 676 | 677 | if (FAILED(hr)) { 678 | return hr_propagate_win32(hr, FALSE); 679 | } 680 | 681 | SetLastError(ERROR_SUCCESS); 682 | 683 | return TRUE; 684 | } 685 | 686 | static BOOL WINAPI my_SetCommBreak(HANDLE fd) 687 | { 688 | struct irp irp; 689 | HRESULT hr; 690 | 691 | memset(&irp, 0, sizeof(irp)); 692 | irp.op = IRP_OP_IOCTL; 693 | irp.fd = fd; 694 | irp.ioctl = IOCTL_SERIAL_SET_BREAK_ON; 695 | 696 | hr = iohook_invoke_next(&irp); 697 | 698 | if (FAILED(hr)) { 699 | return hr_propagate_win32(hr, FALSE); 700 | } 701 | 702 | SetLastError(ERROR_SUCCESS); 703 | 704 | return TRUE; 705 | } 706 | 707 | static BOOL WINAPI my_ClearCommBreak(HANDLE fd) 708 | { 709 | struct irp irp; 710 | HRESULT hr; 711 | 712 | memset(&irp, 0, sizeof(irp)); 713 | irp.op = IRP_OP_IOCTL; 714 | irp.fd = fd; 715 | irp.ioctl = IOCTL_SERIAL_SET_BREAK_OFF; 716 | 717 | hr = iohook_invoke_next(&irp); 718 | 719 | if (FAILED(hr)) { 720 | return hr_propagate_win32(hr, FALSE); 721 | } 722 | 723 | SetLastError(ERROR_SUCCESS); 724 | 725 | return TRUE; 726 | } 727 | -------------------------------------------------------------------------------- /hook/iohook.c: -------------------------------------------------------------------------------- 1 | #define WIN32_NO_STATUS 2 | /* See precompiled.h for more information */ 3 | #include 4 | #undef WIN32_NO_STATUS 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "hook/hr.h" 18 | #include "hook/iohook.h" 19 | #include "hook/table.h" 20 | 21 | /* Helpers */ 22 | 23 | static void iohook_init(void); 24 | static BOOL iohook_overlapped_result( 25 | uint32_t *syncout, 26 | OVERLAPPED *ovl, 27 | uint32_t value); 28 | 29 | static HRESULT iohook_invoke_real(struct irp *irp); 30 | static HRESULT iohook_invoke_real_open(struct irp *irp); 31 | static HRESULT iohook_invoke_real_close(struct irp *irp); 32 | static HRESULT iohook_invoke_real_read(struct irp *irp); 33 | static HRESULT iohook_invoke_real_write(struct irp *irp); 34 | static HRESULT iohook_invoke_real_seek(struct irp *irp); 35 | static HRESULT iohook_invoke_real_fsync(struct irp *irp); 36 | static HRESULT iohook_invoke_real_ioctl(struct irp *irp); 37 | 38 | /* API hooks. We take some liberties with function signatures here (e.g. 39 | stdint.h types instead of DWORD and LARGE_INTEGER et al). */ 40 | 41 | static BOOL WINAPI iohook_CloseHandle(HANDLE fd); 42 | 43 | static HANDLE WINAPI iohook_CreateFileW( 44 | const wchar_t *lpFileName, 45 | uint32_t dwDesiredAccess, 46 | uint32_t dwShareMode, 47 | SECURITY_ATTRIBUTES *lpSecurityAttributes, 48 | uint32_t dwCreationDisposition, 49 | uint32_t dwFlagsAndAttributes, 50 | HANDLE hTemplateFile); 51 | 52 | static HANDLE WINAPI iohook_CreateFileA( 53 | const char *lpFileName, 54 | uint32_t dwDesiredAccess, 55 | uint32_t dwShareMode, 56 | SECURITY_ATTRIBUTES *lpSecurityAttributes, 57 | uint32_t dwCreationDisposition, 58 | uint32_t dwFlagsAndAttributes, 59 | HANDLE hTemplateFile); 60 | 61 | static BOOL WINAPI iohook_ReadFile( 62 | HANDLE hFile, 63 | void *lpBuffer, 64 | uint32_t nNumberOfBytesToRead, 65 | uint32_t *lpNumberOfBytesRead, 66 | OVERLAPPED *lpOverlapped); 67 | 68 | static BOOL WINAPI iohook_WriteFile( 69 | HANDLE hFile, 70 | const void *lpBuffer, 71 | uint32_t nNumberOfBytesToWrite, 72 | uint32_t *lpNumberOfBytesWritten, 73 | OVERLAPPED *lpOverlapped); 74 | 75 | static DWORD WINAPI iohook_SetFilePointer( 76 | HANDLE hFile, 77 | int32_t lDistanceToMove, 78 | int32_t *lpDistanceToMoveHigh, 79 | uint32_t dwMoveMethod); 80 | 81 | static BOOL WINAPI iohook_SetFilePointerEx( 82 | HANDLE hFile, 83 | int64_t liDistanceToMove, 84 | uint64_t *lpNewFilePointer, 85 | uint32_t dwMoveMethod); 86 | 87 | static BOOL WINAPI iohook_FlushFileBuffers(HANDLE hFile); 88 | 89 | static BOOL WINAPI iohook_DeviceIoControl( 90 | HANDLE hFile, 91 | uint32_t dwIoControlCode, 92 | void *lpInBuffer, 93 | uint32_t nInBufferSize, 94 | void *lpOutBuffer, 95 | uint32_t nOutBufferSize, 96 | uint32_t *lpBytesReturned, 97 | OVERLAPPED *lpOverlapped); 98 | 99 | /* Links */ 100 | 101 | static BOOL (WINAPI *next_CloseHandle)(HANDLE fd); 102 | 103 | static HANDLE (WINAPI *next_CreateFileA)( 104 | const char *lpFileName, 105 | uint32_t dwDesiredAccess, 106 | uint32_t dwShareMode, 107 | SECURITY_ATTRIBUTES *lpSecurityAttributes, 108 | uint32_t dwCreationDisposition, 109 | uint32_t dwFlagsAndAttributes, 110 | HANDLE hTemplateFile); 111 | 112 | static HANDLE (WINAPI *next_CreateFileW)( 113 | const wchar_t *filename, 114 | uint32_t access, 115 | uint32_t share, 116 | SECURITY_ATTRIBUTES *sa, 117 | uint32_t creation, 118 | uint32_t flags, 119 | HANDLE tmpl); 120 | 121 | static BOOL (WINAPI *next_DeviceIoControl)( 122 | HANDLE fd, 123 | uint32_t code, 124 | void *in_bytes, 125 | uint32_t in_nbytes, 126 | void *out_bytes, 127 | uint32_t out_nbytes, 128 | uint32_t *out_returned, 129 | OVERLAPPED *ovl); 130 | 131 | static BOOL (WINAPI *next_ReadFile)( 132 | HANDLE fd, 133 | void *buf, 134 | uint32_t nbytes, 135 | uint32_t *nread, 136 | OVERLAPPED *ovl); 137 | 138 | static BOOL (WINAPI *next_WriteFile)( 139 | HANDLE fd, 140 | const void *buf, 141 | uint32_t nbytes, 142 | uint32_t *nwrit, 143 | OVERLAPPED *ovl); 144 | 145 | static DWORD (WINAPI *next_SetFilePointer)( 146 | HANDLE hFile, 147 | int32_t lDistanceToMove, 148 | int32_t *lpDistanceToMoveHigh, 149 | uint32_t dwMoveMethod); 150 | 151 | static BOOL (WINAPI *next_SetFilePointerEx)( 152 | HANDLE hFile, 153 | int64_t liDistanceToMove, 154 | uint64_t *lpNewFilePointer, 155 | uint32_t dwMoveMethod); 156 | 157 | static BOOL (WINAPI *next_FlushFileBuffers)(HANDLE fd); 158 | 159 | /* Hook symbol table */ 160 | 161 | static const struct hook_symbol iohook_kernel32_syms[] = { 162 | { 163 | .name = "CloseHandle", 164 | .patch = iohook_CloseHandle, 165 | .link = (void *) &next_CloseHandle, 166 | }, { 167 | .name = "CreateFileA", 168 | .patch = iohook_CreateFileA, 169 | .link = (void *) &next_CreateFileA, 170 | }, { 171 | .name = "CreateFileW", 172 | .patch = iohook_CreateFileW, 173 | .link = (void *) &next_CreateFileW, 174 | }, { 175 | .name = "DeviceIoControl", 176 | .patch = iohook_DeviceIoControl, 177 | .link = (void *) &next_DeviceIoControl, 178 | }, { 179 | .name = "ReadFile", 180 | .patch = iohook_ReadFile, 181 | .link = (void *) &next_ReadFile, 182 | }, { 183 | .name = "WriteFile", 184 | .patch = iohook_WriteFile, 185 | .link = (void *) &next_WriteFile, 186 | }, { 187 | .name = "SetFilePointer", 188 | .patch = iohook_SetFilePointer, 189 | .link = (void *) &next_SetFilePointer, 190 | }, { 191 | .name = "SetFilePointerEx", 192 | .patch = iohook_SetFilePointerEx, 193 | .link = (void *) &next_SetFilePointerEx, 194 | }, { 195 | .name = "FlushFileBuffers", 196 | .patch = iohook_FlushFileBuffers, 197 | .link = (void *) &next_FlushFileBuffers, 198 | }, 199 | }; 200 | 201 | static const iohook_fn_t iohook_real_handlers[] = { 202 | [IRP_OP_OPEN] = iohook_invoke_real_open, 203 | [IRP_OP_CLOSE] = iohook_invoke_real_close, 204 | [IRP_OP_READ] = iohook_invoke_real_read, 205 | [IRP_OP_WRITE] = iohook_invoke_real_write, 206 | [IRP_OP_SEEK] = iohook_invoke_real_seek, 207 | [IRP_OP_FSYNC] = iohook_invoke_real_fsync, 208 | [IRP_OP_IOCTL] = iohook_invoke_real_ioctl, 209 | }; 210 | 211 | static bool iohook_initted; 212 | static CRITICAL_SECTION iohook_lock; 213 | static iohook_fn_t *iohook_handlers; 214 | static size_t iohook_nhandlers; 215 | 216 | static void iohook_init(void) 217 | { 218 | HMODULE kernel32; 219 | 220 | /* Permit repeated initializations. This isn't atomic because the whole IAT 221 | insertion dance is extremely non-atomic to begin with. */ 222 | 223 | if (iohook_initted) { 224 | return; 225 | } 226 | 227 | InitializeCriticalSection(&iohook_lock); 228 | EnterCriticalSection(&iohook_lock); 229 | 230 | /* Splice iohook into IAT entries referencing Win32 I/O APIs */ 231 | 232 | hook_table_apply( 233 | NULL, 234 | "kernel32.dll", 235 | iohook_kernel32_syms, 236 | _countof(iohook_kernel32_syms)); 237 | 238 | /* Here be dragons: 239 | 240 | The hook table mechanism will populate our next_ function pointers with 241 | the prior values of these IAT entries, and we invoke these next_ 242 | function pointers to propagate IO requests onwards to the OS (or to 243 | whoever hooked everybody's IATs before us). We don't statically import 244 | these functions using the compiler because those static imports would 245 | also be realized as IAT entries somewhere, so we'd overwrite our own 246 | imports for the Win32 file I/O APIs and send ourselves into an infinite 247 | recursion. 248 | 249 | That being said, not all pass-through operations necessarily end up 250 | leaving through the same function that they entered through, owing to 251 | the existence of multiple and possibly-obsolete ways of doing various 252 | I/O things through the Win32 API. For instance, a call to CreateFileA 253 | intercepted by iohook will emerge as a call to CreateFileW. Similarly, 254 | a call to the clunky SetFilePointer API will emerge as a call to the 255 | safer and simpler SetFilePointerEx. This presents a problem if the 256 | desired API exit path isn't actually imported through an IAT that we 257 | touch with hook_table_apply. In this edge case, we have to explicitly 258 | call GetProcAddress to ensure that these function pointers are populated. 259 | 260 | Oh, and as with all other uses of hook_table_apply(), subtle problems 261 | may arise if a function is referenced from multiple IAT entries across 262 | the process and those IAT entries somehow end up not all pointing to the 263 | same destination. */ 264 | 265 | kernel32 = GetModuleHandleW(L"kernel32.dll"); 266 | 267 | if (next_CreateFileW == NULL) { 268 | next_CreateFileW = (void *) GetProcAddress( 269 | kernel32, 270 | "CreateFileW"); 271 | } 272 | 273 | if (next_SetFilePointerEx == NULL) { 274 | next_SetFilePointerEx = (void *) GetProcAddress( 275 | kernel32, 276 | "SetFilePointerEx"); 277 | } 278 | 279 | iohook_initted = true; 280 | 281 | LeaveCriticalSection(&iohook_lock); 282 | } 283 | 284 | // Deprecated 285 | HANDLE iohook_open_dummy_fd(void) 286 | { 287 | iohook_init(); 288 | 289 | return next_CreateFileW( 290 | L"NUL", 291 | GENERIC_READ | GENERIC_WRITE, 292 | FILE_SHARE_READ | FILE_SHARE_WRITE, 293 | NULL, 294 | OPEN_EXISTING, 295 | FILE_FLAG_OVERLAPPED, 296 | NULL); 297 | } 298 | 299 | HRESULT iohook_open_nul_fd(HANDLE *out) 300 | { 301 | HANDLE fd; 302 | 303 | assert(out != NULL); 304 | 305 | *out = NULL; 306 | iohook_init(); 307 | 308 | fd = next_CreateFileW( 309 | L"NUL", 310 | GENERIC_READ | GENERIC_WRITE, 311 | FILE_SHARE_READ | FILE_SHARE_WRITE, 312 | NULL, 313 | OPEN_EXISTING, 314 | FILE_FLAG_OVERLAPPED, 315 | NULL); 316 | 317 | if (fd == NULL) { 318 | return HRESULT_FROM_WIN32(GetLastError()); 319 | } 320 | 321 | *out = fd; 322 | 323 | return S_OK; 324 | } 325 | 326 | HRESULT iohook_push_handler(iohook_fn_t fn) 327 | { 328 | iohook_fn_t *new_array; 329 | size_t new_size; 330 | HRESULT hr; 331 | 332 | assert(fn != NULL); 333 | 334 | iohook_init(); 335 | EnterCriticalSection(&iohook_lock); 336 | 337 | new_size = iohook_nhandlers + 1; 338 | new_array = realloc(iohook_handlers, new_size * sizeof(iohook_fn_t)); 339 | 340 | if (new_array != NULL) { 341 | iohook_handlers = new_array; 342 | iohook_handlers[iohook_nhandlers++] = fn; 343 | hr = S_OK; 344 | } else { 345 | hr = E_OUTOFMEMORY; 346 | } 347 | 348 | LeaveCriticalSection(&iohook_lock); 349 | 350 | return hr; 351 | } 352 | 353 | static BOOL iohook_overlapped_result( 354 | uint32_t *syncout, 355 | OVERLAPPED *ovl, 356 | uint32_t value) 357 | { 358 | if (ovl != NULL) { 359 | ovl->Internal = STATUS_SUCCESS; 360 | ovl->InternalHigh = value; 361 | 362 | if (ovl->hEvent != NULL) { 363 | SetEvent(ovl->hEvent); 364 | } 365 | } 366 | 367 | if (syncout != NULL) { 368 | *syncout = value; 369 | SetLastError(ERROR_SUCCESS); 370 | 371 | return TRUE; 372 | } else { 373 | SetLastError(ERROR_IO_PENDING); 374 | 375 | return FALSE; 376 | } 377 | } 378 | 379 | HRESULT iohook_invoke_next(struct irp *irp) 380 | { 381 | iohook_fn_t handler; 382 | HRESULT hr; 383 | 384 | assert(irp != NULL); 385 | 386 | EnterCriticalSection(&iohook_lock); 387 | 388 | assert(iohook_initted); 389 | assert(irp->next_handler <= iohook_nhandlers); 390 | 391 | if (irp->next_handler < iohook_nhandlers) { 392 | handler = iohook_handlers[irp->next_handler]; 393 | irp->next_handler++; 394 | } else { 395 | handler = iohook_invoke_real; 396 | irp->next_handler = (size_t) -1; 397 | } 398 | 399 | LeaveCriticalSection(&iohook_lock); 400 | 401 | hr = handler(irp); 402 | 403 | if (FAILED(hr)) { 404 | irp->next_handler = (size_t) -1; 405 | } 406 | 407 | return hr; 408 | } 409 | 410 | static HRESULT iohook_invoke_real(struct irp *irp) 411 | { 412 | iohook_fn_t handler; 413 | 414 | assert(irp != NULL); 415 | assert(irp->op < _countof(iohook_real_handlers)); 416 | 417 | handler = iohook_real_handlers[irp->op]; 418 | 419 | assert(handler != NULL); 420 | 421 | return handler(irp); 422 | } 423 | 424 | static HRESULT iohook_invoke_real_open(struct irp *irp) 425 | { 426 | HANDLE fd; 427 | 428 | assert(irp != NULL); 429 | 430 | fd = next_CreateFileW( 431 | irp->open_filename, 432 | irp->open_access, 433 | irp->open_share, 434 | irp->open_sa, 435 | irp->open_creation, 436 | irp->open_flags, 437 | irp->open_tmpl); 438 | 439 | if (fd == INVALID_HANDLE_VALUE) { 440 | return HRESULT_FROM_WIN32(GetLastError()); 441 | } 442 | 443 | irp->fd = fd; 444 | 445 | return S_OK; 446 | } 447 | 448 | static HRESULT iohook_invoke_real_close(struct irp *irp) 449 | { 450 | BOOL ok; 451 | 452 | assert(irp != NULL); 453 | 454 | ok = next_CloseHandle(irp->fd); 455 | 456 | if (!ok) { 457 | return HRESULT_FROM_WIN32(GetLastError()); 458 | } 459 | 460 | return S_OK; 461 | } 462 | 463 | static HRESULT iohook_invoke_real_read(struct irp *irp) 464 | { 465 | uint32_t nread; 466 | BOOL ok; 467 | 468 | assert(irp != NULL); 469 | 470 | ok = next_ReadFile( 471 | irp->fd, 472 | &irp->read.bytes[irp->read.pos], 473 | irp->read.nbytes - irp->read.pos, 474 | &nread, 475 | irp->ovl); 476 | 477 | if (!ok) { 478 | return HRESULT_FROM_WIN32(GetLastError()); 479 | } 480 | 481 | irp->read.pos += nread; 482 | 483 | return S_OK; 484 | } 485 | 486 | static HRESULT iohook_invoke_real_write(struct irp *irp) 487 | { 488 | uint32_t nwrit; 489 | BOOL ok; 490 | 491 | assert(irp != NULL); 492 | 493 | ok = next_WriteFile( 494 | irp->fd, 495 | &irp->write.bytes[irp->write.pos], 496 | irp->write.nbytes - irp->write.pos, 497 | &nwrit, 498 | irp->ovl); 499 | 500 | if (!ok) { 501 | return HRESULT_FROM_WIN32(GetLastError()); 502 | } 503 | 504 | irp->write.pos += nwrit; 505 | 506 | return S_OK; 507 | } 508 | 509 | static HRESULT iohook_invoke_real_seek(struct irp *irp) 510 | { 511 | BOOL ok; 512 | 513 | assert(irp != NULL); 514 | 515 | ok = next_SetFilePointerEx( 516 | irp->fd, 517 | irp->seek_offset, 518 | &irp->seek_pos, 519 | irp->seek_origin); 520 | 521 | if (!ok) { 522 | return HRESULT_FROM_WIN32(GetLastError()); 523 | } 524 | 525 | return S_OK; 526 | } 527 | 528 | static HRESULT iohook_invoke_real_fsync(struct irp *irp) 529 | { 530 | BOOL ok; 531 | 532 | assert(irp != NULL); 533 | 534 | ok = next_FlushFileBuffers(irp->fd); 535 | 536 | if (!ok) { 537 | return HRESULT_FROM_WIN32(GetLastError()); 538 | } 539 | 540 | return S_OK; 541 | } 542 | 543 | static HRESULT iohook_invoke_real_ioctl(struct irp *irp) 544 | { 545 | uint32_t nread; 546 | BOOL ok; 547 | 548 | assert(irp != NULL); 549 | 550 | /* ioctl in/out params tend to be structs, so it probably does not make 551 | sense to concatenate a synthetic result with a pass-through result in 552 | the same way as one might do with read/write. */ 553 | 554 | assert(irp->write.pos == 0); 555 | assert(irp->read.pos == 0); 556 | 557 | ok = next_DeviceIoControl( 558 | irp->fd, 559 | irp->ioctl, 560 | (void *) irp->write.bytes, // Cast off const 561 | irp->write.nbytes, 562 | irp->read.bytes, 563 | irp->read.nbytes, 564 | &nread, 565 | irp->ovl); 566 | 567 | /* Must be propagated even if there is an error, see 568 | iohook_DeviceIoControl. */ 569 | 570 | irp->read.pos = nread; 571 | 572 | if (!ok) { 573 | return HRESULT_FROM_WIN32(GetLastError()); 574 | } 575 | 576 | return S_OK; 577 | } 578 | 579 | static HANDLE WINAPI iohook_CreateFileA( 580 | const char *lpFileName, 581 | uint32_t dwDesiredAccess, 582 | uint32_t dwShareMode, 583 | SECURITY_ATTRIBUTES *lpSecurityAttributes, 584 | uint32_t dwCreationDisposition, 585 | uint32_t dwFlagsAndAttributes, 586 | HANDLE hTemplateFile) 587 | { 588 | wchar_t *wfilename; 589 | int nchars; 590 | int result; 591 | HANDLE fd; 592 | 593 | fd = INVALID_HANDLE_VALUE; 594 | wfilename = NULL; 595 | 596 | if (lpFileName == NULL) { 597 | SetLastError(ERROR_INVALID_PARAMETER); 598 | 599 | goto end; 600 | } 601 | 602 | nchars = MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, NULL, 0); 603 | wfilename = malloc(nchars * sizeof(wchar_t)); 604 | 605 | if (wfilename == NULL) { 606 | SetLastError(ERROR_OUTOFMEMORY); 607 | 608 | goto end; 609 | } 610 | 611 | result = MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, wfilename, nchars); 612 | 613 | if (result == 0) { 614 | goto end; 615 | } 616 | 617 | fd = iohook_CreateFileW( 618 | wfilename, 619 | dwDesiredAccess, 620 | dwShareMode, 621 | lpSecurityAttributes, 622 | dwCreationDisposition, dwFlagsAndAttributes, 623 | hTemplateFile); 624 | 625 | end: 626 | free(wfilename); 627 | 628 | return fd; 629 | } 630 | 631 | static HANDLE WINAPI iohook_CreateFileW( 632 | const wchar_t *lpFileName, 633 | uint32_t dwDesiredAccess, 634 | uint32_t dwShareMode, 635 | SECURITY_ATTRIBUTES *lpSecurityAttributes, 636 | uint32_t dwCreationDisposition, 637 | uint32_t dwFlagsAndAttributes, 638 | HANDLE hTemplateFile) 639 | { 640 | struct irp irp; 641 | HRESULT hr; 642 | 643 | if (lpFileName == NULL) { 644 | SetLastError(ERROR_INVALID_PARAMETER); 645 | 646 | return INVALID_HANDLE_VALUE; 647 | } 648 | 649 | memset(&irp, 0, sizeof(irp)); 650 | irp.op = IRP_OP_OPEN; 651 | irp.fd = INVALID_HANDLE_VALUE; 652 | irp.open_filename = lpFileName; 653 | irp.open_access = dwDesiredAccess; 654 | irp.open_share = dwShareMode; 655 | irp.open_sa = lpSecurityAttributes; 656 | irp.open_creation = dwCreationDisposition; 657 | irp.open_flags = dwFlagsAndAttributes; 658 | irp.open_tmpl = hTemplateFile; 659 | 660 | hr = iohook_invoke_next(&irp); 661 | 662 | if (FAILED(hr)) { 663 | return hr_propagate_win32(hr, INVALID_HANDLE_VALUE); 664 | } 665 | 666 | SetLastError(ERROR_SUCCESS); 667 | 668 | return irp.fd; 669 | } 670 | 671 | static BOOL WINAPI iohook_CloseHandle(HANDLE hFile) 672 | { 673 | struct irp irp; 674 | HRESULT hr; 675 | 676 | if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) { 677 | SetLastError(ERROR_INVALID_PARAMETER); 678 | 679 | return FALSE; 680 | } 681 | 682 | memset(&irp, 0, sizeof(irp)); 683 | irp.op = IRP_OP_CLOSE; 684 | irp.fd = hFile; 685 | 686 | hr = iohook_invoke_next(&irp); 687 | 688 | if (FAILED(hr)) { 689 | return hr_propagate_win32(hr, FALSE); 690 | } 691 | 692 | /* Don't call SetLastError on success! */ 693 | 694 | return TRUE; 695 | } 696 | 697 | static BOOL WINAPI iohook_ReadFile( 698 | HANDLE hFile, 699 | void *lpBuffer, 700 | uint32_t nNumberOfBytesToRead, 701 | uint32_t *lpNumberOfBytesRead, 702 | OVERLAPPED *lpOverlapped) 703 | { 704 | struct irp irp; 705 | HRESULT hr; 706 | 707 | if (hFile == NULL || hFile == INVALID_HANDLE_VALUE || lpBuffer == NULL) { 708 | SetLastError(ERROR_INVALID_PARAMETER); 709 | 710 | return FALSE; 711 | } 712 | 713 | if (lpOverlapped == NULL) { 714 | if (lpNumberOfBytesRead == NULL) { 715 | SetLastError(ERROR_INVALID_PARAMETER); 716 | 717 | return FALSE; 718 | } 719 | 720 | *lpNumberOfBytesRead = 0; 721 | } 722 | 723 | memset(&irp, 0, sizeof(irp)); 724 | irp.op = IRP_OP_READ; 725 | irp.fd = hFile; 726 | irp.ovl = lpOverlapped; 727 | irp.read.bytes = lpBuffer; 728 | irp.read.nbytes = nNumberOfBytesToRead; 729 | irp.read.pos = 0; 730 | 731 | hr = iohook_invoke_next(&irp); 732 | 733 | if (FAILED(hr)) { 734 | return hr_propagate_win32(hr, FALSE); 735 | } 736 | 737 | assert(irp.read.pos <= irp.read.nbytes); 738 | 739 | return iohook_overlapped_result( 740 | lpNumberOfBytesRead, 741 | lpOverlapped, 742 | irp.read.pos); 743 | } 744 | 745 | static BOOL WINAPI iohook_WriteFile( 746 | HANDLE hFile, 747 | const void *lpBuffer, 748 | uint32_t nNumberOfBytesToWrite, 749 | uint32_t *lpNumberOfBytesWritten, 750 | OVERLAPPED *lpOverlapped) 751 | { 752 | struct irp irp; 753 | HRESULT hr; 754 | 755 | if (hFile == NULL || hFile == INVALID_HANDLE_VALUE || lpBuffer == NULL) { 756 | SetLastError(ERROR_INVALID_PARAMETER); 757 | 758 | return FALSE; 759 | } 760 | 761 | if (lpOverlapped == NULL) { 762 | if (lpNumberOfBytesWritten == NULL) { 763 | SetLastError(ERROR_INVALID_PARAMETER); 764 | 765 | return FALSE; 766 | } 767 | 768 | *lpNumberOfBytesWritten = 0; 769 | } 770 | 771 | memset(&irp, 0, sizeof(irp)); 772 | irp.op = IRP_OP_WRITE; 773 | irp.fd = hFile; 774 | irp.ovl = lpOverlapped; 775 | irp.write.bytes = lpBuffer; 776 | irp.write.nbytes = nNumberOfBytesToWrite; 777 | irp.write.pos = 0; 778 | 779 | hr = iohook_invoke_next(&irp); 780 | 781 | if (FAILED(hr)) { 782 | return hr_propagate_win32(hr, FALSE); 783 | } 784 | 785 | assert(irp.write.pos <= irp.write.nbytes); 786 | 787 | return iohook_overlapped_result( 788 | lpNumberOfBytesWritten, 789 | lpOverlapped, 790 | irp.write.pos); 791 | } 792 | 793 | static DWORD WINAPI iohook_SetFilePointer( 794 | HANDLE hFile, 795 | int32_t lDistanceToMove, 796 | int32_t *lpDistanceToMoveHigh, 797 | uint32_t dwMoveMethod) 798 | { 799 | struct irp irp; 800 | HRESULT hr; 801 | 802 | if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) { 803 | SetLastError(ERROR_INVALID_PARAMETER); 804 | 805 | return INVALID_SET_FILE_POINTER; 806 | } 807 | 808 | memset(&irp, 0, sizeof(irp)); 809 | irp.op = IRP_OP_SEEK; 810 | irp.fd = hFile; 811 | irp.seek_origin = dwMoveMethod; 812 | 813 | /* This is a clumsy API. In 32-bit mode lDistanceToMove is a signed 32-bit 814 | int, but in 64-bit mode it is a 32-bit UNsigned int. Care must be taken 815 | with sign-extension vs zero-extension here. */ 816 | 817 | if (lpDistanceToMoveHigh != NULL) { 818 | irp.seek_offset = ((( int64_t) *lpDistanceToMoveHigh) << 32) | 819 | ((uint64_t) lDistanceToMove ) ; 820 | } else { 821 | irp.seek_offset = ( int64_t) lDistanceToMove; 822 | } 823 | 824 | hr = iohook_invoke_next(&irp); 825 | 826 | if (FAILED(hr)) { 827 | return hr_propagate_win32(hr, INVALID_SET_FILE_POINTER); 828 | } 829 | 830 | SetLastError(ERROR_SUCCESS); 831 | 832 | if (lpDistanceToMoveHigh != NULL) { 833 | *lpDistanceToMoveHigh = irp.seek_pos >> 32; 834 | } 835 | 836 | return (DWORD) irp.seek_pos; 837 | } 838 | 839 | static BOOL WINAPI iohook_SetFilePointerEx( 840 | HANDLE hFile, 841 | int64_t liDistanceToMove, 842 | uint64_t *lpNewFilePointer, 843 | uint32_t dwMoveMethod) 844 | { 845 | struct irp irp; 846 | HRESULT hr; 847 | 848 | if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) { 849 | SetLastError(ERROR_INVALID_PARAMETER); 850 | 851 | return FALSE; 852 | } 853 | 854 | /* Much nicer than SetFilePointer */ 855 | 856 | memset(&irp, 0, sizeof(irp)); 857 | irp.op = IRP_OP_SEEK; 858 | irp.fd = hFile; 859 | irp.seek_offset = liDistanceToMove; 860 | irp.seek_origin = dwMoveMethod; 861 | 862 | hr = iohook_invoke_next(&irp); 863 | 864 | if (FAILED(hr)) { 865 | return hr_propagate_win32(hr, FALSE); 866 | } 867 | 868 | if (lpNewFilePointer != NULL) { 869 | *lpNewFilePointer = irp.seek_pos; 870 | } 871 | 872 | SetLastError(ERROR_SUCCESS); 873 | 874 | return TRUE; 875 | } 876 | 877 | static BOOL WINAPI iohook_FlushFileBuffers(HANDLE hFile) 878 | { 879 | struct irp irp; 880 | HRESULT hr; 881 | 882 | if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) { 883 | SetLastError(ERROR_INVALID_PARAMETER); 884 | 885 | return FALSE; 886 | } 887 | 888 | memset(&irp, 0, sizeof(irp)); 889 | irp.op = IRP_OP_FSYNC; 890 | irp.fd = hFile; 891 | 892 | hr = iohook_invoke_next(&irp); 893 | 894 | if (FAILED(hr)) { 895 | return hr_propagate_win32(hr, FALSE); 896 | } 897 | 898 | SetLastError(ERROR_SUCCESS); 899 | 900 | return TRUE; 901 | } 902 | 903 | static BOOL WINAPI iohook_DeviceIoControl( 904 | HANDLE hFile, 905 | uint32_t dwIoControlCode, 906 | void *lpInBuffer, 907 | uint32_t nInBufferSize, 908 | void *lpOutBuffer, 909 | uint32_t nOutBufferSize, 910 | uint32_t *lpBytesReturned, 911 | OVERLAPPED *lpOverlapped) 912 | { 913 | struct irp irp; 914 | HRESULT hr; 915 | 916 | if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) { 917 | SetLastError(ERROR_INVALID_PARAMETER); 918 | 919 | return FALSE; 920 | } 921 | 922 | if (lpOverlapped == NULL) { 923 | if (lpBytesReturned == NULL) { 924 | SetLastError(ERROR_INVALID_PARAMETER); 925 | 926 | return FALSE; 927 | } 928 | 929 | *lpBytesReturned = 0; 930 | } 931 | 932 | memset(&irp, 0, sizeof(irp)); 933 | irp.op = IRP_OP_IOCTL; 934 | irp.fd = hFile; 935 | irp.ovl = lpOverlapped; 936 | irp.ioctl = dwIoControlCode; 937 | 938 | if (lpInBuffer != NULL) { 939 | irp.write.bytes = lpInBuffer; 940 | irp.write.nbytes = nInBufferSize; 941 | } 942 | 943 | if (lpOutBuffer != NULL) { 944 | irp.read.bytes = lpOutBuffer; 945 | irp.read.nbytes = nOutBufferSize; 946 | } 947 | 948 | hr = iohook_invoke_next(&irp); 949 | 950 | if (FAILED(hr)) { 951 | /* Special case: ERROR_MORE_DATA requires this out parameter to be 952 | propagated, per MSDN. All ioctls in the entire process that go via 953 | the win32 API (as opposed to the NTDLL API) get redirected through 954 | iohook, and the Windows XP version of DirectSound is known to rely 955 | on this behavior. */ 956 | 957 | if (lpBytesReturned != NULL) { 958 | *lpBytesReturned = (DWORD) irp.read.pos; 959 | } 960 | 961 | return hr_propagate_win32(hr, FALSE); 962 | } 963 | 964 | return iohook_overlapped_result( 965 | lpBytesReturned, 966 | lpOverlapped, 967 | irp.read.pos); 968 | } 969 | --------------------------------------------------------------------------------