Debugging ‘Last Error’ problems


On a few occasions I have run into problems where some Windows API was returning a ‘strange’ error code. Here is a technique that works with some APIs:



  1. Go get public symbols for your operating system – add http://msdl.microsoft.com/download/symbols to your symbol path and define a symbol cache directory

  2. Set a breakpoint on SetLastError: to be on the safe side, set the breakpoint on both {,,kernel32}_SetLastError@4 and {,,ntdll}_RtlSetLastWin32Error@4

  3. Assuming that you get a million calls, figure out where the last error is being passed to the set last error functions. For me, this is *(unsigned*)(@esp+4), but your mileage may vary. Once you know where the error code is passed, you can either set a tracepoint, or turn the breakpoint into a conditional breakpoint (see more information bellow).

  4. Run your scenario and find your bug

To set a tracepoint:



  1. Right click on the breakpoint, and click ‘When Hit…’

  2. Check ‘print a message’

  3. Set the message to ‘{*(unsigned*)(@esp+4)} — $CALLSTACK’, or whatever information you want

  4. Click ‘okay’

To set a conditional breakpoint:



  1. Right click on the breakpoint, and click ‘Condition…’

  2. Set your condition to be ‘*(unsigned*)(@esp+4)==1234’ where 1234 is the strange error code that you are seeing

 


Comments (1)

  1. Barry Bond says:

    Putting the breakpoint on the function means you’ll see the lasterror writes from all threads. If your debugger doesn’t support per-thread code breakpoints, here’s another alternative…

    Disassemble ntdll!_RtlSetLastWin32Error and look at the code (this is from my WinXP SP2 x86 machine):

    ntdll!RtlRestoreLastWin32Error:

    7c910340 8bff mov edi,edi

    7c910342 55 push ebp

    7c910343 8bec mov ebp,esp

    7c910345 64a118000000 mov eax,fs:[00000018]

    7c91034b 8b4d08 mov ecx,[ebp+0x8]

    7c91034e 894834 mov [eax+0x34],ecx

    7c910351 5d pop ebp

    7c910352 c20400 ret 0x4

    That last "mov" is the one that writes the LastError value to the per-thread structure. If you single-step the code to that line, you can use that address (the value of eax+34) and set a data breakpoint on it.

    Also, if you’re using NTSD/WinDBG, you can determine the per-thread LastError address directly. Type "~" and hit enter, to dump the list of threads. The lasterror address is the address of the "Teb" + 0x34. Here is a sample, again, from my WinXP SP2 x86 machine:

    0:000> ~

    . 0 id: d6c.c94 Suspend: 1 Teb 7ffdf000 Unfrozen

    0:000> ba w4 7ffdf000+34

    0:000> g

Skip to main content