_UNDOCUMENTED CORNER_ by Kelly Zytaruk edited by Andrew Schulman Figure 1: Accessing a VMCB from a Windows program. // for Windows 3.1 only! #define VM_FROM_HWND(hWndDosBox) \ *((LPDWORD) MK_FP(GetWindowWord(hWndDosBox, GWW_HINSTANCE), 0x0FC)) // undocumented Windows API function; see UndocWin, pp. 303-304 extern BOOL FAR PASCAL IsWinOldApTask(HANDLE hTask); #define ISDOSBOX(hWnd) IsWinOldApTask(GetWindowTask(hWnd)) if (ISDOSBOX(hwnd)) { DWORD vm_handle = VM_FROM_HWND(hwnd); VMCB far *vm_cb = (VMCB far *) map_linear(VM_FROM_HWND(hwnd), sizeof(VMCB)); WORD ldt = vm_cb->CB_LDT; // or *((WORD far *) ((BYTE far *) vm_cb) + 0x114) FreeSelector(FP_SEG(vm_cb)); // now do something with LDT in other VM } Figure 2: Displaying a Selector List. (a) C:\DDJ\VM>protdump -vm #1 VMCB=804C1000 high lin=81C00000h #2 VMCB=8065A000 high lin=82000000h (b) C:\DDJ\VM>protdump -ptr -list -dword 804c1048 8 804007E0 | 000E0000 0000104F 804007D4 | 000D0000 00001047 804007C8 | 000C0000 0000103F 804007BC | 000B8000 00001037 804007B0 | 000B0000 0000102F 804007A4 | 000A0000 00001027 80400798 | 000F0000 0000101F 8040078C | 00000400 00001017 80400770 | 00000000 0000100F 804002D4 | 000009A0 00001007 804002C8 | 0001E490 000000C7 (c) C:\DDJ\VM>protdump -prot #1 101f:fff0 81CFFFF0 | EA 5B E0 00 F0 30 36 2F 30 36 2F 39 31 00 FC 00 | .[...06/06/91... 81D00000 | 00 00 00 56 44 49 53 4B 33 2E 33 80 00 01 01 00 | ...VDISK3.3..... (d) C:\DDJ\VM>protdump -prot #1 0617:00f0 81C4FEF0 | 00 00 50 52 4F 47 4D 41 4E 00 54 44 00 00 00 00 | ..PROGMAN.TD.... (e) C:\DDJ\VM>protdump -all DOS:330 -word 2 #1 81C00CD0 | 29DC #2 82000CD0 | 710B Figure 3: Layout of a sample VM Control Block VxD Owner Size Offset into VMCB --------- ---- ---------------- VMM 210h 0h VPICD BCh 210h VTD 17h 2CCh VDDVGA 840h 2EAh VKD E3h B24h VFD 2h C08h - Note that sizes are rounded DOSMGR 4Dh C0Ch up to a multiple of 4 bytes . . . . . . . . . VSERVER 4h 1A9Ch VMPOLL 14h 1AA0h VPFD 8Ch 1AB4h Figure 4: The Windows 3.1 Virtual Machine Control Block (offsets are for the retail version) typedef struct { DWORD CB_VM_Status; // 00h DWORD CB_High_Linear; // 04h DWORD CB_Client_Pointer; // 08h DWORD CB_VMID; // 0Ch DWORD CB_PM_Int_Table; // 10h DWORD CB_VM_ExecTime; // 14h DWORD CB_V86_PageTable; // 18h DWORD CB_Local_Port_Trapping_BitMap[8]; // 1Ch DWORD CB_Begin_Nested_Exec_List; // 3Ch DWORD CB_OS_Stack; // 40h DWORD CB_Scheduler_Flags; // 44h DWORD CB_Selector_List; // 48h WORD CB_unused0; // 4Ch WORD CB_Locked_PM_Stack_LDT; // 4Eh WORD CB_Locked_PM_Stack_GDT; // 50h WORD CB_Locked_PM_Stack_Prev_SS; // 52h DWORD CB_Locked_PM_Stack_Prev_ESP; // 54h DWORD CB_Locked_PM_Stack_hMem; // 58h DWORD CB_Locked_PM_Stack_Count; // 5Ch DWORD CB_Locked_PM_Stack_EIP; // 60h DWORD CB_PM_App_CB; // 64h DWORD CB_VM_List_Link; // 68h DWORD CB_ListNext; // 6Ch DWORD CB_ListHead; // 70h DWORD CB_BlockedSemaphore; // 74h DWORD CB_SuspendedList_Head; // 78h DWORD CB_Suspended_BlockedSemaphore; // 7Ch DWORD CB_Exec_Priority_Bits; // 80h DWORD CB_SchedulerStatus // 84h DWORD CB_Suspended_Stack; // 88h DWORD CB_TSS_ESP0; // 8Ch DWORD CB_hMem_Stack; // 90h DWORD CB_SuspendedVM_Count; // 94h DWORD CB_SuspendedVM_EventHandle; // 98h WORD CB_ForeGround_TS_Priority; // 9Ch WORD CB_BackGround_TS_Priority; // 9Eh DWORD CB_Weighted_Priority; // A0h DWORD CB_Weighted_Time; // A4h DWORD CB_Next_Scheduled_VM; // A8h DWORD CB_Last_Weighted_VMTime; // ACh DWORD CB_ExtendedErrorCode; // B0h DWORD CB_ExtendedErrorRefData; // B4h DWORD CB_V86_PgTbl_PhysAddr; // B8h DWORD CB_Int_Table_Instance; // BCh DWORD CB_hMem_VMDataArea; // C0h DWORD CB_Int_Table_hMem; // C4h DWORD CB_DeviceV86Pages[9]; // C8h DWORD CB_V86PageableArray[8]; // ECh DWORD CB_MMGR_Flags; // 10Ch DWORD CB_MMGR_Pages; // 110h WORD CB_LDT; // 114h WORD CB_unused2; // 116h DWORD CB_hMem_LDT; // 118h DWORD CB_VM_Event_Count; // 11Ch DWORD CB_VM_Event_List; // 120h DWORD CB_Priority_VM_Event_List; // 124h DWORD CB_CallWhenVMIntsEnabled_Count; // 128h DWORD CB_CallWhenVMIntsEnabled_List; // 12Ch DWORD CB_Next_Timeout_Handle; // 130h DWORD CB_Prev_Timeout_Handle; // 134h DWORD CB_First_Timeout; // 138h DWORD CB_Expiration_Time; // 13Ch DWORD CB_IDT_Base_hMem; // 140h WORD CB_unused3; // 144h WORD CB_IDT_Limit; // 146h DWORD CB_IDT_Base; // 148h struct { // 14Ch DWORD Ex_EIP; WORD Ex_CS; } CB_Exception_Handlers[32]; DWORD CB_V86_CallBack_List; // 20Ch // end of VMM portion // start of VxD CB areas } VM_ControlBlock; // size: 210h bytes Figure 5: Using the documented VMCB CB_High_Linear field to read real-mode memory in other VMs. The somehow_get_VM_handle() function could be implemented using VM_FROM_HWND() in Figure 1, or with the generic VxD. DWORD LinAddr = (0x40L << 4) + 8; DWORD VMHandle = somehow_get_VM_handle(); VMCB far *VMCB = map_linear(VMHandle, sizeof(VMCB)); LinAddr += VMCB->CB_High_Linear; FreeSelector(FP_SEG(VMCB)); WORD far *WPtr = map_linear(LinAddr, sizeof(WORD)); WORD W = *WPtr; FreeSelector(FP_SEG(WPtr)); // W is WORD at 40:08 in other VM