The Art of Guessing


Often when debugging, we’re presented with a problem scenario where our application specific knowledge is limited.  This could mean we don’t have access to the source code or even that we do have access to the source code but simply don’t have time to read millions of lines of code to completely grasp the situation at hand.

In this type of scenario, it’s generally not immediately clear what steps we should take for a resolution.  This means we often have to guess.  Guessing is somewhat of a misnomer in this situation.  I’m not advocating random guessing by any means, but rather taking a logical approach to guessing.  When selecting an action based on guess work, you should be able to explain each step along the way.  This means we need a way to guess well.

 The key to guessing well is leveraging what we do know about a system to in an attempt to make some logical assumptions to fill in the gaps of information that we don’t know.   These are still guesses, but they’re logical guesses based on reason and sound thinking.

Below is a simple case study of a guess the number game written in Silverlight.  The goal of the case study is not to demonstrate how to hack a guess the number game, but rather to illustrate the reasoning behind the guesswork needed to beat the game without relying on…well, guessing. 

The game works by prompting the user to guess a number and click a button.  If the user guesses incorrectly, they see the following screen:

Our goal is to figure out what number to enter in order to guess correctly.  To start with, we want to aggregate what we know about the application:

  1. The user enters a number into a text box and clicks a button.
  2. The number is likely evaluated against something in the application and if they match, we expect to get a message indicating success.  Currently we are trying randomly and only getting failures.

Next we have to figure out what we want to know.

  1. What happens if the user enters the correct value?
  2. How was the application created and where is it hosted?
  3. How can we figure out what the correct value is?

With these questions, the logical first step is to talk to one of the developers that support the project. We’ll assume we got the following back:

  1. What happens if the user enters the correct value? The documentation says the label at the bottom should read, “You chose wisely.”
  2. How was the application created and where is it hosted? All I know is that it’s a Silverlight application that is viewed through the browser.
  3. How can we figure out what the correct value is?  I have no idea.  The code is all on Tommy’s computer and Tommy is on vacation in Jamaica.

Ok, we have a couple of good answers now.  We’ve expanded what we know about the application.  Now it’s time to take some action.  Since the developer told us this is a Silverlight application hosted in a browser we also know that to debug this we can connect a debugger to the browser.

So, we hook up to the browser, and now we have to decide what to look for.   We don’t know much about how the application works.  We don’t know where the application stores the correct value.  We don’t know enough about the application to know what method does the comparison of values.  We don’t even know if the comparison code is hosted locally or if a call is made to a server.

That’s a lot of things we don’t know, so let’s go back to what we do know.  We know that to kick off the comparison operation the user has to click a button.  We also know this is a Silverlight application.  We can guess that the button will be a Silverlight button.

This means we have a first step.  We can use the debugger to search for the button in the managed heap.

First step is to load SOS for Silverlight:

0:038> .loadby sos coreclr

With SOS loaded, we can look on the managed heap for a button object.  We might not know going into to it exactly what type is needed to create a Silverlight button, but we can guess that it probably contains the word “Button.”

0:038> !dumpheap -stat -type Button

Statistics:

      MT    Count    TotalSize Class Name

50878ec8        1           32 MS.Internal.FXCallbackDelegates+PasswordBox_AttachRevealButtonListenerDelegate

50878c1c        1           32 MS.Internal.FXCallbackDelegates+TextBox_AttachDeleteButtonClickHandlerDelegate

508ad1c0        1          128 System.Windows.Controls.Button

Total 3 objects

 

System.Windows.Controls.Button looks promising.  There is only 1 of them and it ‘feels’ right.  Next step is to find the address for that type:

0:038> !dumpheap -mt 508ad1c0       

 Address       MT     Size

0c862790 508ad1c0      128

Now we have an address, so clearly the next step is to dump the object:

0:038> !do 0c862790

Name:        System.Windows.Controls.Button

MethodTable: 508ad1c0

EEClass:     504f3fd4

Size:        128(0x80) bytes

File:        C:\Program Files (x86)\Microsoft Silverlight\5.0.61118.0\System.Windows.dll

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

50898494  4000499        4 ...eObjectSafeHandle  0 instance 0c86281c m_nativePtr

508986f4  400049a        8 ... System.Windows]]  0 instance 0c862ad4 _valueTable

50e36b40  400049b       24       System.Boolean  1 instance        0 _propagateInheritanceChanged

50894fa4  400049c        c ....DependencyObject  0 instance 00000000 _inheritanceParent

508989fc  400049d       10 ...reTypeEventHelper  0 instance 0c862810 _coreTypeEventHelper

508777b8  400049e       14 ...angedEventHandler  0 instance 00000000 DPChanged

50e139ec  400049f       18  System.EventHandler  0 instance 00000000 _inheritaneContextChanged

50875ab4  40004a1       1c ...bject, mscorlib]]  0 instance 0c8628a8 _treeChildren

508735b0  40004a2       20 ...rnal.IManagedPeer  0 instance 0c836118 _treeParent

50e42e50  40004a0       10 ...flection.Assembly  0   shared   static _executingAssembly

… 

50e36b40  4001120       64       System.Boolean  1 instance        1 _isLoaded

50e36b40  4001121       65       System.Boolean  1 instance        0 _isMouseCaptured

50e36b40  4001122       66       System.Boolean  1 instance        0 _isSpaceOrEnterKeyDown

50e36b40  4001123       67       System.Boolean  1 instance        0 _isMouseLeftButtonDown

508aa2a4  4001124       6c System.Windows.Point  1 instance 0c8627fc _mousePosition

5089acf4  4001125       58 ...mation.Storyboard  0 instance 00000000 _currentState

50e36b40  4001126       68       System.Boolean  1 instance        0 _suspendStateChanges

50e36b40  4001127       69       System.Boolean  1 instance        0 _isSuspendingIsEnabled

5089eb88  4001128       5c ...tArgs, mscorlib]]  0 instance 00000000 _weakCanExecuteChangedListener

50873a68  4001129       60 ...outedEventHandler  0 instance 0c862fb0 Click

508987dc  400111a      e80 ...ependencyProperty  0   shared   static ClickModeProperty

    >> Domain:Value  0b0b0cd0:NotInit  0b0807c0:0c84dc6c <<

508987dc  400111b      e84 ...ependencyProperty  0   shared   static IsFocusedProperty

    >> Domain:Value  0b0b0cd0:NotInit  0b0807c0:0c84dd4c <<

508987dc  400111c      e88 ...ependencyProperty  0   shared   static IsMouseOverProperty

    >> Domain:Value  0b0b0cd0:NotInit  0b0807c0:0c84dd88 <<

508987dc  400111d      e8c ...ependencyProperty  0   shared   static IsPressedProperty

    >> Domain:Value  0b0b0cd0:NotInit  0b0807c0:0c84ddf4 <<

508987dc  400111e      e90 ...ependencyProperty  0   shared   static CommandProperty

    >> Domain:Value  0b0b0cd0:NotInit  0b0807c0:0c84df18 <<

508987dc  400111f      e94 ...ependencyProperty  0   shared   static CommandParameterProperty

    >> Domain:Value  0b0b0cd0:NotInit  0b0807c0:0c84df78 <<

5088fe30  400112a      e98 ...tArgs, mscorlib]]  0   shared   static CS$<>9__CachedAnonymousMethodDelegate5

    >> Domain:Value  0b0b0cd0:NotInit  0b0807c0:00000000 <<

 

Some of the above output was trimmed for brevity.  We have some more information.  We might not have the internals of the System.Windows.Controls.Button memorized, but we do know enough to know that there is very likely a click event.  We can guess that event will have the word click in it.  Looking in the output above, we find this line:

50873a68  4001129       60 ...outedEventHandler  0 instance 0c862fb0 Click

 

Dumping this object, we find:

0:038> !do 0c862fb0

Name:        System.Windows.RoutedEventHandler

MethodTable: 50873a68

EEClass:     504ddb60

Size:        32(0x20) bytes

File:        C:\Program Files (x86)\Microsoft Silverlight\5.0.61118.0\System.Windows.dll

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

50e36684  400002d        4        System.Object  0 instance 0c8163e8 _target

50e36684  400002e        8        System.Object  0 instance 00000000 _methodBase

50e3dfd8  400002f        c        System.IntPtr  1 instance  803c430 _methodPtr

50e3dfd8  4000030       10        System.IntPtr  1 instance        0 _methodPtrAux

50e36684  4000031       14        System.Object  0 instance 00000000 _invocationList

50e3dfd8  4000032       18        System.IntPtr  1 instance        0 _invocationCount

 

Again, we have an object whose internals may not be familiar to us, so we have to guess.   _target and _methodPtr both seem like good guesses.  We’ll start by dumping _target:

0:038> !do 0c8163e8

Name:        GuessTheNumber.MainPage

MethodTable: 080356b8

EEClass:     0835374c

Size:        120(0x78) bytes

File:        GuessTheNumber, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

50898494  4000499        4 ...eObjectSafeHandle  0 instance 0c81646c m_nativePtr

508986f4  400049a        8 ... System.Windows]]  0 instance 00000000 _valueTable

50e36b40  400049b       24       System.Boolean  1 instance        0 _propagateInheritanceChanged

50894fa4  400049c        c ....DependencyObject  0 instance 00000000 _inheritanceParent

508989fc  400049d       10 ...reTypeEventHelper  0 instance 0c816460 _coreTypeEventHelper

508777b8  400049e       14 ...angedEventHandler  0 instance 00000000 DPChanged

50e139ec  400049f       18  System.EventHandler  0 instance 00000000 _inheritaneContextChanged

50875ab4  40004a1       1c ...bject, mscorlib]]  0 instance 0c8637c8 _treeChildren

508735b0  40004a2       20 ...rnal.IManagedPeer  0 instance 00000000 _treeParent

50e42e50  40004a0       10 ...flection.Assembly  0   shared   static _executingAssembly

08035848  4000002       54 ...er.MyRandomObject  0 instance 0c8164a8 randomObject

5089f2f4  4000003       58 ...ows.Controls.Grid  0 instance 0c836118 LayoutRoot

08035b18  4000004       5c ...ws.Controls.Label  0 instance 0c81dd78 label1

08035b18  4000005       60 ...ws.Controls.Label  0 instance 0c836730 label2

50897f28  4000006       64 ....Controls.TextBox  0 instance 0c83779c guessTextBox

508ad1c0  4000007       68 ...s.Controls.Button  0 instance 0c862790 submitButton

08035b18  4000008       6c ...ws.Controls.Label  0 instance 0c863124 resultLabel

50e36b40  4000009       70       System.Boolean  1 instance        1 _contentLoaded

 

The abbreviated content above looks promising.  The controls listed as members of the object look promising.  We still don’t have a click event though.  We’re left guessing one more time.  This time, we’ll guess that the click method is associated with the MethodTable for GuessTheNumber.MainPage. Based on this guess, we’ll dump the method descriptors for the MethodTable:

0:038> !dumpmt -md 080356b8

EEClass:         0835374c

Module:          080346e4

Name:            GuessTheNumber.MainPage

mdToken:         02000003

File:            GuessTheNumber, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

BaseSize:        0x78

ComponentSize:   0x0

Slots in VTable: 63

Number of IFaces in IFaceMap: 4

--------------------------------------

MethodDesc Table

   Entry MethodDe    JIT Name

50c32330 50a1b150 PreJIT System.Object.ToString()

50c32350 50a1b158 PreJIT System.Object.Equals(System.Object)

50c323c0 50a1b178 PreJIT System.Object.GetHashCode()

50c323d0 50a1b190 PreJIT System.Object.Finalize()

5074cd34 50508d44 PreJIT

50785800 50514cbc PreJIT System.Windows.Controls.Control.OnManipulationCompleted(System.Windows.Input.ManipulationCompletedEventArgs)

50785828 50514cc4 PreJIT System.Windows.Controls.Control.OnTap(System.Windows.Input.GestureEventArgs)

50785850 50514ccc PreJIT System.Windows.Controls.Control.OnDoubleTap(System.Windows.Input.GestureEventArgs)

50785878 50514cd4 PreJIT System.Windows.Controls.Control.OnHold(System.Windows.Input.GestureEventArgs)

08370240 0803566c    JIT GuessTheNumber.MainPage..ctor()

0803c071 08035674   NONE GuessTheNumber.MainPage.submitButton_Click(System.Object, System.Windows.RoutedEventArgs)

083703d0 08035680    JIT GuessTheNumber.MainPage.InitializeComponent() 

Looks like we’re on to something.  The submitButton_Click event looks promising.  We can guess that is the method we’ve been looking for and dump out the IL:

0:038> !dumpil 08035674  

ilAddr = 083302c8

IL_0000: nop

IL_0001: ldarg.0

IL_0002: ldfld GuessTheNumber.MainPage::guessTextBox

IL_0007: callvirt System.Windows.Controls.TextBox::get_Text

IL_000c: ldloca.s VAR OR ARG 0

IL_000e: call System.Int32::TryParse

IL_0013: pop

IL_0014: ldloc.0

IL_0015: ldarg.0

IL_0016: ldfld GuessTheNumber.MainPage::randomObject

IL_001b: callvirt GuessTheNumber.MyRandomObject::get_randomValue

IL_0020: ceq

IL_0022: ldc.i4.0

IL_0023: ceq

IL_0025: stloc.1

IL_0026: ldloc.1

IL_0027: brtrue.s IL_003c

IL_0029: ldarg.0

IL_002a: ldfld GuessTheNumber.MainPage::resultLabel

IL_002f: ldstr "You chose wisely."

IL_0034: callvirt System.Windows.Controls.ContentControl::set_Content

IL_0039: nop

IL_003a: br.s IL_0058

IL_003c: ldarg.0

IL_003d: ldfld GuessTheNumber.MainPage::resultLabel

IL_0042: ldstr "Your guess of {0} was incorrect."

IL_0047: ldloc.0

IL_0048: box System.Int32

IL_004d: call System.String::Format

IL_0052: callvirt System.Windows.Controls.ContentControl::set_Content

IL_0057: nop

IL_0058: ret

IL isn’t always easy to follow without practice, but the above makes it clear we are in the right place since we can see both, “You chose wisely,” and “Your guess of {0} was incorrect.” To make the code easier to read, we can extract the module and decompile the IL into C#

0:038> !dumpmodule 080346e4

Name:       GuessTheNumber, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Attributes: PEFile

Assembly:   0b0c2310

LoaderHeap:              00000000

TypeDefToMethodTableMap: 08030124

TypeRefToMethodTableMap: 0803013c

MethodDefToDescMap:      0803020c

FieldDefToDescMap:       08030248

MemberRefToDescMap:      0803027c

FileReferencesMap:       0803035c

AssemblyReferencesMap:   08030360

MetaData start address:  08330d00 (4512 bytes)

0:038> lmm GuessTheNumber

start    end        module name

08330000 08332600   GuessTheNumber C (no symbols)          

0:038> !savemodule 08330000 d:\GuessTheNumberModule.dll

3 sections in file

section 0 - VA=2000, VASize=1da4, FileAddr=200, FileSize=1e00

section 1 - VA=4000, VASize=370, FileAddr=2000, FileSize=400

section 2 - VA=6000, VASize=c, FileAddr=2400, FileSize=200

 

Using a decompiler such as Redgate’s reflector, we see the following C#:

private void submitButton_Click(object sender, RoutedEventArgs e)

{

    int num;

    int.TryParse(this.guessTextBox.get_Text(), out num);

    if (num == this.randomObject.randomValue)

    {

        this.resultLabel.set_Content("You chose wisely.");

    }

    else

    {

        this.resultLabel.set_Content(string.Format("Your guess of {0} was incorrect.", num));

    }

}

Now it’s clear what’s going on here.  The value entered by the user into the text box is parsed into an int then compared to another int stored in this.randomObject.randomValue.  All that’s left for us to do is to find the current value for randomValue.  We know that in this case, this refers to a MainPage object.  Interestingly, that’s the exact type we dumped previously as the _target from the event handler that we found.  Reviewing our previous output, we find:

08035848  4000002       54 ...er.MyRandomObject  0 instance 0c8164a8 randomObject

Dumping the object we see:

0:038> !do 0c8164a8

Name:        GuessTheNumber.MyRandomObject

MethodTable: 08035848

EEClass:     08353800

Size:        12(0xc) bytes

File:        GuessTheNumber, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Fields:

      MT    Field   Offset                 Type VT     Attr    Value Name

50e371d4  4000001        4         System.Int32  1 instance      174 <randomValue>k__BackingField

We see a member variable named randomValue which is exactly what we were looking for.  Using the value current value of 174, we’ll try another guess in the application:

 

Good guess.

To recap, we were tasked with troubleshooting an application that we knew very little about.  We asked a few questions, used a bit of prior knowledge, and a handful of reason driven guesses to get us to a solution.

Although it’s unlikely you will ever need to cheat at a Guess the Number game in a production environment, the approach here is applicable to any situation in which you are approaching with limited domain specific knowledge.

Guess wisely my friends.

 

-Jarrod

 

Comments (2)

  1. nathan says:

    Why did u just load it up in ilspy? 😉

  2. jmattheus says:

    @Nathan: Because ILSpy is not always an immediate option.  Sometimes we're given just a memory dump to work with and would have to, at the very least, extract the related modules before ILSpy would help us out.

    It's also not always practical within time constraints to do a code review to identify the root cause of an issue.  In this case, I'm sure any developer could have read through all 60 lines of code in no time flat, but if your production application only has 60 lines of code, you probably won't have many problems with it anyway.

    As for why ILSpy over Reflector?  No real reason there.  I just picked one. Reflector is great.  ILSpy is great.  JetBrains's dotPeek is great.  Telerik's JustDecompile is great.  They're all great.  I'm seeing a compare and contrast of decompilers article in my future.

Skip to main content