Skip to content

Commit 132da7b

Browse files
felixriesebergcopy
authored andcommitted
vmware: add legacy text-clipboard backdoor commands (6-9)
Host stages bytes via the vmware-clipboard-host bus event; guest polls GETSELLENGTH/GETNEXTPIECE. Guest pushes via SETSELLENGTH/SETNEXTPIECE and the device emits vmware-clipboard-guest when the buffer fills. Same protocol as open-vm-tools' pre-RPC copy/paste, so a tiny Win9x agent can do IN EAX,DX from ring 3 with no driver.
1 parent 96fd8b8 commit 132da7b

1 file changed

Lines changed: 77 additions & 4 deletions

File tree

src/vmware.js

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { BusConnector } from "./bus.js";
88
const VMWARE_PORT = 0x5658;
99
const VMWARE_MAGIC = 0x564D5868;
1010

11+
const CMD_GETSELLENGTH = 6;
12+
const CMD_GETNEXTPIECE = 7;
13+
const CMD_SETSELLENGTH = 8;
14+
const CMD_SETNEXTPIECE = 9;
1115
const CMD_GETVERSION = 10;
1216
const CMD_GETTIME = 23;
1317
const CMD_ABSPOINTER_DATA = 39;
@@ -32,14 +36,17 @@ const BUTTON_MIDDLE = 0x08;
3236
const RELATIVE_PACKET = 0x00010000;
3337

3438
const QUEUE_MAX = 1024;
39+
const CLIP_MAX = 0x10000;
3540

3641
/**
3742
* VMware backdoor (port 0x5658). Lets a guest driver read absolute pointer
3843
* position so the guest cursor can track the host cursor 1:1 without pointer
39-
* lock, and implements GETTIME (23) so a guest agent can keep the guest clock
40-
* in sync with the host (guests only read the RTC at boot, so a restored
41-
* state resumes with a stale clock). PS/2 still supplies the mouse IRQ; the
42-
* driver reads this port on each IRQ12. While the host pointer is locked
44+
* lock, lets a guest agent sync the clipboard with the host using the legacy
45+
* text commands (6–9), and implements GETTIME (23) so a guest agent can keep
46+
* the guest clock in sync with the host (guests only read the RTC at boot, so
47+
* a restored state resumes with a stale clock). PS/2 still supplies the mouse
48+
* IRQ; the driver reads this port on each IRQ12. While the host pointer is
49+
* locked
4350
* (e.g. for games), movement is reported as relative packets instead, since
4451
* no meaningful absolute position exists.
4552
*
@@ -69,6 +76,22 @@ export function VMwareMouse(cpu, bus)
6976
this.last_y = -1;
7077
this.tail_is_move = false;
7178

79+
/** @type {Uint8Array} host→guest text staged for the guest to read */
80+
this.clip_out = new Uint8Array(0);
81+
this.clip_out_cursor = 0;
82+
this.clip_out_fresh = false;
83+
84+
/** @type {Uint8Array} guest→host text being received */
85+
this.clip_in = new Uint8Array(0);
86+
this.clip_in_cursor = 0;
87+
88+
this.bus.register("vmware-clipboard-host", function(data)
89+
{
90+
this.clip_out = data.length > CLIP_MAX ? data.subarray(0, CLIP_MAX) : data;
91+
this.clip_out_cursor = 0;
92+
this.clip_out_fresh = true;
93+
}, this);
94+
7295
/**
7396
* Whether the host pointer is currently locked (browser pointer lock).
7497
* While locked, the host cursor position is meaningless, so movement is
@@ -225,6 +248,56 @@ VMwareMouse.prototype.port_read32 = function()
225248
reg32[REG_EBX] = VMWARE_MAGIC;
226249
return 6;
227250

251+
case CMD_GETSELLENGTH:
252+
if(!this.clip_out_fresh)
253+
{
254+
return 0xFFFFFFFF | 0;
255+
}
256+
this.clip_out_fresh = false;
257+
this.clip_out_cursor = 0;
258+
return this.clip_out.length;
259+
260+
case CMD_GETNEXTPIECE:
261+
{
262+
const c = this.clip_out;
263+
let i = this.clip_out_cursor;
264+
const v = (c[i] | 0) | (c[i + 1] | 0) << 8 |
265+
(c[i + 2] | 0) << 16 | (c[i + 3] | 0) << 24;
266+
this.clip_out_cursor = i + 4;
267+
return v;
268+
}
269+
270+
case CMD_SETSELLENGTH:
271+
{
272+
const n = Math.min(reg32[REG_EBX] >>> 0, CLIP_MAX);
273+
this.clip_in = new Uint8Array(n);
274+
this.clip_in_cursor = 0;
275+
if(n === 0)
276+
{
277+
this.bus.send("vmware-clipboard-guest", this.clip_in);
278+
}
279+
return 0;
280+
}
281+
282+
case CMD_SETNEXTPIECE:
283+
{
284+
const c = this.clip_in;
285+
const v = reg32[REG_EBX] >>> 0;
286+
let i = this.clip_in_cursor;
287+
if(i < c.length) c[i++] = v;
288+
if(i < c.length) c[i++] = v >>> 8;
289+
if(i < c.length) c[i++] = v >>> 16;
290+
if(i < c.length) c[i++] = v >>> 24;
291+
this.clip_in_cursor = i;
292+
if(i >= c.length)
293+
{
294+
this.bus.send("vmware-clipboard-guest", c);
295+
this.clip_in = new Uint8Array(0);
296+
this.clip_in_cursor = 0;
297+
}
298+
return 0;
299+
}
300+
228301
case CMD_GETTIME:
229302
{
230303
// EAX = host time in seconds since the Unix epoch (UTC),

0 commit comments

Comments
 (0)