How to set a break point at the assembly instruction that returns an error code

By Linkai Yu 

When debugging, there are those situations when you don't have access to source code, you might have debugging symbols of a program that neither crashes nor generates an exception but simply displays an error message such as "The program encounters an error: 12055". So you can't use a debugger to break into the precise location of the error. By the time the error message is displayed, all you can see on the call stack is the code related to error display. The real location of instruction where the error was encountered is gone from the call stack. If you can set a break point at the instruction that determines the error, you might look at the assembly instructions and guess what could cause the problem. The question then is how can we find the instruction that generates the error code?

Here is what I do, not bullet proof but with a good success rate.

 The idea is to search the relevant DLL(s) for the error number. Then verify the error number by unassemblying the instructions backward from the location to see if the instructions makes sense. If you see instruction pattern mov <target>, <value>, then most likely it's the right instruction.

 The following is an example of an error code returned from WININET.DLL: 12055.

step 1. get the hex of 12055 by debugger command "?"

0:003> ?0n12055
Evaluate expression: 12055 = 00002f17

step 2. find the start and end address of WININET.DLL

0:003> lm m wininet
start    end        module name
46a70000 46b40000   WININET    (private pdb symbols) 

step 3. search for hex 00002f17. since the search starts from lower address towards higher address, the serach byte order is: 17 2f 00 00

0:003> s 46a70000 46b40000 17 2f 00 00
46ac2623  17 2f 00 00 8b 8e d8 00-00 00 57 e8 07 3e ff ff  ./........W..>..
46ac2870  17 2f 00 00 e9 27 5a ff-ff 8b 46 34 83 e8 07 0f  ./...'Z...F4....
46ac4273  17 2f 00 00 0f 84 62 4c-fc ff 81 f9 7d 2f 00 00  ./....bL....}/..
46afcbbb  17 2f 00 00 0f 84 46 01-00 00 48 48 0f 84 3e 01  ./....F...HH..>.
46b0e9b8  17 2f 00 00 7d 00 00 00-30 f0 af 46 01 00 00 00  ./..}...0..F....

this search turns out 5 results.

step 4. unassemble backward from the location to verify if the instructions make sense.

0:003> ub 46ac2623  +4
WININET!ICSecureSocket::ReVerifyTrust+0x1f0 [d:\vista_gdr\inetcore\wininet\common\ssocket.cxx @ 1380]:
46ac2600 2f              das
46ac2601 0000            add     byte ptr [eax],al
46ac2603 755a            jne     WININET!ICSecureSocket::ReVerifyTrust+0x292 (46ac265f)
46ac2605 8b8d24fdffff    mov     ecx,dword ptr [ebp-2DCh]
46ac260b 8b86d8000000    mov     eax,dword ptr [esi+0D8h]
46ac2611 81c900000004    or      ecx,4000000h
46ac2617 0988f8020000    or      dword ptr [eax+2F8h],ecx
46ac261d c78528fdffff172f0000 mov dword ptr [ebp-2D8h],2F17h 

0:003> ub 46ac2870  +4
WININET!ICSecureSocket::VerifyTrust+0x4e9 [d:\vista_gdr\inetcore\wininet\common\ssocket.cxx @ 1674]:
46ac2844 89bd24fdffff    mov     dword ptr [ebp-2DCh],edi
46ac284a 899d1cfdffff    mov     dword ptr [ebp-2E4h],ebx
46ac2850 399d1cfdffff    cmp     dword ptr [ebp-2E4h],ebx
46ac2856 748f            je      WININET!ICSecureSocket::VerifyTrust+0x422 (46ac27e7)
46ac2858 8b86d8000000    mov     eax,dword ptr [esi+0D8h]
46ac285e 8b8d24fdffff    mov     ecx,dword ptr [ebp-2DCh]
46ac2864 0988f8020000    or      dword ptr [eax+2F8h],ecx
46ac286a c78528fdffff172f0000 mov dword ptr [ebp-2D8h],2F17h 

of the five search results, two show valid instructions: 

   c78528fdffff172f0000 mov dword ptr [ebp-2D8h],2F17h 

  [ebp-2D8h] points to a local variable. The C++ equivalent code would look like:

    intError = 12055;

now you can set break point at address 46ac286a and 46ac261d and have fun debugging!

bp 46ac2623;bp 46ac2870 ;g

Thanks

Linkai Yu