vmware userland helper
at master 345 lines 8.0 kB view raw
1/* 2 * Copyright (c) 2007 David Crawshaw <david@zentus.com> 3 * Copyright (c) 2008 David Gwynne <dlg@openbsd.org> 4 * Copyright (c) 2010 joshua stein <jcs@jcs.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <stdint.h> 20#include <stdlib.h> 21#include <stdio.h> 22#include <string.h> 23#include <err.h> 24#include <vis.h> 25 26#include "vmwh.h" 27 28int16_t host_mouse_x; 29int16_t host_mouse_y; 30int mouse_grabbed = 0; 31 32/* "The" magic number, always occupies the EAX register. */ 33#define VM_MAGIC 0x564D5868 34 35/* Port numbers, passed on EDX.LOW . */ 36#define VM_PORT_CMD 0x5658 37#define VM_PORT_RPC 0x5659 38 39/* Commands, passed on ECX.LOW. */ 40#define VM_CMD_GET_MOUSEPOS 0x04 41#define VM_MOUSE_UNGRABBED_POS -100 42#define VM_CMD_SET_MOUSEPOS 0x05 43#define VM_CMD_GET_CLIPBOARD_LEN 0x06 44#define VM_CMD_GET_CLIPBOARD 0x07 45#define VM_CMD_SET_CLIPBOARD_LEN 0x08 46#define VM_CMD_SET_CLIPBOARD 0x09 47#define VM_CMD_GET_VERSION 0x0a 48#define VM_VERSION_UNMANAGED 0x7fffffff 49 50/* A register. */ 51union vm_reg { 52 struct { 53 uint16_t low; 54 uint16_t high; 55 } part; 56 uint32_t word; 57#ifdef __amd64__ 58 struct { 59 uint32_t low; 60 uint32_t high; 61 } words; 62 uint64_t quad; 63#endif 64} __attribute__((__packed__)); 65 66/* A register frame. */ 67struct vm_backdoor { 68 union vm_reg eax; 69 union vm_reg ebx; 70 union vm_reg ecx; 71 union vm_reg edx; 72 union vm_reg esi; 73 union vm_reg edi; 74 union vm_reg ebp; 75} __attribute__((__packed__)); 76 77void vm_cmd(struct vm_backdoor *); 78void vm_ins(struct vm_backdoor *); 79void vm_outs(struct vm_backdoor *); 80 81#define BACKDOOR_OP_I386(op, frame) \ 82 __asm__ __volatile__ ( \ 83 "pushal;" \ 84 "pushl %%eax;" \ 85 "movl 0x18(%%eax), %%ebp;" \ 86 "movl 0x14(%%eax), %%edi;" \ 87 "movl 0x10(%%eax), %%esi;" \ 88 "movl 0x0c(%%eax), %%edx;" \ 89 "movl 0x08(%%eax), %%ecx;" \ 90 "movl 0x04(%%eax), %%ebx;" \ 91 "movl 0x00(%%eax), %%eax;" \ 92 op \ 93 "xchgl %%eax, 0x00(%%esp);" \ 94 "movl %%ebp, 0x18(%%eax);" \ 95 "movl %%edi, 0x14(%%eax);" \ 96 "movl %%esi, 0x10(%%eax);" \ 97 "movl %%edx, 0x0c(%%eax);" \ 98 "movl %%ecx, 0x08(%%eax);" \ 99 "movl %%ebx, 0x04(%%eax);" \ 100 "popl 0x00(%%eax);" \ 101 "popal;" \ 102 ::"a"(frame) \ 103 ) 104 105#define BACKDOOR_OP_AMD64(op, frame) \ 106 __asm__ __volatile__ ( \ 107 "pushq %%rbp; \n\t" \ 108 "pushq %%rax; \n\t" \ 109 "movq 0x30(%%rax), %%rbp; \n\t" \ 110 "movq 0x28(%%rax), %%rdi; \n\t" \ 111 "movq 0x20(%%rax), %%rsi; \n\t" \ 112 "movq 0x18(%%rax), %%rdx; \n\t" \ 113 "movq 0x10(%%rax), %%rcx; \n\t" \ 114 "movq 0x08(%%rax), %%rbx; \n\t" \ 115 "movq 0x00(%%rax), %%rax; \n\t" \ 116 op "\n\t" \ 117 "xchgq %%rax, 0x00(%%rsp); \n\t" \ 118 "movq %%rbp, 0x30(%%rax); \n\t" \ 119 "movq %%rdi, 0x28(%%rax); \n\t" \ 120 "movq %%rsi, 0x20(%%rax); \n\t" \ 121 "movq %%rdx, 0x18(%%rax); \n\t" \ 122 "movq %%rcx, 0x10(%%rax); \n\t" \ 123 "movq %%rbx, 0x08(%%rax); \n\t" \ 124 "popq 0x00(%%rax); \n\t" \ 125 "popq %%rbp; \n\t" \ 126 : /* No outputs. */ : "a" (frame) \ 127 /* No pushal on amd64 so warn gcc about the clobbered registers. */ \ 128 : "rbx", "rcx", "rdx", "rdi", "rsi", "cc", "memory" \ 129 ) 130 131 132#ifdef __i386__ 133#define BACKDOOR_OP(op, frame) BACKDOOR_OP_I386(op, frame) 134#else 135#define BACKDOOR_OP(op, frame) BACKDOOR_OP_AMD64(op, frame) 136#endif 137 138void 139vm_cmd(struct vm_backdoor *frame) 140{ 141 BACKDOOR_OP("inl %%dx, %%eax;", frame); 142} 143 144void 145vm_ins(struct vm_backdoor *frame) 146{ 147 BACKDOOR_OP("cld;\n\trep insb;", frame); 148} 149 150void 151vm_outs(struct vm_backdoor *frame) 152{ 153 BACKDOOR_OP("cld;\n\trep outsb;", frame); 154} 155 156void 157vmware_check_version(void) 158{ 159 struct vm_backdoor frame; 160 161 bzero(&frame, sizeof(frame)); 162 163 frame.eax.word = VM_MAGIC; 164 frame.ebx.word = ~VM_MAGIC; 165 frame.ecx.part.low = VM_CMD_GET_VERSION; 166 frame.ecx.part.high = 0xffff; 167 frame.edx.part.low = VM_PORT_CMD; 168 frame.edx.part.high = 0; 169 170 vm_cmd(&frame); 171 172 if (debug) 173 printf("vmware_check_version: received version 0x%x\n", 174 frame.eax.word); 175 176 if (frame.eax.word == 0xffffffff || 177 frame.ebx.word != VM_MAGIC) 178 errx(1, "not running under vmware\n"); 179} 180 181void 182vmware_get_mouse_position(void) 183{ 184 struct vm_backdoor frame; 185 int last_x = host_mouse_x; 186 int last_y = host_mouse_y; 187 int was_grabbed = mouse_grabbed; 188 189 bzero(&frame, sizeof(frame)); 190 191 frame.eax.word = VM_MAGIC; 192 frame.ecx.part.low = VM_CMD_GET_MOUSEPOS; 193 frame.ecx.part.high = 0xffff; 194 frame.edx.part.low = VM_PORT_CMD; 195 frame.edx.part.high = 0; 196 197 vm_cmd(&frame); 198 199 host_mouse_x = frame.eax.word >> 16; 200 host_mouse_y = frame.eax.word & 0xffff; 201 202 if (host_mouse_x == VM_MOUSE_UNGRABBED_POS) 203 mouse_grabbed = 0; 204 else 205 mouse_grabbed = 1; 206 207 if (debug && ((last_x != host_mouse_x) || (last_y != host_mouse_y) || 208 (was_grabbed != mouse_grabbed))) 209 printf("vmware_get_mouse_position: host cursor is at %d, %d (%s)\n", 210 host_mouse_x, host_mouse_y, (mouse_grabbed ? "grabbed" 211 : "ungrabbed")); 212} 213 214void 215vmware_set_mouse_position(int x, int y) 216{ 217 struct vm_backdoor frame; 218 219 if (debug) 220 printf("vmware_set_mouse_position: setting to %d, %d\n", x, y); 221 222 bzero(&frame, sizeof(frame)); 223 224 frame.eax.word = VM_MAGIC; 225 frame.ecx.part.low = VM_CMD_SET_MOUSEPOS; 226 frame.ecx.part.high = 0xffff; 227 frame.edx.part.low = VM_PORT_CMD; 228 frame.edx.part.high = 0; 229 frame.ebx.word = ((uint32_t)x << 16) | y; 230 231 vm_cmd(&frame); 232} 233 234int 235vmware_get_clipboard(char **buf) 236{ 237 struct vm_backdoor frame; 238 uint32_t total, left; 239 240 bzero(&frame, sizeof(frame)); 241 242 frame.eax.word = VM_MAGIC; 243 frame.ecx.part.low = VM_CMD_GET_CLIPBOARD_LEN; 244 frame.ecx.part.high = 0xffff; 245 frame.edx.part.low = VM_PORT_CMD; 246 frame.edx.part.high = 0; 247 248 vm_cmd(&frame); 249 250 total = left = frame.eax.word; 251 252 if (total == 0 || total > 0xffff) { 253 if (debug) 254 printf("vmware_get_clipboard: nothing there\n"); 255 256 return (0); 257 } 258 259 if (debug) 260 printf("vmware_get_clipboard: have %d byte%s to read\n", 261 total, (total == 1 ? "" : "s")); 262 263 if ((*buf = malloc(total + 1)) == NULL) 264 err(1, "malloc"); 265 266 for (;;) { 267 bzero(&frame, sizeof(frame)); 268 269 frame.eax.word = VM_MAGIC; 270 frame.ecx.part.low = VM_CMD_GET_CLIPBOARD; 271 frame.ecx.part.high = 0xffff; 272 frame.edx.part.low = VM_PORT_CMD; 273 frame.edx.part.high = 0; 274 275 vm_cmd(&frame); 276 277 memcpy(*buf + (total - left), &frame.eax.word, 278 left > 4 ? 4 : left); 279 280 if (left <= 4) { 281 buf[0][total] = '\0'; 282 break; 283 } else 284 left -= 4; 285 } 286 287 if (debug) { 288 char visbuf[strlen(*buf) * 4]; 289 strnvis(visbuf, *buf, sizeof(visbuf), VIS_TAB | VIS_NL | VIS_CSTYLE); 290 printf("vmware_get_clipboard: \"%s\"\n", visbuf); 291 } 292 293 return (1); 294} 295 296void 297vmware_set_clipboard(char *buf) 298{ 299 struct vm_backdoor frame; 300 uint32_t total, left; 301 302 if (debug) { 303 char visbuf[strlen(buf) * 4]; 304 strnvis(visbuf, buf, sizeof(visbuf), VIS_TAB | VIS_NL | VIS_CSTYLE); 305 printf("vmware_set_clipboard: \"%s\"\n", visbuf); 306 } 307 308 if (!strlen(buf)) 309 return; 310 311 bzero(&frame, sizeof(frame)); 312 313 frame.eax.word = VM_MAGIC; 314 frame.ecx.part.low = VM_CMD_SET_CLIPBOARD_LEN; 315 frame.ecx.part.high = 0xffff; 316 frame.edx.part.low = VM_PORT_CMD; 317 frame.edx.part.high = 0; 318 frame.ebx.word = (uint32_t)strlen(buf); 319 320 vm_cmd(&frame); 321 322 total = left = strlen(buf); 323 324 for (;;) { 325 bzero(&frame, sizeof(frame)); 326 327 frame.eax.word = VM_MAGIC; 328 frame.ecx.part.low = VM_CMD_SET_CLIPBOARD; 329 frame.ecx.part.high = 0xffff; 330 frame.edx.part.low = VM_PORT_CMD; 331 frame.edx.part.high = 0; 332 333 memcpy(&frame.ebx.word, buf + (total - left), 334 left > 4 ? 4 : left); 335 336 vm_cmd(&frame); 337 338 if (left <= 4) 339 break; 340 else 341 left -= 4; 342 } 343 344 return; 345}