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:
************* 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.
Buy Questz Titanium Headphones At the Best Price 2021 - TITanium
ReplyDeleteYou 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