Einzelnen Beitrag anzeigen
Alt 02.10.2016, 13:24   #5
leecher
Moderator
#unhide (This post can be viewed by guests also)

NTVDM Fullscren Issue #2
========================

Today I noticed another problem with the NTVDM Fullscreen handling in
Windows 7. I can't call it a bug, as it's by design, but it's an annoyance
anyway. When running DBASE for DOS, I noticed that switching between
windowed mode and Fullscreen in some cases took 5 seconds to react which is
very annoying and gives the impression that the NTVDM got stuck.
This issue is not with all DOS-applications, but it can be reproduced with
DBASE.

This time, it's not CONHOST which is at fault, but it's NTVDM itself.
When attaching WinDbg to the failed process, the following Stacktrace can be
seen (we already know this one from the CONHOST bug..):

Code:
kd> !process 0 0 ntvdm.exe
PROCESS 863ef2a8  SessionId: 1  Cid: 06f4    Peb: 7ffdf000  ParentCid: 0f04
    DirBase: 3f4c7580  ObjectTable: 9498ccd0  HandleCount: 158.
    Image: ntvdm.exe

kd> .process /r /p 863ef2a8  
Implicit process is now 863ef2a8
.cache forcedecodeuser done
Loading User Symbols
........................................
kd> !process 863ef2a8  
PROCESS 863ef2a8  SessionId: 1  Cid: 06f4    Peb: 7ffdf000  ParentCid: 0f04
    DirBase: 3f4c7580  ObjectTable: 9498ccd0  HandleCount: 158.
    Image: ntvdm.exe
    VadRoot 86200e58 Vads 110 Clone 0 Private 462. Modified 375. Locked 0.
    DeviceMap 97da9648
    Token                             973c1040
    ElapsedTime                       00:06:20.128
    UserTime                          00:00:00.000
    KernelTime                        00:00:00.000
    QuotaPoolUsage[PagedPool]         0
    QuotaPoolUsage[NonPagedPool]      0
    Working Set Sizes (now,min,max)  (1672, 50, 345) (6688KB, 200KB, 1380KB)
    PeakWorkingSetSize                1699
    VirtualSize                       77 Mb
    PeakVirtualSize                   78 Mb
    PageFaultCount                    2880
    MemoryPriority                    BACKGROUND
    BasePriority                      8
    CommitCharge                      1898

...

        THREAD 851e2040  Cid 06f4.0818  Teb: 7ffdc000 Win32Thread: 00000000 WAIT: (UserRequest) UserMode Non-Alertable
            860a7ff0  NotificationEvent
            8637c6e8  NotificationEvent
        Not impersonating
        DeviceMap                 97da9648
        Owning Process            863ef2a8       Image:         ntvdm.exe
        Attached Process          N/A            Image:         N/A
        Wait Start TickCount      139029         Ticks: 109 (0:00:00:01.703)
        Context Switch Count      3186             
        UserTime                  00:00:00.000
        KernelTime                00:00:00.171
        Win32 Start Address ntvdm!HeartBeatThread (0x0e2c9a56)
        Stack Init 93e3ffd0 Current 93e3f748 Base 93e40000 Limit 93e3d000 Call 0
        Priority 15 BasePriority 15 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
        ChildEBP RetAddr  
        93e3f760 82899e2d nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
        93e3f798 82898c87 nt!KiSwapThread+0x266
        93e3f7c0 82894a64 nt!KiCommitThreadWait+0x1df
        93e3f93c 82a46b0c nt!KeWaitForMultipleObjects+0x535
        93e3fbc8 82a46879 nt!ObpWaitForMultipleObjects+0x262
        93e3fd18 82859896 nt!NtWaitForMultipleObjects+0xcd
        93e3fd18 77ac70f4 nt!KiSystemServicePostCall (FPO: [0,3] TrapFrame @ 93e3fd34)
        012cfa5c 77ac6a44 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
        012cfa60 75d56a8e ntdll!ZwWaitForMultipleObjects+0xc (FPO: [5,0,0])
        012cfafc 7650be2e KERNELBASE!WaitForMultipleObjectsEx+0x100 (FPO: [SEH])
        012cfb44 7650be9c kernel32!WaitForMultipleObjectsExImplementation+0xe0 (FPO: [5,8,4])
        012cfb60 0e2d96b7 kernel32!WaitForMultipleObjects+0x18 (FPO: [4,0,0])
        012cfbac 0e2c95ca ntvdm!DoHandShake+0x5c (FPO: [SEH])
        012cfbc8 0e2c9916 ntvdm!DelayHeartBeat+0x53 (FPO: [1,2,4])
        012cfbe4 0e2c9ad0 ntvdm!Win32_host_timer+0x19 (FPO: [0,2,0])
        012cfc18 7650ee1c ntvdm!HeartBeatThread+0x7a (FPO: [SEH])
        012cfc24 77ae37eb kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
        012cfc64 77ae37be ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
        012cfc7c 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0])
So it's stuck at the first WaitForMultipleObjects call in ntvdm!DoHandShake:

Code:
ntvdm!DoHandShake:
0e4c965b 6a14            push    14h
0e4c965d 68f8e04f0e      push    offset ntvdm!mouse_cursor_mode_change+0x4d3 (0e4fe0f8)
0e4c9662 e8cdc6fcff      call    ntvdm!_SEH_prolog4 (0e495d34)
0e4c9667 33db            xor     ebx,ebx
0e4c9669 895de4          mov     dword ptr [ebp-1Ch],ebx
0e4c966c a1c0d1530e      mov     eax,dword ptr [ntvdm!hErrorHardwareEvent (0e53d1c0)]
0e4c9671 8945dc          mov     dword ptr [ebp-24h],eax
0e4c9674 a1a4d4530e      mov     eax,dword ptr [ntvdm!hMainThreadSuspended (0e53d4a4)]
0e4c9679 8945e0          mov     dword ptr [ebp-20h],eax
0e4c967c ff35b4d4530e    push    dword ptr [ntvdm!hResume (0e53d4b4)]
0e4c9682 ff15b011490e    call    dword ptr [ntvdm!_imp__ResetEvent (0e4911b0)]
0e4c9688 ff35acd4530e    push    dword ptr [ntvdm!hSuspend (0e53d4ac)]
0e4c968e 8b3d7411490e    mov     edi,dword ptr [ntvdm!_imp__SetEvent (0e491174)]
0e4c9694 ffd7            call    edi
0e4c9696 c605c42d510e01  mov     byte ptr [ntvdm!HandshakeInProgress (0e512dc4)],1
0e4c969d 891dc8d1530e    mov     dword ptr [ntvdm!HandshakeStage (0e53d1c8)],ebx
0e4c96a3 68f4010000      push    1F4h
0e4c96a8 53              push    ebx
0e4c96a9 8d45dc          lea     eax,[ebp-24h]
0e4c96ac 50              push    eax
0e4c96ad 6a02            push    2
0e4c96af 8b35cc11490e    mov     esi,dword ptr [ntvdm!_imp__WaitForMultipleObjects (0e4911cc)]
0e4c96b5 ffd6            call    esi
0e4c96b7 3d02010000      cmp     eax,102h
0e4c96bc 7547            jne     ntvdm!DoHandShake+0xaa (0e4c9705)
0e4c96be b814070000      mov     eax,714h
0e4c96c3 f0810800000001  lock or dword ptr [eax],1000000h
0e4c96ca ff352ce0530e    push    dword ptr [ntvdm!CurrentMonitorTeb (0e53e02c)]
0e4c96d0 e81fa70000      call    ntvdm!ThreadLookUp (0e4d3df4)
0e4c96d5 3bc3            cmp     eax,ebx
0e4c96d7 7408            je      ntvdm!DoHandShake+0x86 (0e4c96e1)
0e4c96d9 50              push    eax
0e4c96da 6a01            push    1
0e4c96dc e877d10200      call    ntvdm!NtVdmControl (0e4f6858)
0e4c96e1 bbc0270900      mov     ebx,927C0h
0e4c96e6 53              push    ebx
0e4c96e7 6a00            push    0
0e4c96e9 8d45dc          lea     eax,[ebp-24h]
0e4c96ec 50              push    eax
0e4c96ed 6a02            push    2
0e4c96ef ffd6            call    esi
0e4c96f1 83f801          cmp     eax,1
0e4c96f4 7427            je      ntvdm!DoHandShake+0xc2 (0e4c971d)
0e4c96f6 c705c8d1530e14000000 mov dword ptr [ntvdm!HandshakeStage (0e53d1c8)],14h
0e4c9700 e9d4000000      jmp     ntvdm!DoHandShake+0x17e (0e4c97d9)
0e4c9705 3bc3            cmp     eax,ebx
0e4c9707 750f            jne     ntvdm!DoHandShake+0xbd (0e4c9718)
0e4c9709 c705c8d1530e0a000000 mov dword ptr [ntvdm!HandshakeStage (0e53d1c8)],0Ah
0e4c9713 e9c1000000      jmp     ntvdm!DoHandShake+0x17e (0e4c97d9)
...
As can be seen, there are 2 handles being waited on:
ntvdm!hErrorHardwareEvent and ntvdm!hMainThreadSuspended with a timeout of
5000ms (1F4h). Bingo!
Now obviously normally the hMainThreadSuspended needs to get satisfied, we're
not keen on a handshake error. If the wait times out (return value is
WAIT_TIMEOUT=102h) then a Wakeup-call to the Monitor in the kernel, which is
responsible for the V86 execution of the program is being sent via NtVdmControl
and then there is another Wait for 60 seconds (927C0h) this time.
This gives us a clue where to search for where the necessary condition is being
set:

Code:
ntvdm!cpu_simulate:
0e4d3148 8bff            mov     edi,edi
0e4d314a 55              push    ebp
0e4d314b 8bec            mov     ebp,esp
0e4d314d 51              push    ecx
0e4d314e 64a118000000    mov     eax,dword ptr fs:[00000018h]
0e4d3154 53              push    ebx
0e4d3155 a32ce0530e      mov     dword ptr [ntvdm!CurrentMonitorTeb (0e53e02c)],eax
0e4d315a 64a118000000    mov     eax,dword ptr fs:[00000018h]
0e4d3160 56              push    esi
0e4d3161 8bb0180f0000    mov     esi,dword ptr [eax+0F18h]
0e4d3167 33db            xor     ebx,ebx
0e4d3169 57              push    edi
0e4d316a bf00020000      mov     edi,200h
0e4d316f 3bf3            cmp     esi,ebx
0e4d3171 0f846a010000    je      ntvdm!cpu_simulate+0x199 (0e4d32e1)
0e4d3177 8d8674060000    lea     eax,[esi+674h]
0e4d317d c60001          mov     byte ptr [eax],1
0e4d3180 c786d802000007000100 mov dword ptr [esi+2D8h],10007h
0e4d318a 3818            cmp     byte ptr [eax],bl
0e4d318c 0f8448010000    je      ntvdm!cpu_simulate+0x192 (0e4d32da)
0e4d3192 f6051407000003  test    byte ptr ds:[714h],3
0e4d3199 7405            je      ntvdm!cpu_simulate+0x58 (0e4d31a0)
0e4d319b e87b090000      call    ntvdm!DispatchInterrupts (0e4d3b1b)
0e4d31a0 e8aef6ffff      call    ntvdm!getMSW (0e4d2853)
0e4d31a5 a801            test    al,1
0e4d31a7 7456            je      ntvdm!cpu_simulate+0xb7 (0e4d31ff)
0e4d31a9 391d54df500e    cmp     dword ptr [ntvdm!VDMForWOW (0e50df54)],ebx
0e4d31af 7524            jne     ntvdm!cpu_simulate+0x8d (0e4d31d5)
0e4d31b1 e843f6ffff      call    ntvdm!getIF (0e4d27f9)
0e4d31b6 85c0            test    eax,eax
0e4d31b8 751b            jne     ntvdm!cpu_simulate+0x8d (0e4d31d5)
0e4d31ba 81ff00020000    cmp     edi,200h
0e4d31c0 7513            jne     ntvdm!cpu_simulate+0x8d (0e4d31d5)
0e4d31c2 8d45fc          lea     eax,[ebp-4]
0e4d31c5 50              push    eax
0e4d31c6 6a0d            push    0Dh
0e4d31c8 c745fc03000000  mov     dword ptr [ebp-4],3
0e4d31cf ff159814490e    call    dword ptr [ntvdm!_imp__NtVdmControl (0e491498)]
0e4d31d5 81a698030000fffffdff and dword ptr [esi+398h],0FFFDFFFFh
0e4d31df 391dc42d510e    cmp     dword ptr [ntvdm!HandshakeInProgress (0e512dc4)],ebx    ; BINGO #1!
0e4d31e5 740b            je      ntvdm!cpu_simulate+0xaa (0e4d31f2)
0e4d31e7 ff35a4d4530e    push    dword ptr [ntvdm!hMainThreadSuspended (0e53d4a4)]    ; Our wait condition
0e4d31ed e804ffffff      call    ntvdm!CheckScreenSwitchRequest (0e4d30f6)        ; Aha, what's that?
0e4d31f2 881d3865500e    mov     byte ptr [ntvdm!MainThreadInMonitor (0e506538)],bl
0e4d31f8 e84f160000      call    ntvdm!FastEnterPm (0e4d484c)
0e4d31fd eb2b            jmp     ntvdm!cpu_simulate+0xe2 (0e4d322a)
0e4d31ff 818e9803000000000200 or  dword ptr [esi+398h],20000h
0e4d3209 391dc42d510e    cmp     dword ptr [ntvdm!HandshakeInProgress (0e512dc4)],ebx
...
So there are multiple calls to ntvdm!CheckScreenSwitchRequest in cpu_simulate
of the NTVDM, which is the trap handler for V86 execution.

Code:
ntvdm!CheckScreenSwitchRequest:
0e4d30f6 8bff            mov     edi,edi
0e4d30f8 55              push    ebp
0e4d30f9 8bec            mov     ebp,esp
0e4d30fb 56              push    esi
0e4d30fc 8b357811490e    mov     esi,dword ptr [ntvdm!_imp__WaitForSingleObject (0e491178)]
0e4d3102 57              push    edi
0e4d3103 6a00            push    0
0e4d3105 ff35acd4530e    push    dword ptr [ntvdm!hSuspend (0e53d4ac)]
0e4d310b ffd6            call    esi
0e4d310d 8b3db011490e    mov     edi,dword ptr [ntvdm!_imp__ResetEvent (0e4911b0)]
0e4d3113 eb1f            jmp     ntvdm!CheckScreenSwitchRequest+0x3e (0e4d3134)
0e4d3115 ff157411490e    call    dword ptr [ntvdm!_imp__SetEvent (0e491174)]
0e4d311b 6aff            push    0FFFFFFFFh
0e4d311d ff35b4d4530e    push    dword ptr [ntvdm!hResume (0e53d4b4)]
0e4d3123 ffd6            call    esi
0e4d3125 ff7508          push    dword ptr [ebp+8]
0e4d3128 ffd7            call    edi
0e4d312a 6a00            push    0
0e4d312c ff35acd4530e    push    dword ptr [ntvdm!hSuspend (0e53d4ac)]
0e4d3132 ffd6            call    esi
0e4d3134 ff7508          push    dword ptr [ebp+8]
0e4d3137 85c0            test    eax,eax
0e4d3139 74da            je      ntvdm!CheckScreenSwitchRequest+0x1f (0e4d3115)
0e4d313b ffd7            call    edi
0e4d313d 5f              pop     edi
0e4d313e 5e              pop     esi
0e4d313f 5d              pop     ebp
0e4d3140 c20400          ret     4
So we finally get the idea what is going on here:
NTVDM is assuming that the hosted DOS application generates some traps that
cause it to return to the cpu_simulate thread which in turn checks for
HandshakeInProgress in multiple places. If the is is set, it suspends
execution of the DOS application and the signals success by setting the
hMainThreadSuspended event which in turn releases the Wait-condition in
DoHandshake and let it continue its screen switch routine. After that the main
thread can be resumed and DOS-application is happy to continue its execution.
But if the application like DBASE isn't doing any traps, the Wait-call runs
into its 5 second timeout and this causes it to finally wake up the V86
MONITOR in kernel via NtVdmControl syscall and then finally the event gets
trapped and everything continues as expected.

Now why didn't this occur i.e. in the Windows XP version of NTVDM?
When looking at the DoHandshake() code in there, it just does a normal
SuspendThread(MainThread); in there and everything is working flawlessly.
Why M$ decided to change this behaviour is unknown to me, I guess only their
architects can tell us.

So this time, it's not a bug, but I think the timeout of 5 seconds is a bad
choice and a bit too high and therefore causes annoyance to the user.
So let's change it to another value, i.e. 500ms which looks fair considered
the fact that there is a wakeup-call to the Monitor in case of timeout anyway:
Search for

Code:
  68 88 13 00 00 53 8D 45 DC 50
in NTVDM and replace it with i.e.:

Code:
  68 F4 01 00 00 ...
for 500ms.
When patching ntvdm in Windows\System32 directory, ensure that you have
permissions to do so, so doing

Code:
takeown /f ntvdm.exe
icacls ntvdm.exe /grant [your username]:F
before.
Of course, you can also use a little patcher that I wrote for your convenience:
Just run ntvdm_pat.exe to patch your Windows 7 NTVDM to 500ms timeout.
If you want to use a different timeout value, please pass the desired value to
it on the command line, i.e. if you want to use 200ms timeout, run:

Code:
ntvdm_pat 200
This should fix another annoying behaviour of Windows 7 NTVDM for people who
for whatever reason cannot stick with Windows XP (i.e. because of a lack of
drivers).

Direct download: http://www.waldbauer.com/tmp/dl.php?download=ntvdmpatch
Angehängte Dateien
Dateityp: zip ntvdm_pat.zip‎ (8.8 KB, 9x aufgerufen)
leecher ist offline   Mit Zitat antworten