SmidgeonSoft Logo

PEBrowse Professional
Interactive Debugger

Last Steps and Shutdown

Last Steps and the Shutdown of the Program

If the title of this tutorial led you to believe that we would visit EVERY executed instruction in Wilderland.EXE, I am sorry to disappoint you.  That would be a virtually impossible task as may be inferred by looking at the size of the Index.  There have been, however, a number of highlights along the way that may entice you to explore areas of interest in the CLR.  Instead, we will now turn our attention to a brief overview of the shutdown of the program, and, more importantly, how PEBrowse Interactive can help you understand this phase.  Do you remember setting an additional breakpoint inside the main method of the Wilderland program a while back?  Let us now close down the application (don't terminate it inside the debugger (Debug/Terminate Process)!)  The disassembly window should now be displaying the JITted code for Wilderland.WilderlandForm::Main with the current instruction located just after the System.Windows.Forms.Application::Run call.  Single-step to the second call statement (remembering to F10 (Debug/Step Over the first call statement) and step into it -- see Figure 12.

Figure 12

For those of you who have read my discussion on the JIT thunk layer, this code may look familiar; to the others I say do not step into the call statement for "mscorwks.dll!_PreStubWorker" -- there are spiders lurking!  All should now run to the return statement, and single-step (F11) until you see something looking like the code found in Listing 1. 

Listing 1

Disassembly of THUNK at 0x071D1070
  0x71D1070: PUSH     EDX
  0x71D1071: PUSH     0x791B2FD8
  0x71D1076: PUSH     EBP
  0x71D1077: PUSH     EBX
  0x71D1078: PUSH     ESI
  0x71D1079: PUSH     EDI
  0x71D107A: LEA      ESI,DWORD PTR [ESP+0x10]
  0x71D107E: PUSH     ECX
  0x71D107F: PUSH     EDX
  0x71D1080: MOV      EBX,DWORD PTR FS:[0xE2C]
  0x71D1087: MOV      EDI,DWORD PTR [EBX+0x8]
  0x71D108A: MOV      DWORD PTR [ESI+0x4],EDI
  0x71D108D: MOV      DWORD PTR [EBX+0x8],ESI
  0x71D1090: PUSH     DWORD PTR [ESI+0x10]
  0x71D1093: MOV      EAX,DWORD PTR [ESI+0x14]
  0x71D1096: TEST     EAX,EAX
  0x71D1098: JZ       0x71D109D               ; (*+0x5)
  0x71D109A: ADD      EAX,0xC
  0x71D109D: PUSH     EAX                      ; <==0x071D1098(*-0x5)
  0x71D109E: MOV      EAX,DWORD PTR [ESI-0x18]
  0x71D10A1: TEST     EAX,EAX
  0x71D10A3: JZ       0x71D10A8               ; (*+0x5)
  0x71D10A5: ADD      EAX,0xC
  0x71D10A8: PUSH     EAX                      ; <==0x071D10A3(*-0x5)
  0x71D10A9: PUSH     DWORD PTR [ESI-0x14]
  0x71D10AC: MOV      BYTE PTR [EBX+0x4],0x0
  0x71D10B0: TEST     BYTE PTR [EBX],0x1F
  0x71D10B3: JNZ      0x71D10DB               ; (*+0x28)
  0x71D10B5: MOV      EAX,DWORD PTR [ESI+0x8]  ; <==0x071D10E0(*+0x2B)
  0x71D10B8: CALL     DWORD PTR [EAX+0x8]       <<<<<<<<<<<< RUN TO HERE!
  0x71D10BB: MOV      BYTE PTR [EBX+0x4],0x1
  0x71D10BF: CMP      DWORD PTR [0x793DCA78],0x0
  0x71D10C6: JNE      0x71D10E2               ; (*+0x1C)
  0x71D10C8: LEA      ESP,DWORD PTR [ESI-0x18] ; <==0x071D10E7(*+0x1F)
  0x71D10CB: MOV      DWORD PTR [EBX+0x8],EDI
  0x71D10CE: ADD      ESP,0x8
  0x71D10D1: POP      EDI
  0x71D10D2: POP      ESI
  0x71D10D3: POP      EBX
  0x71D10D4: POP      EBP
  0x71D10D5: ADD      ESP,0xC
  0x71D10D8: RET      0x8
  0x71D10DB: CALL     mscorwks.dll!InstructionFormat::CanReach + 0x10BE66 ; (0x792C1F55) ; <==0x071D10B3(*-0x28)
  0x71D10E0: JMP      0x71D10B5
  0x71D10E2: CALL     mscorwks.dll!UMThunkStubRareDisableWorker - 0x90EFE ; (0x7923139C) ; <==0x071D10C6(*-0x1C)
  0x71D10E7: JMP      0x71D10C8

This code is one of the interop or PInvoke (or Platform Invoke) thunk layer variants between managed and native code -- there might be additional call statements, mostly concerned with security setup on your system.  Run until the call statement:

0x71D10B8: CALL     DWORD PTR [EAX+0x8]   

and step into this call.  You will find yourself now magically transported into the MessageBoxW API inside of USER32.DLL, or unmanaged code!  Return from this system DLL call (after dismissing the small dialog box) and in short order you will return back to the sheltered, managed environment of .NET.  Notice how seamlessly you have moved between the two environments.  Hopefully, this little demonstration will convince you that it is possible to debug your application in one session when you have PInvoke calls present.  PInvoke calls through COM can also be debugged, only here the interface is more complex and imposing.  I will leave it to you to discover how much thicker this layer is.

As the small dialog box informed us, "The adventure is ending".  Therefore, we now will briefly examine what transpires when your .NET application is ending.  If you turn your attention to the stack frame display, you will find that there are a number of frames displayed.  Looking more closely and if you are familiar with the JIT compiler, you may realize from the names that the code has not returned from the first call to the first method that the JIT compiler was asked to handle!  Single-step over the next several instructions until you hit the code for mscorwks.dll:SystemDomain::ExecuteMainMethod.  If you wish later to more closely investigate what you are skipping over, press View/Execution Path (or Ctrl+X) and you will see a new window with the caption, "Execution Path".  PEBrowse Interactive is recording all of the statements it broke on, i.e., that you visited, in the background.  You can now copy the instructions, perhaps after appending a comment or two (Edit/Add Remark), to the clipboard, and save the record of your session in a separate file for later review.  If you leave this window open, you can see how new instructions are added at the bottom of the display (this action is also configurable).  The number in parentheses after the address is a visit count, which may help you recognize when you are stepping through a loop.

Eventually, single stepping brings us to a routine internal to MSCORWKS named EEShutDown where as you might expect the CLR shutdown activities commence.  Shortly after that you will find a call to another internal routine, SafeExitProcess -- let's step into that.  Aha!  We see a call to KERNEL32's API, ExitProcess, which as many who have explored a program's termination know, leads to LdrShutdownProcess inside of NTDLL (bringing us full circle as it were) and after notifying DLLs and the debugger (in the call to CsrClientCallServer) that the process is ending, the code calls to the kernel service, ZwTerminateProcess, which takes care of the remaining cleanup tasks in kernel memory.  This is the last user mode instruction for Wilderland.EXE and the last statement PEBrowse Interactive can show you, so I have lived up to at least part of my promise to lead you through the program from its beginning to its end.  One final note: the execution path contents remain until you start a new debugging session or close down the debugger, so you will have one final chance to save information about this session before losing it for good.

prev page 1st page next page
Home | FAQ | News | Software | Documentation | SiteSearch | Licensing | Links | SiteIndex | AboutUs | ContactUs
Page best viewed at 1024x768.   Page last updated 2006-11-19.   This site is PIKT® powered.
Copyright © 1998-2006 Russell Osterlund.  All rights reserved.  SmidgeonSoft is a wholly-owned division of SmidgeonSoft, LLC.
Home FAQ News Software Documentation SiteSearch