├── .gitignore ├── Makefile ├── README.md ├── evdev-mirror.c ├── kallsyms.c ├── kallsyms.h ├── read_example.c └── write_example.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.ko 3 | *.mod.c 4 | .*.cmd 5 | .tmp_versions/ 6 | Module.symvers 7 | modules.order 8 | .idea/ 9 | cmake-build-debug/ 10 | read_example 11 | *.mod 12 | write_example 13 | read_example 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | KERNEL_PATH ?= /lib/modules/$(shell uname -r)/build 2 | CC=gcc 3 | 4 | obj-m += evdev_mirror.o 5 | evdev_mirror-objs := evdev-mirror.o kallsyms.o 6 | 7 | all: 8 | make -C $(KERNEL_PATH) M=$(PWD) modules 9 | gcc -o read_example read_example.c 10 | gcc -o write_example write_example.c 11 | 12 | clean: 13 | make -C $(KERNEL_PATH) M=$(PWD) clean 14 | rm read_example 15 | rm write_example 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # _Experimental_ evdev input mirror 2 | 3 | When qemu is setup to use exclusive evdev input, it's actually a real pain to read the inputs to your vm from your host machine. 4 | 5 | If you don't want to rebuild qemu or linux from source, you're kinda stuck with hooking(maybe if ur a big brain you can do something like register another input listener in the kernel but I tried that and it no work). I think hooking the kernel is better because it seems to almost never change and the symbols are exported with kallsyms. 6 | 7 | This is setup just for keypresses/mousebuttons at the moment. 8 | 9 | ## Update Kernel 5.7+ 10 | Kernel 5.7 stops the export of kallsyms functions and has forced us to change the way we get symbol addresses. 11 | 12 | The new method by [heep](https://github.com/h33p/kallsyms-lp) requires Kernel 5.0 or higher and uses the livepatcher feature. 13 | ## System Requirements ( approx ) 14 | * 64bit Linux system 15 | * Kernel Version >= 5.00 ( for kernel livepatcher ) 16 | * The following Kernel Build configurations 17 | * CONFIG_FTRACE 18 | * CONFIG_KALLSYMS 19 | * CONFIG_DYNAMIC_FTRACE_WITH_REGS 20 | * CONFIG_HAVE_FENTRY 21 | * CONFIG_LIVEPATCH 22 | 23 | Your distro provider probably put a list of your config options in `/boot/config*`, there's a good chance your kernel already has these options, but if it does not, you'll have to rebuild from source. 24 | * Kernel headers for your current kernel. 25 | * elfutils development package ( "elfutils-libelf-devel" for redhat, "libelf-dev" for ubuntu ) 26 | * Development Essentials ( make, gcc, etc. ) 27 | ## Build Instructions 28 | * After installing kernel headers, you should just be able to use the makefile. 29 | * `make` in the cartographer directory. 30 | 31 | After the build you can load with `insmod evdev_mirror.ko` and unload with `rmmod evdev_mirror` 32 | 33 | ## Why did you take out the timestamp that the other evdev events have 34 | Because that recently got changed in the linux kernel. My VM was on 4.19 and i'm on 4.20 and I was wondering why it wasn't working. ( it was mismatched ) 35 | 36 | I figured that I didn't really need the timestamp anyway. 37 | 38 | The struct is still a standard linux struct, which is also in , but it's just an `input_value` instead of an `input_event` and the only difference is the missing time. 39 | 40 | ## Overview 41 | * When inserted into the kernel, it will hook the evdev events function and create `/dev/input/evdev-mirror` character device 42 | * You can now read keyboard events from that device, (see the example) 43 | 44 | ## Credits 45 | -[Heep](https://github.com/h33p/) for his new method of getting symbol addresses 46 | 47 | -Alexey Lozovsky - For his series of articles [part1](https://www.apriorit.com/dev-blog/544-hooking-linux-functions-1) about ftrace and hooking with ftrace along with code snippets that I used in this project. 48 | -------------------------------------------------------------------------------- /evdev-mirror.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include //copy_from_user 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "kallsyms.h" 19 | 20 | MODULE_DESCRIPTION("Mirror evdev events to a file."); 21 | MODULE_AUTHOR("LWSS/Heep"); 22 | MODULE_LICENSE("GPL"); 23 | MODULE_INFO(livepatch, "Y"); 24 | 25 | //#define OUTPUT_MOUSE_EVENTS 1 26 | 27 | #define kprint(fmt, ...) printk( (KBUILD_MODNAME ": "fmt), ##__VA_ARGS__ ); 28 | 29 | #ifndef CONFIG_X86_64 30 | #error Only x86_64 architecture is supported! 31 | #endif 32 | 33 | /* 34 | * There are two ways of preventing vicious recursive loops when hooking: 35 | * - detect recusion using function return address (USE_FENTRY_OFFSET = 0) 36 | * - avoid recusion by jumping over the ftrace call (USE_FENTRY_OFFSET = 1) 37 | */ 38 | #define USE_FENTRY_OFFSET 0 39 | 40 | 41 | /* 42 | * Tail call optimization can interfere with recursion detection based on 43 | * return address on the stack. Disable it to avoid machine hangups. 44 | */ 45 | #if !USE_FENTRY_OFFSET 46 | #pragma GCC optimize("-fno-optimize-sibling-calls") 47 | #endif 48 | 49 | static DEFINE_MUTEX(data_mutex); 50 | 51 | struct ftrace_hook { 52 | const char *name; 53 | void *function; 54 | void *original; 55 | 56 | unsigned long address; 57 | struct ftrace_ops ops; 58 | }; 59 | 60 | struct device dev; 61 | struct cdev cdev; 62 | 63 | spinlock_t input_lock; 64 | static struct input_value last_event; 65 | bool fresh = false; 66 | 67 | static struct ftrace_hook evdev_events_hook; 68 | static struct input_handle *last_handle = 0; 69 | 70 | 71 | 72 | static int init_hook( struct ftrace_hook *hook ) 73 | { 74 | #if USE_FENTRY_OFFSET 75 | *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; 76 | #else 77 | *((unsigned long*) hook->original) = hook->address; 78 | #endif 79 | 80 | return 0; 81 | } 82 | 83 | static void notrace ftrace_thunk(unsigned long ip, 84 | unsigned long parent_ip, 85 | struct ftrace_ops *ops, 86 | struct pt_regs *regs) 87 | { 88 | struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); 89 | 90 | #if USE_FENTRY_OFFSET 91 | regs->ip = (unsigned long) hook->function; 92 | #else 93 | if (!within_module(parent_ip, THIS_MODULE)) 94 | regs->ip = (unsigned long) hook->function; 95 | #endif 96 | } 97 | 98 | static asmlinkage void (*orig_evdev_events)(struct input_handle *handle, 99 | const struct input_value *vals, 100 | unsigned int count); 101 | 102 | static asmlinkage void hooked_evdev_events(struct input_handle *handle, 103 | const struct input_value *vals, 104 | unsigned int count) 105 | { 106 | int i; 107 | for( i = 0; i < count; i++ ){ 108 | #ifdef OUTPUT_MOUSE_EVENTS 109 | /* We don't care about anything except for keypresses/mouse/touchpad */ 110 | if( vals[i].type != EV_KEY && vals[i].type != EV_REL && vals[i].type != EV_ABS ){ 111 | continue; 112 | } 113 | if( vals[i].type == EV_REL ){ 114 | last_handle = handle; // save mouse 115 | } 116 | #else 117 | if( vals[i].type != EV_KEY ){ 118 | continue; 119 | } 120 | #endif 121 | spin_lock(&input_lock); // This could cause small input lag? Maybe add a buffer 122 | last_event = vals[i]; 123 | fresh = true; 124 | spin_unlock(&input_lock); 125 | //kprint("Event #(%d): type: %d - code: %d - value: %d\n", i, vals[i].type, vals[i].code, vals[i].value); 126 | } 127 | orig_evdev_events( handle, vals, count ); 128 | } 129 | 130 | static ssize_t mirror_read(struct file *file, 131 | char *user_buffer, 132 | size_t count, 133 | loff_t *ppos) 134 | { 135 | if( !fresh ) 136 | return 0; 137 | 138 | if( count < sizeof(struct input_value) ){ 139 | kprint("ERROR: Userspace buffer smaller than input event!\n"); 140 | return -EFAULT; 141 | } 142 | 143 | spin_lock(&input_lock); 144 | //kprint("mirror_read: input code: (%d/%ld)\n", last_event.code, sizeof(struct input_value)); 145 | if( copy_to_user(user_buffer, &last_event, sizeof(struct input_value)) ){ 146 | kprint("ERROR: Copying to User failed\n"); 147 | return -EFAULT; 148 | } 149 | 150 | fresh = false; 151 | spin_unlock(&input_lock); 152 | 153 | return sizeof(struct input_value); 154 | } 155 | 156 | static ssize_t mirror_write(struct file *file, 157 | const char *user_buffer, 158 | size_t count, 159 | loff_t *ppos){ 160 | if( count != sizeof(struct input_value) || *ppos > 0 ){ 161 | kprint("ERROR: Malformed input_value\n"); 162 | return -EFAULT; 163 | } 164 | if( !last_handle ){ 165 | kprint("Move your mouse first\n"); 166 | return -EFAULT; 167 | } 168 | struct input_value input; 169 | copy_from_user(&input, user_buffer, sizeof(struct input_value)); 170 | //kprint("injecting event type:(%d) - code(%d) - value(%d)\n", input.type, input.code, input.value); 171 | 172 | input_inject_event(last_handle, input.type, input.code, input.value); 173 | input_sync(last_handle->dev); 174 | 175 | return sizeof(struct input_value); 176 | } 177 | 178 | static const struct file_operations mirrordev_fops = { 179 | .owner = THIS_MODULE, 180 | .read = mirror_read, 181 | .write = mirror_write 182 | }; 183 | 184 | static void mirrordev_release(struct device *dev) 185 | { 186 | 187 | } 188 | 189 | static int startup(void) 190 | { 191 | if( init_kallsyms() ){ 192 | kprint( "Error initing kallsyms hack.\n" ); 193 | return -EAGAIN; 194 | } 195 | 196 | evdev_events_hook.name = "evdev_events"; 197 | evdev_events_hook.address = kallsyms_lookup_name( "evdev_events" ); 198 | if( !evdev_events_hook.address ){ 199 | kprint( "Error getting evdev_events addr! (%p)\n", evdev_events_hook.address ); 200 | return -EAGAIN; 201 | } 202 | 203 | if( !evdev_events_hook.address ){ 204 | kprint( "Error resolving the Address\n" ); 205 | return -ENXIO; 206 | } 207 | 208 | evdev_events_hook.function = hooked_evdev_events; 209 | evdev_events_hook.original = &orig_evdev_events; 210 | int ret = init_hook( &evdev_events_hook ); 211 | 212 | if( ret ) 213 | return ret; 214 | 215 | evdev_events_hook.ops.func = ftrace_thunk; 216 | evdev_events_hook.ops.flags = FTRACE_OPS_FL_SAVE_REGS 217 | | FTRACE_OPS_FL_RECURSION_SAFE 218 | | FTRACE_OPS_FL_IPMODIFY; 219 | 220 | ret = ftrace_set_filter_ip(&evdev_events_hook.ops, evdev_events_hook.address, 0, 0); 221 | if( ret ){ 222 | kprint("ftrace_set_filter_ip() failed: %d\n", ret); 223 | return ret; 224 | } 225 | 226 | ret = register_ftrace_function(&evdev_events_hook.ops); 227 | if (ret) { 228 | kprint("register_ftrace_function() failed: %d\n", ret); 229 | ftrace_set_filter_ip(&evdev_events_hook.ops, evdev_events_hook.address, 1, 0); 230 | return ret; 231 | } 232 | 233 | spin_lock_init(&input_lock); 234 | 235 | dev_set_name(&dev, "evdev-mirror"); 236 | dev.class = &input_class; 237 | dev.devt = MKDEV(INPUT_MAJOR, input_get_new_minor(63, 1, false)); 238 | dev.release = mirrordev_release; 239 | ret = device_register(&dev); 240 | if (ret) { 241 | kprint("device_register failed: %d\n", ret); 242 | return ret; 243 | } 244 | 245 | cdev_init(&cdev, &mirrordev_fops); 246 | cdev.kobj.parent = &dev.kobj; 247 | ret = cdev_add(&cdev, dev.devt, 1); 248 | if (ret) { 249 | kprint("cdev_add failed: %d\n", ret); 250 | return ret; 251 | } 252 | 253 | kprint("Loading complete.\n"); 254 | return 0; 255 | } 256 | 257 | static void shutdown(void) 258 | { 259 | int ret; 260 | ret = unregister_ftrace_function(&evdev_events_hook.ops); 261 | if( ret ) 262 | kprint("unregister_ftrace_function() failed: %d\n", ret); 263 | 264 | ret = ftrace_set_filter_ip(&evdev_events_hook.ops, evdev_events_hook.address, 1, 0); 265 | if( ret ) 266 | kprint("ftrace_set_filter_ip() failed: %d\n", ret); 267 | 268 | cdev_del(&cdev); 269 | device_del(&dev); 270 | //input_free_minor(MINOR(dev.devt)); 271 | //put_device(&dev); 272 | 273 | kprint("UnLoaded.\n"); 274 | } 275 | 276 | module_init(startup); 277 | module_exit(shutdown); -------------------------------------------------------------------------------- /kallsyms.c: -------------------------------------------------------------------------------- 1 | //https://github.com/h33p/kallsyms-lp 2 | #include "kallsyms.h" 3 | #include 4 | 5 | static struct klp_func funcs[] = { 6 | { 7 | .old_name = "kallsyms_lookup_name", 8 | .new_func = kallsyms_lookup_name, 9 | }, {} 10 | }; 11 | 12 | static struct klp_func failfuncs[] = { 13 | { 14 | .old_name = "___________________", 15 | }, {} 16 | }; 17 | 18 | static struct klp_object objs[] = { 19 | { 20 | .funcs = funcs, 21 | }, 22 | { 23 | .name = "kallsyms_failing_name", 24 | .funcs = failfuncs, 25 | }, { } 26 | }; 27 | 28 | static struct klp_patch patch = { 29 | .mod = THIS_MODULE, 30 | .objs = objs, 31 | }; 32 | 33 | unsigned long kallsyms_lookup_name(const char *name) 34 | { 35 | return ((unsigned long(*)(const char *))funcs->old_func)(name); 36 | } 37 | 38 | int init_kallsyms(void) 39 | { 40 | int r = klp_enable_patch(&patch); 41 | 42 | if (!r) 43 | return -1; 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /kallsyms.h: -------------------------------------------------------------------------------- 1 | //https://github.com/h33p/kallsyms-lp 2 | unsigned long kallsyms_lookup_name(const char *name); 3 | int init_kallsyms(void); 4 | -------------------------------------------------------------------------------- /read_example.c: -------------------------------------------------------------------------------- 1 | // An example to read from the evdev-mirror kernel module 2 | // 3 | // Note: The module will only output EV_KEY events (keypresses) 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // This struct is the same at 13 | struct input_value { 14 | uint16_t type; 15 | uint16_t code; 16 | int32_t value; 17 | }; 18 | 19 | #define EV_KEY 0x01 20 | #define EV_REL 0x02 21 | #define EV_ABS 0x03 22 | 23 | // Made up 24 | #define AXIS_X 0x00 // left negative - right positive 25 | #define AXIS_Y 0x01 // up negative - down positive 26 | 27 | int main( int argc, char **argv ) 28 | { 29 | int fd; 30 | struct input_value input; 31 | 32 | if( geteuid() != 0 ){ 33 | printf("Run as root!\n"); 34 | return -2; 35 | } 36 | fd = open("/dev/input/evdev-mirror", O_RDONLY); 37 | if( fd < 0 ){ 38 | printf("Error opening evdev-mirror!(%d)\n", fd); 39 | return -1; 40 | } 41 | 42 | while( 1 ){ 43 | // if zero bytes, keep goin... 44 | if( !read( fd, &input, sizeof(struct input_value) ) ) 45 | continue; 46 | if( input.type == EV_KEY ){ 47 | printf("KeyPress: %d - state: %d\n", input.code, input.value); 48 | } else if( input.type == EV_REL ){ 49 | printf("MouseMove: axis(%s) units(%d) ", input.code ? "Y" : "X", input.value); 50 | if( input.code ){ 51 | printf( "%s\n", (input.value > 0) ? "DOWN" : "UP" ); 52 | } else { 53 | printf( "%s\n", (input.value > 0) ? "RIGHT" : "LEFT" ); 54 | } 55 | } else if( input.type == EV_ABS ){ 56 | printf( "MouseMoveAbs: (%d/%d)\n", input.code, input.value); 57 | } else { 58 | printf("unknown input type (BUG?)\n"); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /write_example.c: -------------------------------------------------------------------------------- 1 | // An example to inject input events by using evdev-mirror kernel module 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // This struct is the same at 11 | struct input_value { 12 | uint16_t type; 13 | uint16_t code; 14 | int32_t value; 15 | }; 16 | 17 | #define EV_KEY 0x01 18 | #define EV_REL 0x02 19 | #define EV_ABS 0x03 20 | 21 | // Made up 22 | #define AXIS_X 0x00 // left negative - right positive 23 | #define AXIS_Y 0x01 // up negative - down positive 24 | 25 | int main( int argc, char **argv ) 26 | { 27 | int fd; 28 | struct input_value input; 29 | 30 | if( geteuid() != 0 ){ 31 | printf("Run as root!\n"); 32 | return -2; 33 | } 34 | fd = open("/dev/input/evdev-mirror", O_WRONLY); 35 | if( fd < 0 ){ 36 | printf("Error opening evdev-mirror!(%d)\n", fd); 37 | return -1; 38 | } 39 | 40 | input.type = EV_REL; 41 | input.value = 10; 42 | input.code = AXIS_X; 43 | 44 | printf("setting mouse right 10units.\n"); 45 | 46 | write( fd, &input, sizeof(struct input_value) ); 47 | 48 | return 0; 49 | } 50 | --------------------------------------------------------------------------------