Image File Execution Options just inserts the debugger in front of the command line


If you use the Image File Execution Options registry key to force a program to run under the debugger, all the kernel does is insert the debugger in front of the command line. In other words, the CreateProcess function figure out what program is about to be run and checks the Image File Execution Options. If it finds a debugger, then the debugger is prepended to the command line and then CreateProcess resumes as if that were the command line you had passed originally.

In particular, it doesn't do anything with the other parameters to the CreateProcess function. If you passed special parameters via the STARTUPINFO structure, those parameters get passed to the debugger. And the PROCESS_INFO that is returned by the CreateProcess function describes the debugger, not the process being debugged.

Specifically, if you specified the STARTF_USESHOWWINDOW flag and passed, say, SW_HIDE, as the wShowWindow, then the debugger will be hidden. This bites me every so often when I am called upon to debug a program that happens to be launched as hidden. I'll slap the debugger underneath it with Image File Execution Options, run through the scenario, and then... nothing.

And then eventually I realize, "Oh, right, the debugger is hidden."

To unstick myself, I fire up a program like Spy to get the window handle of the hidden debugger and fire up a scratch copy of Notepad so I can make it do my bidding and show the window.

ntsd -Ggx notepad
<F12>

Break instruction exception - code 80000003 (first chance)
eax=7ffdf000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005
eip=7c901230 esp=00a1ffcc ebp=00a1fff4 iopl=0         nv up ei pl zr na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
7c901230 cc               int     3
0:001> r esp=esp-4
0:001> ed esp 1
0:001> r esp=esp-4
0:001> ed esp 0x00010164
0:001> r esp=esp-4
0:001> ed esp eip
0:001> r eip=user32!showwindow
0:001> g
0:001> q

The first two commands push the value SW_SHOWNORMAL (numerical value 1) onto the stack. Then goes the window handle. And then the return address. Move the instruction pointer to user32!ShowWindow and we've simulated the function call ShowWindow(0x00010164, SW_SHOWNORMAL);. Once I let execution resume, *boom* the debugger window appears and I can continue my work.

Comments (24)
  1. Mark Steward says:

    I frequently find myself having to do this for API calls (SendMessage, especially) that aren’t available on the command line.  Isn’t there an easier way to call into Win32 (without VBA)?

    Another handy call is for when the debugger itself is hosed (works in ntsd):

    resp=@esp-8

    ed @esp 0n{TargetPID}

    g=kernel32!DebugActiveProcessStop

    [Yaniv Pessach wrote a program that takes a function name and a parameter list on the command line. Ony simple types are supported, but for some purposes that may be enough. -Raymond]
  2. KJK::Hyperion says:

    Just what is it that makes Notepad such a perfect designated debugger victim?

  3. Mark Steward says:

    Make that ed @esp @eip 0n{TargetPID}

  4. El Guapo says:

    Er, why not just try again. Why go through all that manual command entering into the debugger when all you need to do is run it again without the SW_HIDE?

    [Why waste your time with Image File Execution options? Just run Notepad under the debugger manually! -Raymond]
  5. anonymous says:

    Just what is it that makes Notepad such a perfect

    designated debugger victim?

    It’s single-threaded and straight-forward crud code.

  6. GregM says:

    "Just what is it that makes Notepad such a perfect designated debugger victim?"

    My guess: it’s lightweight so it starts fast, and it’s in the PATH, so it’s fast to type the command line.

  7. GregM says:

    "Why go through all that manual command entering into the debugger when all you need to do is run it again without the SW_HIDE?"

    This assumes that it’s easy to run it again exactly as it was just run.  That may not be the case.

  8. KJK::Hyperion says:

    Yes, but, I wonder why not cmd? or the venerable winver? There’s something about notepad

  9. Anders says:

    or just use winspy (http://www.catch22.net/software/winspypics.asp) and show the window

  10. Kevin says:

    You can also use the .call command if you don’t want to fool around with the stack directly. For example:

    0:001> .call user32!NtUserShowWindow(0x303e2, 6)

    Thread is set up for call, ‘g’ will execute.

    WARNING: This can have serious side-effects,

    including deadlocks and corruption of the debuggee.

    0:001> g

    eax=7ffde000 ebx=00000000 ecx=00000000 edx=77c4f06d esi=00000000 edi=00000000

    eip=77c02ea8 esp=01c3f7e0 ebp=01c3f80c iopl=0         nv up ei pl zr na pe nc

    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

    ntdll!DbgBreakPoint:

    77c02ea8 cc              int     3

  11. Mark Steward says:

    KJK::Hyperion: I wondered this, and decided that Notepad’s more popular than Winver and has a main window.  In fact, it’s got most elements of a Windows program (writing a Notepad replacement was a common project in Win32 programming books), and simpler code than Calc or Winmine.  You can also change its title, or type something in and scan memory for it.  Cmd has the whole console window thing, which it just annoying.

    Kevin: what symbols are you using?

  12. Ehtyar says:

    For sending random messages to windows: http://www.maxoutput.com/SendMsg.html

    For modifying properties of random windows: http://nirsoft.net/utils/winexp.html

    Ehtyar.

  13. Nick says:

    I admit I’m not real knowledgeable when it comes to Win32 debugging, but I got lost about the time ntsd was invoked.

    I assume you’re connecting the debugger to Notepad, and then issuing commands to manually execute the ShowWindow API call, correct?

    I’ve not heard of ntsd before. Does it come with Windows, or is it part of Visual Studio (which I have installed)?

  14. Dean Harding says:

    I’ve not heard of ntsd before. Does it come with Windows, or is it part

    of Visual Studio (which I have installed)?

    It’s part of the "Debugging Tools for Windows".

  15. Chad Austin says:

    Python (with the pywin32 package) works pretty well too:

    import win32gui, win32con

    win32gui.ShowWindow(0x5656, win32con.SW_SHOW)

  16. Jonathan says:

    I must say that’s a rather poor design on the kernel’s part, and
    very easy to cause minor behavior changes when launched under debugger
    – exactly those who would hide the bug you’re trying to find. Also,
    this trick is non-trivial (= few would ever figure it out without
    having the accurate magic spell from a great wizard).

    Alternately, you could run ntsd as a debugging server:

    ntsd -server tcp:port=1234 -gGW

    And then connect with windbg:

    windbg -remote tcp:server=localhost,port=1234

    Also works for services that are not allowed to interact with the desktop. And across the network too (of course).

    [And what would be a better design? -Raymond]
  17. Neil says:

    Kevin wrote:

    You can also use the .call command if you don’t want to fool around with the stack directly. For example:

    0:001> .call user32!NtUserShowWindow(0x303e2, 6)

    ^ Symbol not a function in ‘.call user32!NtUserShowWindow(0x303e2, 6)’

    Raymond mentioned that you can do this if you have some other function with the same signature as the API that you’re trying to call, but that’s unlikely when you’re debugging Notepad.

    Raymond wrote:

    Yaniv Pessach wrote a program that takes a function name and a parameter list on the command line.

    Personally I use a version of the Callfunc executable from Undocumented Windows 3.1 that I modified to work as a Win32 console application. Sadly I don’t have the exact source of the current version I use available – I tried and failed to add pagination to the dump command (I couldn’t work out how to wait for a keypress…)

  18. Harold Hunt says:

    I’m usually a lurker… but after seeing this (never thought of it before) I have to remark:

    That’s awesome!

    Thanks Raymond

    Harold

  19. Neil says:

    [And what would be a better design? -Raymond]

    Perhaps the debugger could use saved winposinfo for itself and pass on the startup info to the child process?

    [And that’s the design we have today. -Raymond]
  20. Dean Harding says:

    Perhaps the debugger could use saved winposinfo for itself and

    pass on the startup info to the child process?

    The debugger can choose to do whatever it likes. We’re talking about the design of Image File Execution Options here.

Comments are closed.

Skip to main content