WinDbg Kata 001: Division By Zero on App Startup

The first Kata is about a division by zero exception that happens on startup, specifically a .net 4.5 console application (that’s explicitly set to x64 instead of AnyCPU and debugged with a 64-Bit WinDbg just so that I have a consistent environment) whose main method is this:

static void Main(string[] args)
{
    // To make sure the Compiler doesn't catch or optimize this
    int x = 10 - 10;
    // This blows up with a Division By Zero exception
    int i = 2000 / x;

    Console.WriteLine("The result of 2000 / {0} is {1}.", x, i);
}

Running this application results in an immediate crash, with no chance to attach a debugger. We start out by opening WinDbg and File > Open Executable. This results in WinDbg starting the app and immediately breaking into the debugger.

CommandLine: F:\WinDbgKatas\001-DivByZeroOnStartup\001-DivByZeroOnStartup\bin\Release\001-DivByZeroOnStartup.exe
Symbol search path is: SRV*C:\Users\mstum\Desktop\MSDN\DEBUG\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
ModLoad: 00000001`3fb50000 00000001`3fb56000   001-DivByZeroOnStartup.exe
ModLoad: 00000000`77290000 00000000`77439000   ntdll.dll
ModLoad: 000007fe`f86f0000 000007fe`f875f000   C:\windows\SYSTEM32\MSCOREE.DLL
ModLoad: 00000000`77170000 00000000`7728f000   C:\windows\system32\KERNEL32.dll
ModLoad: 000007fe`fd3d0000 000007fe`fd43c000   C:\windows\system32\KERNELBASE.dll
(30c4.337c): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00000000`7733cb60 cc              int     3

Debug > Go (or the g command line) makes it run and crash.

0:000> g
ModLoad: 000007fe`ff4c0000 000007fe`ff59b000   C:\windows\system32\ADVAPI32.dll
ModLoad: 000007fe`fe470000 000007fe`fe50f000   C:\windows\system32\msvcrt.dll
ModLoad: 000007fe`fd910000 000007fe`fd92f000   C:\windows\SYSTEM32\sechost.dll
ModLoad: 000007fe`fdf40000 000007fe`fe06d000   C:\windows\system32\RPCRT4.dll
ModLoad: 000007fe`f8650000 000007fe`f86e9000   C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscoreei.dll
ModLoad: 000007fe`fda80000 000007fe`fdaf1000   C:\windows\system32\SHLWAPI.dll
ModLoad: 000007fe`fd930000 000007fe`fd997000   C:\windows\system32\GDI32.dll
ModLoad: 00000000`77070000 00000000`7716a000   C:\windows\system32\USER32.dll
ModLoad: 000007fe`fdd50000 000007fe`fdd5e000   C:\windows\system32\LPK.dll
ModLoad: 000007fe`fd490000 000007fe`fd559000   C:\windows\system32\USP10.dll
ModLoad: 000007fe`fd610000 000007fe`fd63e000   C:\windows\system32\IMM32.DLL
ModLoad: 000007fe`fdba0000 000007fe`fdca9000   C:\windows\system32\MSCTF.dll
ModLoad: 000007fe`f7c50000 000007fe`f85ae000   C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
ModLoad: 000007fe`f7b70000 000007fe`f7c42000   C:\windows\system32\MSVCR110_CLR0400.dll
(30c4.337c): Unknown exception - code 04242420 (first chance)
ModLoad: 000007fe`f65f0000 000007fe`f7b6d000   C:\windows\assembly\NativeImages_v4.0.30319_64\mscorlib\1b61dcfd88eae971a10423d914e1014a\mscorlib.ni.dll
ModLoad: 000007fe`ff2b0000 000007fe`ff4b3000   C:\windows\system32\ole32.dll
ModLoad: 000007fe`fcef0000 000007fe`fceff000   C:\windows\system32\CRYPTBASE.dll
ModLoad: 000007fe`f64c0000 000007fe`f65ee000   C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clrjit.dll
ModLoad: 000007fe`fd9a0000 000007fe`fda77000   C:\windows\system32\OLEAUT32.dll
(30c4.337c): Integer divide-by-zero - code c0000094 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.

From here, we can load the .net debugging extensions through .loadby sos clr (remember, in .net 4.0 it’s clr instead of mscorwks) Now, this is a first chance exception and as the text says, it’s reported before any exception handling. Specifically, it’s not on the managed stack yet, so !pe won’t find it:

0:000> .loadby sos clr
0:000> !pe
There is no current managed exception on this thread

At this point, we can look at the !ClrStack to get an idea where we crashed.

0:000> !ClrStack
OS Thread Id: 0x337c (0)
        Child SP               IP Call Site
000000000049ea60 000007fe9860009f *** WARNING: Unable to verify checksum for 001-DivByZeroOnStartup.exe
WinDbgKata.DivByZeroOnStartup.Program.Main(System.String[]) [f:\WinDbgKatas\001-DivByZeroOnStartup\001-DivByZeroOnStartup\Program.cs @ 12]
000000000049ed90 000007fef7c9f713 [GCFrame: 000000000049ed90]

Entering g again will give us a second chance, this time the Exception is on the managed stack.

0:000> g
ModLoad: 000007fe`fc140000 000007fe`fc14c000   C:\windows\system32\VERSION.dll
ModLoad: 000007fe`f3f70000 000007fe`f4079000   C:\Windows\Microsoft.NET\Framework64\v4.0.30319\diasymreader.dll
(30c4.337c): Integer divide-by-zero - code c0000094 (!!! second chance !!!)
KERNELBASE!RaiseException+0x39:
000007fe`fd3dbccd 4881c4c8000000  add     rsp,0C8h
0:000> !pe
Exception object: 0000000002572ed8
Exception type:   System.DivideByZeroException
Message:          Attempted to divide by zero.
InnerException:   <none>
StackTrace (generated):
    SP               IP               Function
    000000000049EA60 000007FE9860009F 001_DivByZeroOnStartup!WinDbgKata.DivByZeroOnStartup.Program.Main(System.String[])+0xf

StackTraceString: <none>
HResult: 80020012

Depending on the exact exception we can now dig further, since it’s on the managed stack we can !do it. This won’t give us much more information in the Division by Zero case, but if there’s an InnerException

0:000> !do 0000000002572ed8
Name:        System.DivideByZeroException
MethodTable: 000007fef6c6f020
EEClass:     000007fef675f068
Size:        160(0xa0) bytes
File:        C:\windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fef6c8aee0  4000002        8        System.String  0 instance 000000000257ae30 _className
000007fef6c89460  4000003       10 ...ection.MethodBase  0 instance 0000000000000000 _exceptionMethod
000007fef6c8aee0  4000004       18        System.String  0 instance 0000000000000000 _exceptionMethodString
000007fef6c8aee0  4000005       20        System.String  0 instance 0000000002578fd0 _message
000007fef6c88ee8  4000006       28 ...tions.IDictionary  0 instance 0000000000000000 _data
000007fef6c8b110  4000007       30     System.Exception  0 instance 0000000000000000 _innerException
000007fef6c8aee0  4000008       38        System.String  0 instance 0000000000000000 _helpURL
000007fef6c8b4c0  4000009       40        System.Object  0 instance 0000000002579100 _stackTrace
000007fef6c8b4c0  400000a       48        System.Object  0 instance 0000000002579148 _watsonBuckets
000007fef6c8aee0  400000b       50        System.String  0 instance 0000000000000000 _stackTraceString
000007fef6c8aee0  400000c       58        System.String  0 instance 0000000000000000 _remoteStackTraceString
000007fef6c8dc90  400000d       88         System.Int32  1 instance                0 _remoteStackIndex
000007fef6c8b4c0  400000e       60        System.Object  0 instance 0000000000000000 _dynamicMethods
000007fef6c8dc90  400000f       8c         System.Int32  1 instance      -2147352558 _HResult
000007fef6c8aee0  4000010       68        System.String  0 instance 0000000000000000 _source
000007fef6c8ed00  4000011       78        System.IntPtr  1 instance                0 _xptrs
000007fef6c8dc90  4000012       90         System.Int32  1 instance       -532462766 _xcode
000007fef6c77828  4000013       80       System.UIntPtr  1 instance                0 _ipForWatsonBuckets
000007fef6cf2960  4000014       70 ...ializationManager  0 instance 00000000025790c8 _safeSerializationManager
000007fef6c8b4c0  4000001        0        System.Object  0   shared           static s_EDILock
                                 >> Domain:Value  00000000005c4a50:NotInit  <<

Don’t use AdBlock in your Browser. Block in your Router instead.

One of the most popular browser extensions is AdBlock, which prevents loading of ads in websites. Now, there isn’t much wrong with ads itself, except that all the ad networks like to spy on you with tracking cookies and more malicious methods like supercookies that use browser plugins. There are some ridiculous attempts at allowing users to opt out of that (e.g., the complete failure that Do-Not-Track is) but these will never work because ad networks will continue to try everything they can to play dirty.

AdBlock browser extensions are one way to play dirty in return, but it’s not that effective: It only protects one browser, but won’t protect your smartphones, tablets or other machines without adblock on your network. You need to go deeper: You need to block malicious sites in your router, so that each and every device that goes through your router. Specifically, you want to override DNS entries to set malicious domains to 0.0.0.0. Any decent Linux-Firmware should do that, in my case I run TomatoUSB on my Asus RT-N16 router. (Disclaimer: Flashing new Firmware is done at your own risk. If you don’t understand the instructions on the Tomato Website, get someone who does 🙂 You will need to properly setup the whole thing, including your network configuration, DHCP, WiFi with security etc.).

Under Administration > Scripts > WAN Up, I have this script:

## Adblock script [Version 2.1 | 08 July 2008 | 3778 bytes]
##
## Created by Adrian Jon Kriel: root-AT-extremecooling-DOT-org
## Modified
## tomato WAN Up script
##
## 0 = disable
## 1 = enable
## (1) = default value
## optimising of dnsmasq, (1)
eval OPTDNSMASQ="1"
## automatic updating, (1)
eval AUTOUPDATE="1"
## MVPS HOSTS ~18,500 lines, 680 Kbyte, (1)
eval MVPSSOURCE="1"
## pgl.yoyo.org ~2,200 lines, 68 Kbyte, (1)
eval PGLSOURCE="1"
## hosts-file.net ~53,000 lines, 1.5 Mbyte, (0)
eval HSFSOURCE="0"
## Hosts File Project ~102,000 lines, 3.0 Mbyte ***6+mb free memory***, (0)
## This actually makes the RT-N16 grind to a halt, I guess it's not powerful enough.
eval HFPSOURCE="0"
##
## varibles
## location of temp file, (/tmp/hosts)
eval GENFILE="/tmp/hosts"
## redirect ip, (0.0.0.0)
eval REDIRECTIP="0.0.0.0"
## sources
eval MVPSOURCEFILE="http://www.mvps.org/winhelp2002/hosts.txt"
eval PGLSOURCEFILE="http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts;showintro=0"
eval HSFSOURCEFILE="http://support.it-mate.co.uk/downloads/hosts.txt"
eval HFPSOURCEFILE="http://hostsfile.mine.nu/Hosts"

if ping -c 1 yahoo.com ; then

eval GOTSOURCE="0"
echo "" > $GENFILE
## download 
if [ "$MVPSSOURCE" = "1" ]  ; then
if wget $MVPSOURCEFILE -O - >> $GENFILE ; then
logger ADBLOCK Downloaded $MVPSOURCEFILE
eval GOTSOURCE="1"
else
logger ADBLOCK Failed $MVPSOURCEFILE
fi
fi
if [ "$PGLSOURCE" = "1" ]  ; then
if wget $PGLSOURCEFILE -O - >> $GENFILE ; then
logger ADBLOCK Load $PGLSOURCEFILE
eval GOTSOURCE="1"
else
logger ADBLOCK Fail $PGLSOURCEFILE
fi
fi
if [ "$HSFSOURCE" = "1" ]  ; then
if wget $HSFSOURCEFILE -O - >> $GENFILE ; then
logger ADBLOCK load $HSFSOURCEFILE
eval GOTSOURCE="1"
else
logger ADBLOCK Fail $HSFSOURCEFILE
fi
fi
if [ "$HFPSOURCE" = "1" ]  ; then
if wget $HFPSOURCEFILE -O - >> $GENFILE ; then
logger ADBLOCK Load $HFPSOURCEFILE
eval GOTSOURCE="1"
else
logger ADBLOCK Fail $HFPSOURCEFILE
fi
fi

if [ "$GOTSOURCE" = "1" ]; then
logger ADBLOCK Got Source Files
#FREE MEMORY!
service dnsmasq stop
killall -9 dnsmasq
logger ADBLOCK Ignor Fail Safe
##strip source file
sed -i -e '/^[0-9A-Za-z]/!d' $GENFILE
sed -i -e '/%/d' $GENFILE
sed -i -e 's/[[:cntrl:][:blank:]]//g' $GENFILE
sed -i -e 's/^[ \t]*//;s/[ \t]*$//' $GENFILE

## dnsmasq, sanitize, optimised
sed -i -e 's/[[:space:]]*\[.*$//'  $GENFILE
sed -i -e 's/[[:space:]]*\].*$//'  $GENFILE
sed -i -e '/[[:space:]]*#.*$/ s/[[:space:]]*#.*$//'  $GENFILE		
sed -i -e '/^$/d' $GENFILE
sed -i -e '/127.0.0.1/ s/127.0.0.1//'  $GENFILE		
sed -i -e '/^www[0-9]./ s/^www[0-9].//'  $GENFILE		
sed -i -e '/^www./ s/^www.//' $GENFILE
## remove duplicates (resource friendly)	
cat $GENFILE | sort -u > $GENFILE.new
mv $GENFILE.new $GENFILE
## format
sed -i -e 's|$|/'$REDIRECTIP'|' $GENFILE
sed -i -e 's|^|address=/|' $GENFILE
## load values from dnsmasq config
cat /etc/dnsmasq.conf >> $GENFILE
## optimise dnsmasq
if [ "$OPTDNSMASQ" = "1" ] ; then
cat >> $GENFILE <<EOF
cache-size=2048
log-async=5
EOF
fi

## remove/whitelist websites
sed -i -e '/ark.intel.com/d' $GENFILE

## apply blacklist
dnsmasq --conf-file=$GENFILE

## failsafe added
dnsmasq
logger ADBLOCK Ignor Fail Safe

## dev info
logger ADBLOCK Unique Hosts Blocked $(awk 'END { print NR }' $GENFILE)
else
logger ADBLOCK Error Not Downloaded
fi
else
logger ADBLOCK Error No Internet
fi
## remove the generated files
rm $GENFILE*
## automatic update
if [ "$AUTOUPDATE" = "1" ] ; then
## script exists
if [ -x /tmp/script_wanup.sh ] ; then
cru a UpdateAdlist "0 6,12,18,0 * * * /tmp/script_wanup.sh >/dev/null 2>&1"
fi
fi
## the end

Note that some of the sources no longer work, but it’s a good starting point. Some sites you may want to visit may be blocked, see the “remove/whitelist websites” part and add the ones you want to unblock.

Under Advanced > DHCP / DNS, make sure that “Use internal DNS” is ticked and add additional sites like this:

address=/googlesyndication.com/0.0.0.0
address=/google-analytics.com/0.0.0.0
address=/googleadservices.com/0.0.0.0
address=/doubleclick.net/0.0.0.0
address=/finder.cox.net/0.0.0.0

The Status > Web Usage page is extremely helpful to find additional hosts that need to be blocked – there is a war out there and the bad guys keep moving, so as a target we also need to stay vigilant and move with them.

I’m still trying to figure out if there is a way to do Layer 7 filtering, to only disallow certain subdirectories on a URL (e.g., prevent facebook.com/plugins without blocking Facebook as a whole) or to inspect and block or modify requests (e.g., add the (mostly) useless DNT: 1 header to each and every outgoing HTTP request, just in case. Or change cookie values/ids for "Social Media Share" plugins to random values.). Any decent Firewall does that (Stateful Packet Inspection), but I might want to get a more powerful router since the low memory and CPU speed (128 MB/480 MHz) on the RT-N16 makes some of the more interesting things slow down things a lot.

Debugging a .net 4.0 application when .net 4.5 is installed

I have a machine that runs .net 4.0 and where I took a memory dump of an application. I moved the dump to my machine, which has .net 4.5 installed and tried to debug it in WinDbg:

0:000> .loadby sos clr
0:000> !DumpHeap
Failed to load data access DLL, 0x80004005
Verify that 1) you have a recent build of the debugger (6.2.14 or newer)
            2) the file mscordacwks.dll that matches your version of clr.dll is 
                in the version directory or on the symbol path
            3) or, if you are debugging a dump file, verify that the file 
                mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.
            4) you are debugging on supported cross platform architecture as 
                the dump file. For example, an ARM dump file must be debugged
                on an X86 or an ARM machine; an AMD64 dump file must be
                debugged on an AMD64 machine.

You can also run the debugger command .cordll to control the debugger's
load of mscordacwks.dll.  .cordll -ve -u -l will do a verbose reload.
If that succeeds, the SOS command should work on retry.

If you are debugging a minidump, you need to make sure that your executable
path is pointing to clr.dll as well.

On the internet, there’s a bunch of guides that focus on bringing in the right version of mscordacwks.dll, but that wasn’t my problem (I have setup the Symbol Server and WinDBG was correctly downloading the right version). Turns out that .net 4.5 comes with a new SOS.dll that is incompatible with .net 4.0 memory dumps. The solution for me was to copy the SOS.dll from a .net 4.0 machine (from C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319) into the winext folder of my WinDbg installation (C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\winext) and then just load it with

.load sos

Yeah, this whole in-place upgrade of .net 4.0 to 4.5 was truly a great idea…