Steps for analysing traps in PM DLLs:

This example is of a trap in PMMERGE.DLL, but caused by an application fault!

Because we have a trap E, we set the fatal vector under the Kernel Debugger (or use TRAPDUMP=ON in CONFIG.SYS to take a dump) then re-create the problem.

##vsf *##g
Trap 14 (0EH) - Page Fault 0006, Not Present, Write Access, User Mode
eax=00000007 ebx=00273fcc ecx=00000001 edx=00000007 esi=12d3e089 edi=00000000
eip=1bd3d261 esp=00273ebc ebp=00273f1c iopl=2 rf -- -- nv up ei pl nz ac po cy
cs=005b ss=0053 ds=0053 es=0053 fs=150b gs=0000  cr2=00000000  cr3=001d4000
005b:1bd3d261 f3a5          repe movsd es:00000000=invalid ds:12d3e089=69727453
##ln
005b:00000000 turkey:FLAT:__$dummy$ + 1bd3d261

We have trapped in a DLL, probably PMMERGE.DLL, certainly not in the user's .EXE code. We unwind the stack to find the return address in the user's .EXE

##dd %ebp
%00273f1c  00273f5c 1bd3d05b 00000000 00000005
%00273f2c  00080001 00000000 00080013 00010423
%00273f3c  00190008 00000000 00000001 00000014
%00273f4c  00000000 80000144 00000001 00190008
%00273f5c  00273fa8 1bd03893 80000144 00000071
%00273f6c  00280018 00000000 000103ec 00000000
%00273f7c  00000000 00190008 00273fcc 00000000
%00273f8c  00000000 ffffffff 80000144 12d31630
##d
%00273f9c  00000000 12d31494 00190008 00273ff4
%00273fac  00010932 00190008 00273fcc 0000000c
%00273fbc  00000004 00000004 00000007 80000144
%00273fcc  80000144 00000071 00280018 00000000
%00273fdc  0092d87d 0000027a 000000f0 00000000
%00273fec  12d31630 00190008 00000000 1bfbbf68
%00273ffc  00022bf4
Invalid linear address: %00274000

Not all the stack was paged in to physical memory, but never mind. Enough is there to allow us to find the return address to the user's application code.

Following the base pointer (EBP):

%00273f1c  00273f5c 1bd3d05b

%00273f5c  00273fa8 1bd03893 80000144 00000071

%00273f9c                             00273ff4
%00273fac  00010932 00190008 00273fcc 0000000c

The return address is %10932. We inspect the code just before this address.

##u %10932-20
%00010912 25837ddc2a         and     eax,2adc7d83
%00010917 0f8507000000       jnz     %00010924
%0001091d e916000000         jmp     %00010938
%00010922 8bc0               mov     eax,eax
%00010924 8d45d8             lea     eax,[ebp-28]
%00010927 50                 push    eax
%00010928 ff75fc             push    dword ptr [ebp-04]
%0001092b b002               mov     al,02
%0001092d e8862ecf1b         call    %1bd037b8
%00010932 83c408             add     esp,+08
%00010935 ebc1               jmp     %000108f8
%00010937 fc                 cld
##ln %1bd037b8
%00000000 turkey:FLAT:__$dummy$ + 1bd037b8

This call was to a routine at %1bd037b8. LN doesn't give us a useful symbol (our .EXE is being selected instead of the correct .DLL symbol). We find out who owns this address from the memory management control blocks.

##.m %1bd037b8

*har     par      cpg        va    flg next prev link hash hob   hal
 01c0 %feaf168a 00000001 %ffe3c000 101 000d 01e2 01bd 0000 013e 000c      =0000
 hal=000c pal=%ffe5c078  har=01c0  hptda=010c  pgoff=000d7  f=021
 har     par      cpg        va    flg next prev link hash hob   hal
 01bd %feaf1648 00000001 %ffede000 101 01a2 01be 0106 0000 013e 0009      =0000
 hal=0009 pal=%ffe5c060  har=01bd  hptda=010c  pgoff=00000  f=021
 har     par      cpg        va    flg next prev link hash hob   hal
 0106 %feaf068e 000000f0 %1bd00000 3d9 0105 0107 0000 0000 013e 0000  hco=00307
 hob   har hobnxt flgs own  hmte  sown,cnt lt st xf
 013e  01c0 0000  1838 0139 0139  0000 00  02 00 00 shared    c:pmmerge.dll
 hco=00307 pco=fe64ef3e  hconext=00296 hptda=032f f=1c  pid=0019 a:turkey.exe
 hco=00296 pco=fe64ed09  hconext=001f9 hptda=0301 f=1c  pid=0009 d:cmd.exe
 hco=001f9 pco=fe64e9f8  hconext=00188 hptda=02c9 f=1c  pid=0007 d:cmd.exe
 hco=00188 pco=fe64e7c3  hconext=00107 hptda=0291 f=1c  pid=0005 c:2000.exe
 hco=00107 pco=fe64e53e  hconext=0008e hptda=023b f=1c  pid=0004 c:pmshell.exe
 hco=0008e pco=fe64e2e1  hconext=0002a hptda=01b5 f=1c  pid=0003 c:harderr.exe
 hco=0002a pco=fe64e0ed  hconext=00000 hptda=010c f=1d  pid=0002 c:pmshell.exe

We can see that our call was to an entry point in PMMERGE.DLL. We need to activate PMMERGE's symbols.

##wa pmmerge
##ln %1bd037b8
%1bd037b8 pmmerge:PM32BIT:WIN32DISPATCHMSG
##ln
005b:1bd3d064 pmmerge:PM32BIT:LoadStrMsg + 1fd
005b:1bd3d2a8 WIN32POSTQUEUEMSG - 47

So we called WinDispatchMsg and some time later we probably called LoadStrMsg, which is where we trapped. First we need to check the parameters to WinDispatchMsg. These are:

HAB

PQMSG

The QMSG at %273fcc is also in the stack we dumped:

%00273fcc  80000144 00000071 00280018 00000000%00273fdc  0092d87d 0000027a 000000f0 00000000

The first parameter is the HWND. We convert this to a PWND, dump the WND and look for the window procedure entry point.

##dd phandletable l1
9f3f:0000ab78  12d50000

##dd %12d50000+20+(8*144) l2
%12d50a40  12d31494 00000000

##dd %12d31494
%12d31494  12d31838 12d3c974 00000000 12d3c974
%12d314a4  00c80262 0104029e 80000000 00000008
%12d314b4  12d314f0 00000004 12d31630 80000144
%12d314c4  00000000 000103ec 00000000 00000000
%12d314d4  12d3147c 00000000 00000000 00000000
%12d314e4  00000000 2050534d 00000034 12d31894
%12d314f4  00004b4e 00000000 0000fc4e 00000019
%12d31504  00000000 000103ec 00000000 00000000

Note:

We could have used the following more complex single command construct to achieve the same result:

##dd %(dw(%(dw(phandletable))+20+(8*144)))
%12d31494  12d31838 12d3c974 00000000 12d3c974
%12d314a4  00c80262 0104029e 80000000 00000008
%12d314b4  12d314f0 00000004 12d31630 80000144
%12d314c4  00000000 000103ec 00000000 00000000
%12d314d4  12d3147c 00000000 00000000 00000000
%12d314e4  00000000 2050534d 00000034 12d31894
%12d314f4  00004b4e 00000000 0000fc4e 00000019
%12d31504  00000000 000103ec 00000000 00000000

The window procedure entry point is at offset +0x34.

We now unassemble this:

##u %103ec
%000103ec 55                 push    ebp
%000103ed 8bec               mov     ebp,esp
%000103ef 83ec08             sub     esp,+08
%000103f2 8b4508             mov     eax,dword ptr [ebp+08]
%000103f5 a32c0d0200         mov     dword ptr [00020d2c],eax
%000103fa 8b450c             mov     eax,dword ptr [ebp+0c]
%000103fd e93a000000         jmp     %0001043c
%00010402 8bc0               mov     eax,eax
%00010404 ff7508             push    dword ptr [ebp+08]
%00010407 b001               mov     al,01
%00010409 e8322dcf1b         call    WIN32QUERYANCHORBLOCK (%1bd03140)
%0001040e 8945fc             mov     dword ptr [ebp-04],eax
##u
%00010411 6a00               push    +00
%00010413 6a14               push    +14
%00010415 6a01               push    +01
%00010417 6a00               push    +00
%00010419 ff75fc             push    dword ptr [ebp-04]
%0001041c b005               mov     al,05
%0001041e e81dccd21b         call    WIN32LOADSTRING (%1bd3d040)
%00010423 83c418             add     esp,+18
%00010426 eb1e               jmp     %00010446
%00010428 8b4508             mov     eax,dword ptr [ebp+08]
%0001042b e8d0fbffff         call    main (%00010000)
%00010430 eb14               jmp     %00010446

We notice that we trapped in an internal routine called LoadStrMsg and that we have called WinLoadString in the window procedure. Could these be related?

We see from the PM Programming Reference that WinLoadString has 5 parameters. The right most is a pointer to a buffer and we see that the window procedure has pushed 0 on the stack this will surely cause WinLoadString to trap at some point. How do we make this supposition less circumstantial and more concrete?

Clearly, for EBP to take us back to a call to WinDispatchMsg, without finding a stack frame from the window procedure implies that PMMERGE is using optimised code when the trap occurred. That is, the conventional use of EBP is not in place - and this does occur in many internal routines in PMMERGE, for performance reasons. If we scan back through the stack we notice the address %10423 occurring shortly before (in time) the call to LoadStrMsg. This address is the return address from the WinLoadString call in the window procedure. It would seem therefore that we have called that API with the bad parameter as suspected!


[Back] [Next]