Search This Blog

Monday, 20 October 2014

WinDbg : Debugging A Stack Over Flow

WinDbg : Debugging A Stack Over Flow


We have  learnt about different calling conventions, we have also seen how to view the call stack. Now lets try and debug a stack over flow problem. The idea here is to actually root cause the real reason behind the stack over flow and not just know that it is a stack which has over flowed. Since debugging is always more challenging without actual source code, we will try to figure this one out without the help of source code as well.

Commands used:

  1. uF
  2. kb

************* Symbol Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       SRV*e:\Websims*http://msdl.microsoft.com/download/symbols
OK                                             E:\STACK\

Microsoft (R) Windows Debugger Version 6.3.9600.17200 X86
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: E:\STACK\Over.exe

************* Symbol Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       SRV*e:\Websims*http://msdl.microsoft.com/download/symbols
OK                                             E:\STACK
Symbol search path is: SRV*e:\Websims*http://msdl.microsoft.com/download/symbols;E:\STACK
Executable search path is: 
ModLoad: 00f80000 00fa2000   Over.exe
ModLoad: 77580000 77700000   ntdll.dll
ModLoad: 76ba0000 76cb0000   C:\Windows\syswow64\kernel32.dll
ModLoad: 77130000 77177000   C:\Windows\syswow64\KERNELBASE.dll
ModLoad: 74910000 7497d000   C:\Windows\SysWOW64\SYSFER.DLL
(cf0.17cc): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=9ebf0000 edx=0021dd88 esi=fffffffe edi=00000000
eip=7762103b esp=0032f8a0 ebp=0032f8cc iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2c:
7762103b cc              int     3

0:000> g

(cf0.17cc): Stack overflow - code c00000fd (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** WARNING: Unable to verify checksum for Over.exe
eax=0000001e ebx=7efde000 ecx=00000014 edx=0000000a esi=00000000 edi=00000000
eip=00f81035 esp=00233000 ebp=0023300c iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
Over!add+0x15:
00f81035 e8cbffffff      call    Over!ILT+0(_add) (00f81005)

We executed the binary inside the debugger and it crashed. the debugger gave out the hint that it is a stack over flow problem. Now lets see what further information we can dig out from the debugging session and whether we can root cause the actual code causing this fault.

The first thing to see is the stack which over flowed. 

0:000> kb
ChildEBP RetAddr  Args to Child              
0023300c 00f8103a 0000000a 00000014 0000001e Over!add+0x15 
00233020 00f8103a 0000000a 00000014 0000001e Over!add+0x1a 
00233034 00f8103a 0000000a 00000014 0000001e Over!add+0x1a 
00233048 00f8103a 0000000a 00000014 0000001e Over!add+0x1a 
0023305c 00f8103a 0000000a 00000014 0000001e Over!add+0x1a 
00233070 00f8103a 0000000a 00000014 0000001e Over!add+0x1a 
00233084 00f8103a 0000000a 00000014 0000001e Over!add+0x1a 
00233098 00f8103a 0000000a 00000014 0000001e Over!add+0x1a 
<output snipped as it is the same line over and over again like the above>

It appears that there is a function add inside of which the control is repeatedly transferred (offset 0x1a). This is a tad bit strange. Why would a function always transfer control within itself. As some of you might have already guessed it, it seems to be the case of recursion. Recursive function calls tend to produce such stack traces, where the call stack shows the same offset for many stack frames.

Since, the function add is the only thing showing up on the stack, it indeed is a good place to start digging further. Lets start with disassembling the function.

0:000> uf over!add
Over!add :
    4 00f81020 55              push    ebp                                        // This the the prologue block
    4 00f81021 8bec           mov     ebp,esp

    4 00f81023 51              push    ecx
    6 00f81024 8b4508       mov     eax,dword ptr [ebp+8]
    6 00f81027 03450c       add      eax,dword ptr [ebp+0Ch]
    6 00f8102a 8945fc        mov     dword ptr [ebp-4],eax
    7 00f8102d 8b4d0c       mov     ecx,dword ptr [ebp+0Ch]
    7 00f81030 51              push    ecx
    7 00f81031 8b5508       mov     edx,dword ptr [ebp+8]
    7 00f81034 52              push    edx
    7 00f81035 e8cbffffff   call     Over!ILT+0(_add) (00f81005)    // Here we see that the function calls                                                                                                              // itself
    7 00f8103a 83c408       add     esp,8                                     // Added 2 parameters to stack so cleaning                                                                                                      //  them

    8 00f8103d 8be5         mov    esp,ebp                                   // This is the epilogue block
    8 00f8103f 5d           pop     ebp
    8 00f81040 c3           ret                                                      // This is a CDECL calling convention

At this point we are fairly certain that this recursive function is the one which is causing an over flow. One of the distinctive features of recursion is that the flow is controlled by conditional statements. 

Example, for a recursive function calculating factorial of an integer, the pseudo code would look like this:

function factorial is:
input: integer n such that n >= 0
output: [n × (n-1) × (n-2) × … × 1]

    1. create new variable called running_total with a value of 1

    2. begin loop
          1. if n is 0, exit loop
          2. set running_total to (running_total × n)
          3. decrement n
          4. repeat loop

    3. return running_total

end factorial

Step 2.1 is the exit condition, which will branch the code off to exit the loop.

Such code would show some kind of a flow control logic when disassembled as well. Flow control logic is handled by instructions like CMP (to compare values) and then jumping (JMP, JE, JNE etc) to a code block based on the result of the comparison. In our faulting function, we see no such construct at all.  The function seems to be infinitely calling itself without bothering to create an exit condition. Which is the reason it is eventually runs out of stack frames and crashes.

The above was a very simple example to debug, but I do hope that it will serve as a starting point to debugging and understanding such problems.


1 comment:

  1. Buy Questz Titanium Headphones At the Best Price 2021 - TITanium
    You don't titanium curling iron need a smartphone titanium rod in femur complications to purchase a apple watch titanium vs aluminum Questz black oxide vs titanium drill bits Headphones at the best price, titanium mens rings and you can get it for just $17 on Amazon.com. Read

    ReplyDelete