dumpstring – windbg


Viewing strings inside the debugger has never been pretty, especially if you are using sos extension.  Here is a sample !dumpobj on a string

0:000> !do 00000000025f2280
Name:        System.String
MethodTable: 000007fef6e26960
EEClass:     000007fef69aeec8
Size:        32(0×20) bytes
String:     Foo
Fields:
MT    Field   Offset                 Type VT     Attr            Value Name
000007fef6e2c848  40000ed        8         System.Int32  1 instance                3 m_stringLength
000007fef6e2b388  40000ee        c          System.Char  1 instance               46 m_firstChar
000007fef6e26960  40000ef       10        System.String  0   shared           static Empty
>> Domain:Value  00000000002ae900:00000000025e1420 <<

Some of the devs like to use the du command

du 00000000025f2280+c

0:000> du 00000000025f2280+c

00000000`025f228c  “Foo”

My choice is to use the .printf command and here is my alias for printing string

as !ds .printf "%mu \n", c+

0:000> !ds 00000000025f2280

Foo

I prefer .printf over du because I am not interested in looking at the memory address often especially dumping strings within a script.

Custom DumpArray – Windbg


The sos has !dumparray for getting contents of the array. But it cannot be used for scripting or automation. Here is an example

using System;
namespace ConsoleApplication
{
 class Program
{
 Test[] arr = new[] { new Test() { ID = 1, Name = "Foo" }, new Test() { ID = 2, Name = "Bar" } };
 static void Main(string[] args)
 {
 var p = new Program();
 Console.WriteLine(p.arr);
 Console.Read();
 }
 }
 class Test
 {
 public int ID;
 public string Name;
 }
}

And here is the output of the arr variable within the debugger

0:000> !da -details 021fbc6c
Name:        ConsoleApplication.Test[]
MethodTable: 64b56c28
EEClass:     648d9698
Size:        24(0×18) bytes
Array:       Rank 1, Number of elements 2, Type CLASS
Element Methodtable: 001938b0
[0] 021fbc84
Name:        ConsoleApplication.Test
MethodTable: 001938b0
EEClass:     00191488
Size:        16(0×10) bytes
File:        C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\bin\Debug\ConsoleApplication.exe
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
64ba2978  4000002        8             System.Int32      1     instance            1     ID
64b9f9ac  4000003        4            System.String      0     instance     021fbc44     Name
[1] 021fbc94
Name:        ConsoleApplication.Test
MethodTable: 001938b0
EEClass:     00191488
Size:        16(0×10) bytes
File:        C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication9\bin\Debug\ConsoleApplication.exe
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
64ba2978  4000002        8             System.Int32      1     instance            2     ID
64b9f9ac  4000003        4            System.String      0     instance     021fbc58     Name

From the output  we cannot see the values of the  Name variable within the Test class. To get the value we would have manually issue a !dumpobj on each one of these. In this post I will demonstrate how to automate this. In doing so we will also explore the array internals from raw memory perspective.

To start of lets dump the raw memory of the array. The  array address is 021fbc6c

dd 021fbc6c

0:000> dd 021fbc6c
021fbc6c  64b56c28 00000002 001938b0 021fbc84
021fbc7c  021fbc94 00000000 001938b0 021fbc44
021fbc8c  00000001 00000000 001938b0 021fbc58
021fbc9c  00000002 00000000 64ba7490 00000000
021fbcac  00000000 00000000 00000000 00000000
021fbcbc  00000000 64b9f5e8 00000000 40010000
021fbccc  64ba6034 00000007 00000004 00000100
021fbcdc  00000000 64ba6f40 00000000 00000000

Fields

  1. 64b56c28 - Array’s Method table pointer
  2. 00000002 - Array’s length ( this will be used later)
  3. 001938b0 -Array contents method table pointer ( Test class)
  4. 021fbc84, 021fbc94 - Contents of the array( 2 instances of the Test class)

I would be using  $t0, $t1   User-Defined Pseudo-Registers within my script  as local variables to maintain state. Think of them as predefined variables that we can use. Here is the script to get just the Name from the array

.for (r $t0=0; @$t0 < poi(021fbc6c+0x4); r$t0=@$t0+1 ) { r$t1 = 0; .if(@$t0 = 0) { r$t1=10} .else { r$t1= 10+ @$t0*4};.echo ************;!do poi(poi((021fbc6c-0x4)+@$t1)+0x4) }

Here is the explanation for the above script

  1. The .for loop is used to iterate through the contents of the array :  “.for (r $t0=0; @$t0 < poi(021fbc6c+0×4); r$t0=@$t0+1 ) ”
    • The loop variable is $t0, which is initialized to zero  “r $t0=0;”,
    • Next is the loop condition check @$to < poi(021fbc6c+0×4) , the poi(021fbc6c+0×4) is the pointer deference to array length which is 00000002
    • And the last statement is the increment command of the loop variable r$t0=@$t0+1
    • Tip :- I am using “@” before the “$” for increased speed within the debugger when accessing registers.
  2. The next statement is “r$t1 = 0″ is initializing another pseudo register to zero
  3. After which the command “if(@$t0 = 0) { r$t1=10} .else { r$t1= 10+ @$t0*4}” resets the value of $t1 register either “10″ or $t0 * 4, where $to is loop variable. I do this because the first instance of the Test class within the array is in the 10th offset and the rest of them would be on the next 4th offset. So for example the first time loop ,$t1 would be 10 , the second time  $t1 would 14 (10 + 1*4).
  4. The “.echo ************” is just for line separation
  5. The last command is the one which does most of the work
    • The command poi((021fbc6c-0×4)+@$t1) would return the pointer of the each element in the array which is instance of Test class . The first time it would be poi((021fbc6c-0×4)+10) which would point 021fbc84 and the next time it would be poi((021fbc6c-0×4)+14) which would be 021fbc94
    • The outermost “poi 0×4” is to get pointer of the member variable Name and dump its content using !do

And here is the output from the script

    0:000> .for (r $t0=0; @$t0 < poi(021fbc6c   +0×4); r$t0=@$t0+1 ) { r$t1 = 0; .if(@$t0 = 0) { r$t1=10} .else { r$t1= 10+ @$t0*4};.echo ************;  !do poi(poi((021fbc6c-0×4)+@$t1)+0×4) }
    ************
    Name:        System.String
    MethodTable: 64b9f9ac
    EEClass:     648d8bb0
    Size:        20(0×14) bytes
    File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
    String:      Foo
    Fields:
    MT    Field   Offset                 Type VT     Attr    Value Name
    64ba2978  40000ed        4         System.Int32  1 instance        3 m_stringLength
    64ba1dc8  40000ee        8          System.Char  1 instance       46 m_firstChar
    64b9f9ac  40000ef        8        System.String  0   shared   static Empty
    >> Domain:Value  00745c28:021f1228 <<
    ************
    Name:        System.String
    MethodTable: 64b9f9ac
    EEClass:     648d8bb0
    Size:        20(0×14) bytes
    File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
    String:      Bar
    Fields:
    MT    Field   Offset                 Type VT     Attr    Value Name
    64ba2978  40000ed        4         System.Int32  1 instance        3 m_stringLength
    64ba1dc8  40000ee        8          System.Char  1 instance       42 m_firstChar
    64b9f9ac  40000ef        8        System.String  0   shared   static Empty
    >> Domain:Value  00745c28:021f1228 <<

Do I have Managed or Native memory leak?


I noticed someone who couldn’t figure out the cause of memory leak in  managed application within the debugger. This person had basic debugging skills and was comfortable with sos.  FYI the leak wasn’t in the managed code, but in the native code. The managed code was using native code via PInvoke.

Here is how I figured out the cause. Every time I have to debug a memory leak in managed code ,the first command I run is !vmstat. The !vmstat is available in psscor2.dll for .net 3.5 and for .net 4.0 it is available in sos. This command provides summary of VM. The output is similar to the VMMap tool and here is the output from the command.

Now that I know there is a memory leak, I always start of with running these commands


.shell -ci "!EEHeap -loader" findstr  "LoaderHeap"

.shell -ci "!EEHeap -gc" findstr /B "Total Size"

!heapstat  - iu

And here is the output from the above commands

0:004> .shell -ci “!EEHeap -loader” findstr  “LoaderHeap”
Total LoaderHeap size: 0×10000(65,536)bytes
.shell: Process exited
0:004> .shell -ci “!EEHeap -gc” findstr /B “Total Size”
Total Size   0x3df74(253,812)
.shell: Process exited
0:004> !heapstat -iu
Heap     Gen0         Gen1         Gen2         LOH
Heap0    8204         182020       54820        8768

Free space:                                                 Percentage
Heap0    12           149212       36           48          SOH: 60% LOH:  0%

Unrooted objects:                                           Percentage
Heap0    1188         0            0            0           SOH:  0% LOH:  0%
0:004>

And from the output I know there isn’t a managed memory leak. The total heap size is only 253,812 bytes and my loader heap is 65,536 bytes, so it has to be native code.  Now I can focus my efforts on the native code debugging.

Customizing Witty Twitter Client Part 1- using C# as Compiler Service


This is going to be a multipart blog post where I am going to be demonstrating how I have customized Witty twitter client for my need. I chose Witty because it is the only OSS .NET twitter client I know of.

One of the reasons for customizing is primarily using C# as compiler service to extend it for my needs dynamically. For example I follow this twitter list http://twitter.com/shanselman/programmers , it’s a cool list that Scott maintains, thanks to him. But there is one person in this list who keeps tweeting about weight loss, which I am least interested and I didn’t have control over it but to ignore, until now. And it is always fun to write  software for your daily needs, which for me saves time.  Thanks to Mono for C# as compiler service. FYI I have shown a simple usage of Mono Csharp compiler service in this post.

Here are things that I could get done by just spending few hours of my weekend time

Here is my the default witty

Filter the list by user name  which I am not interested in


new Func<Tweet, bool>(t => !t.User.Name.Contains("CNN"));

Here is the same without CNN

FYI the filter C# code is in the Filter Text box

The next filter criteria is to look for tweets that have hashtag as fsharp and also at least have one link


new Func<Tweet, bool>(t => t.HashTags.DefaultIfEmpty().Contains("#fsharp") && t.Urls.DefaultIfEmpty().Count() > 0);

And here is the filtered list

Be even crazier look for tweets that have hashtag as fsharp and at least one link and then open the link automatically

new Func<Tweet, bool>(t => {
 if (t.HashTags.DefaultIfEmpty().Contains("#fsharp") && t.Urls.DefaultIfEmpty().Count() > 0)
 System.Diagnostics.Process.Start(t.Urls.First().ToString());
 return t.HashTags.DefaultIfEmpty().Contains("#fsharp") && t.Urls.DefaultIfEmpty().Count() > 0; });

The browser with the link opened automatically.

It’s a hack and I shouldn’t be doing the above, but it does the job.

There is so much more possibilities. I could easily provide a save feature for these search scripts and reuse the same.  At the end of the series I will post the entire code in GitHub. But if you want to try it before that here are the simple changes I started doing to the code.

Extension Method for Compilation


static class Extensions
 {
 public static object Compile(this string code)
 {
 return Mono.CSharp.Evaluator.Evaluate(code);
 }
 public static void Run(this string code)
 {
 Mono.CSharp.Evaluator.Run(code);
 }
 }

The filter code


public bool TweetFilter(object item)
 {
 Tweet tweet = item as Tweet;

 // this will prevent the fade animation from starting when the tweet is filtered
 tweet.IsNew = false;
 try
 {
 Func<Tweet, bool> compare = (Func<Tweet, bool>)FilterTextBox.Text.Compile();
 return compare.Invoke(tweet);
 }
 catch(Exception ex)
 {
 Console.WriteLine(ex);

 }
 return true;
 }

The hashtags and Links weren’t part of the tweet class , but they were part of the UI class. So I had to bring them to the tweet class and populate them.

public IEnumerable<Uri> Urls
 {
 get
 {
 return links;
 }
 set
 {
 links.Clear();
 links.AddRange(value);
 }
 }
 public IEnumerable<string> HashTags
 {
 get
 {
 return hashTags;
 }
 set
 {
 hashTags.Clear();
 hashTags.AddRange(value);
 }
 }

And here is the code to populate the above properties

private IEnumerable<Uri> GetLinks(string text)
 {
 string[] words = Regex.Split(text, @"([ \(\)\{\}\[\]])");
 return (from word in words
 let isUrl = new Func<string, bool>(s => UrlShorteningService.IsUrl(s))
 where isUrl(word)
 select new Uri(word) ).ToList();
 }
 private IEnumerable<string> GetHashTags(string text)
 {
 string[] words = Regex.Split(text, @"([ \(\)\{\}\[\]])");
 return (from word in words
 let ishash = new Func<string, bool>(s => s.StartsWith("#"))
 where ishash(word)
 select word).ToList();
 }

I know for the above 2 functions I could have written a High-Order Function

Using Tuple as Dictionary / Map key


I  recently had to create a dictionary which needed a multipart key like <string,int> . To do this I would have to create a custom class  override equals and gethashcode. That’s when someone told I could use Tuple, but weren’t sure it was possible.  Here was the quick sample to try it in F#

let x = [("naveen",1),1;("naveen",1),2] |> Map.ofList

as expected only one item in the Map

val x : Map<(string * int),int> = map [(("naveen", 1), 2)]

And the same in C#

 Console.WriteLine(Tuple.Create("Naveen",1).Equals( Tuple.Create("Naveen",1)));

The next step was to actually disassemble Tuple in reflector

The code implements IStructuralComparable.CompareTo and does the comparison for each item. This is one of the reasons why generics is cool.

Using Tech-Ed OData to download videos


I wanted to watch the Teched 2010 videos, but the problem I had was going to the site manually to download files for offline viewing.  And I was also interested only in Dev sessions which were level 300 / 400. Thanks to OData for teched http://odata.msteched.com/sessions.svc/ ,I  could write 3 statements in linqpad and had them all downloaded using wget

File.Delete(@"C:\temp\download.txt");

Sessions
.Where (s => (s.Level.StartsWith("400") ||  s.Level.StartsWith("300") ) && s.Code.StartsWith("DEV"))
.Take(10)
.ToList()
.Select (s => @"http://ecn.channel9.msdn.com/o9/te/NorthAmerica/2010/mp4/" + s.Code + ".mp4" )
.Run(s => File.AppendAllText(@"C:\temp\download.txt",s + Environment.NewLine));

Util.Cmd(@"wget.exe -b -i c:\Temp\download.txt",true);

Forgot to mention for the Run extension method is from Reactive Extensions

Debugging base class method with conditional break point in .NET using Windbg


In this post I am going to be demonstrating how to have a conditional break-point on the base class method where it has been used by multiple derived classes. A classic example is Winform UI.

The Control base class has got methods like set_Enabled , set_Visible which could be consumed by multiple derived controls. The goal is to debug only the control instance that we are interested in. Here is the sample code.


using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
 public Form1()
 {
 InitializeComponent();
 button3.Click += (s, b) => button1.Enabled = button1.Enabled ? false : true;
 button4.Click += (s, b) => button2.Enabled = button2.Enabled ? false : true;
 }
 }
}

image

This app that has four buttons. On the click of button3 it toggles button1’s enabled property and the button 4 does the same for button2. The goal is to break only when button1′s enabled property is set.

Launched the app and attached it to windbg and loaded sosex, issued the command

!mbm *Control.set_Enabled

to set a break-point on enabled method. FYI set_Enabled is not available in the button class because it is derived from the base class. The goal is to break only when the button1’s enabled property is changed. With the above command it will break every time and here is the output when the break-point hits

rax=000007fede335c40 rbx=0000000000060b28 rcx=000000000242ed18
rdx=0000000000000000 rsi=0000000000000000 rdi=000000000242ed18
rip=000007fede335c4d rsp=000000000015dde0 rbp=000000000015df80
r8=0000000002464500  r9=0000000000000000 r10=000007fffff10018
r11=000000000015dd20 r12=00000000002f5980 r13=000000000015dea8
r14=000000000015e380 r15=0000000000100000
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
System_Windows_Forms_ni+0x2b5c4d:
000007fe`de335c4d 488bcf          mov     rcx,rdi

The rcx register contains the reference of the instance of the class ,  example button1 / button2. And here is the command to get the offset of the button text

.shell -ci "!do @rcx" findstr /E text

and the output of the command is

0:000> .shell -ci”!do @ecx” findstr /E text
000007fef6db6960  40001c2       40        System.String  0 instance 000000000242ec50 text
000007fef6db5ab8  400016d      280        System.Object  0   static 0000000002403d80 EventBindingContext
.shell: Process exited

The reason behind getting the button text offset is to use the text as the property for conditional break-point. The text member is available in the 40 offset of the button class. Now that we have the offset of the text property lets try and reset the break-point with a condition. To do this  get the address of the break-point using the bl command and the output is

0:000> bl
0 e 000007fe`de335c4d     0001 (0001)  0:**** System_Windows_Forms_ni+0x2b5c4d

The address of the set_Enabled function is 000007fe`de335c4d.   Here is the conditional break-point for button1

bp 000007fe`de335c4d "as /mu ${/v:name} (poi(@rcx+40)+c);.block{ .if (0== $scmp( \"${name}\", \"button1\") ) { .echo 'in button1';gc } .else { gc}}"

The “as /mu ${/v:name} (poi(@rcx+40)+c)” will set the value of text property  to the variable “name”. The .block command is used to evaluate the variable  “name” and the rest is a simple .if .else command. Every time the button1 is clicked it would output  “in button1” and continue. Now we have managed to break-in only on the button1 click.

Piracy in .NET Code – Part 3 – Even when the code is obfuscated


Continuing with my series on Piracy, in this post I am going to be  exploring how someone with little advanced knowledge in CLR / Windows can bypass important function calls like  license validation.

Most of the developers assume just because the code is obfuscated nobody can bypass the licensing logic. I am going to be demonstrating how to bypass certain function call,this is very similar to “Set Next Statement ” in VS. I am not going to be discussing on how to fix this problem.

Here is a sample code.

using System;
namespace Conosole
{   class Program
 {
 static void Main(string[] args)
 {
 Console.WriteLine("Test");
 Console.Read();
 }
 }
 }

The code has only two instructions. First one writes to console and the next to reads from the console. I would want to bypass the call to that WriteLine function.

Loaded the assembly within windbg and then issued the command

sxe ld: clrjit

When the break-point hits ,issued the command to set a break-point on the Main Method

.loadby sos clr;.load sosex;!mbm *Program.Main;g

Then when the break-point hits for the Main method , issued the following command to disassemble the Main method.

!u ($ip)

0:000> !u ($ip)
Normal JIT generated code
Conosole.Program.Main(System.String[])
Begin 00230070, size 2d

C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication6\Program.cs @ 6:
00230070 55              push    ebp
00230071 8bec            mov     ebp,esp
00230073 50              push    eax
00230074 894dfc          mov     dword ptr [ebp-4],ecx
00230077 833d3c31180000  cmp     dword ptr ds:[18313Ch],0
0023007e 7405            je      00230085
00230080 e8ca5a6962      call    clr!JIT_DbgIsJustMyCode (628c5b4f)
>>> 00230085 90              nop

C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication6\Program.cs @ 7:
00230086 8b0d30204a03    mov     ecx,dword ptr ds:[34A2030h] (“Test”)
0023008c e81b707a61      call    mscorlib_ni+0x2570ac (619d70ac) (System.Console.WriteLine(System.String), mdToken: 06000919)
00230091 90              nop

C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication6\Program.cs @ 8:
00230092 e8cdc1d761      call    mscorlib_ni+0x82c264 (61fac264) (System.Console.Read(), mdToken: 0600090a)
00230097 90              nop

C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication6\Program.cs @ 9:
00230098 90              nop
00230099 8be5            mov     esp,ebp
0023009b 5d              pop     ebp
0023009c c3              ret
0:000> bp 0023008c
0:000> g

Because I have private symbols the line information is shown. So the function I want to bypass is “0023008c e81b707a61      call    mscorlib_ni+0x2570ac (619d70ac) (System.Console.WriteLine(System.String), mdToken: 06000919)” and the ip for this is 0023008c , so went ahead and set a break-point on the address

bp 0023008c

When the break-point hits on 0023008c, I move pointer to the next instruction that I am interested in ,which is “00230092 e8cdc1d761      call    mscorlib_ni+0x82c264 (61fac264) (System.Console.Read(), mdToken: 0600090a)” to avoid the function being invoked and here is the command

r eip=00230092

0:000> g
Breakpoint 1 hit
eax=001837f0 ebx=00000000 ecx=024abb50 edx=0041efd0 esi=008196c0 edi=0041ef20
eip=0023008c esp=0041eef0 ebp=0041eef4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
0023008c e81b707a61      call    mscorlib_ni+0x2570ac (619d70ac)
0:000> r eip=00230092

Now we have managed to bypass the call to Console.WriteLine and here is my output .

So the key takeaway is to understand the working  of the platform closer to the metal, which can help us write better and secure code.

Using Windows Error Reporting (WER) API in managed code to generate memory dump


The WER is a pretty cool technology from Microsoft for collecting memory dumps on process crash/ hang. This can be extended to generate on demand when the application needs to. The usual reason for getting a memory dump could be based on certain conditions, for example, the customer feels the application is slow and would want to send the information to WinQual (WER server). If the application happens to be installed on hundreds / thousands of boxes then its not going to be possible to get from individual customers, the best bet is WER. To do this here is an API. But this is unmanaged API and I didn’t see one for managed code. FYI this would work only on Vista + systems, it will not work on XP.

Here is the basic PInvoke for creating dump and submitting a report. I am also using it along with the watsonbuckets that I had blogged about.


internal enum WER_CONSENT
 {
 WerConsentAlwaysPrompt = 4,
 WerConsentApproved = 2,
 WerConsentDenied = 3,
 WerConsentMax = 5,
 WerConsentNotAsked = 1
 }
 internal enum WER_DUMP_TYPE
 {
 WerDumpTypeHeapDump = 3,
 WerDumpTypeMax = 4,
 WerDumpTypeMicroDump = 1,
 WerDumpTypeMiniDump = 2
 }
 internal enum WER_REPORT_TYPE
 {
 WerReportNonCritical,
 WerReportCritical,
 WerReportApplicationCrash,
 WerReportApplicationHange,
 WerReportKernel,
 WerReportInvalid
 }

 internal static class Unmanaged
 {
 [DllImport("wer.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 internal static extern int WerReportAddDump(IntPtr hReportHandle,
 IntPtr hProcess, IntPtr hThread, WER_DUMP_TYPE dumpType, IntPtr pExceptionParam, IntPtr pDumpCustomOptions, int dwFlags);
 [DllImport("wer.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 internal static extern int WerReportCreate(string pwzEventType,
 WER_REPORT_TYPE repType, IntPtr pReportInformation, ref IntPtr phReportHandle);
 [DllImport("wer.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 internal static extern int WerReportSetParameter(IntPtr hReportHandle, int dwparamID, string pwzName, string pwzValue);
 [DllImport("wer.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 internal static extern int WerReportSubmit(IntPtr hReportHandle, WER_CONSENT consent, int dwFlags, ref IntPtr pSubmitResult);
 }

I have shown only few functions in the wer api, there are few more.  Here is the total implementation


using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WER
{
 public partial class Form1 : Form
 {
 static bool Failed(int result)
 {
 return (result < 0);
 }
 public Form1()
 {
 InitializeComponent();
 button1.Click += (s, b) =>
 {
 try
 {
 throw new NullReferenceException("Test");
 }
 catch (Exception ex)
 {

 var bucket = GetWatsonBuckets();
 var zero = IntPtr.Zero;

 if ((!Failed(Unmanaged.WerReportCreate("CrashingApp",
 WER_REPORT_TYPE.WerReportCritical, IntPtr.Zero, ref zero)) &&
 (zero != IntPtr.Zero)) && ((((!Failed(Unmanaged.WerReportSetParameter(zero, 0, "AppName", bucket.param0))
 && !Failed(Unmanaged.WerReportSetParameter(zero, 1, "AppVer", bucket.param1))) &&
 (!Failed(Unmanaged.WerReportSetParameter(zero, 2, "AppStamp", bucket.param2)) &&
 !Failed(Unmanaged.WerReportSetParameter(zero, 3, "AsmAndModName", bucket.param3)))) &&
 ((!Failed(Unmanaged.WerReportSetParameter(zero, 4, "AsmVer", bucket.param4)) &&
 !Failed(Unmanaged.WerReportSetParameter(zero, 5, "ModStamp", bucket.param5))) &&
 (!Failed(Unmanaged.WerReportSetParameter(zero, 6, "MethodDef", bucket.param6)) &&
 !Failed(Unmanaged.WerReportSetParameter(zero, 7, "Offset", bucket.param7))))) &&
 !Failed(Unmanaged.WerReportSetParameter(zero, 8, "ExceptionType", bucket.param8))))
 {

 var currentProcess = System.Diagnostics.Process.GetCurrentProcess().Handle;
 if (!Failed(Unmanaged.WerReportAddDump(zero, currentProcess,
 IntPtr.Zero, WER_DUMP_TYPE.WerDumpTypeHeapDump, IntPtr.Zero, IntPtr.Zero, 0)))
 {
 var pSubmitResult = IntPtr.Zero;
 Unmanaged.WerReportSubmit(zero, WER_CONSENT.WerConsentNotAsked, 4, ref pSubmitResult);
 }
 }
 }
 };
 }
 private static WatsonBuckets GetWatsonBuckets()
 {
 var pParams = new WatsonBuckets();
 IClrRuntimeHost host = null;
 host = Activator.CreateInstance(Type.GetTypeFromCLSID(ClrGuids.ClsIdClrRuntimeHost)) as IClrRuntimeHost;
 if (host != null)
 {
 var clrControl = host.GetCLRControl();
 if (clrControl == null)
 {
 return pParams;
 }
 var clrErrorReportingManager =
 clrControl.GetCLRManager(ref ClrGuids.IClrErrorReportingManager) as IClrErrorReportingManager;
 if (clrErrorReportingManager == null)
 {
 return pParams;
 }
 clrErrorReportingManager.GetBucketParametersForCurrentException(out pParams);
 }
 return pParams;
 }
 }
 // BucketParameters Structure to get watson buckets back from CLR
 //http://msdn.microsoft.com/en-us/library/ms404466(v=VS.100).aspx
 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
 internal struct WatsonBuckets
 {
 internal int fInited;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string pszEventTypeName;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param0;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param1;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param2;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param3;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param4;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param5;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param6;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param7;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param8;
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xff)]
 internal string param9;
 }
 internal static class ClrGuids
 {
 internal static readonly Guid ClsIdClrRuntimeHost = new Guid("90F1A06E-7712-4762-86B5-7A5EBA6BDB02");
 internal static Guid IClrErrorReportingManager = new Guid("980D2F1A-BF79-4c08-812A-BB9778928F78");
 internal static readonly Guid IClrRuntimeHost = new Guid("90F1A06C-7712-4762-86B5-7A5EBA6BDB02");
 }
 [Guid("90F1A06C-7712-4762-86B5-7A5EBA6BDB02"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 internal interface IClrRuntimeHost
 {
 void Start();
 void Stop();
 void SetHostControl(IntPtr pHostControl);
 IClrControl GetCLRControl();
 void UnloadAppDomain(int dwAppDomainId, bool fWaitUntilDone);
 void ExecuteInAppDomain(int dwAppDomainId, IntPtr pCallback, IntPtr cookie);
 int GetCurrentAppDomainId();

 int ExecuteApplication(string pwzAppFullName, int dwManifestPaths, string[] ppwzManifestPaths,
 int dwActivationData, string[] ppwzActivationData);

 int ExecuteInDefaultAppDomain(string pwzAssemblyPath, string pwzTypeName, string pwzMethodName,
 string pwzArgument);
 }
 [Guid("9065597E-D1A1-4fb2-B6BA-7E1FCE230F61"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 internal interface IClrControl
 {
 [return: MarshalAs(UnmanagedType.IUnknown)]
 object GetCLRManager([In] ref Guid riid);

 void SetAppDomainManagerType(string pwzAppDomainManagerAssembly, string pwzAppDomainManagerType);
 }
 // IClrErrorReportingManager to get watson bukets back from CLR
 //http://msdn.microsoft.com/en-us/library/ms164367(v=VS.100).aspx
 [Guid("980D2F1A-BF79-4c08-812A-BB9778928F78"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 internal interface IClrErrorReportingManager
 {
 [PreserveSig]
 int GetBucketParametersForCurrentException(out WatsonBuckets pParams);
 }
}

In the above code ,I am using this within a windows form to create a memory dump when an exception is thrown. The GetWatson bucket and the clr hosting interface is just to get the watson bucket information from clr. This can even be extended to attach a custom log file. How cool is this. Here is the call-stack for the above exception

Using Mono Cecil Decompiler within Windbg to decompile


I have been hacking Mono lately and one of the coolest projects I have seen is the Mono Decompiler. It’s like Reflector but no UI and an API to decompile. Guess what, even reflector is built on top of Mono Cecil.  Most of times when I have to debug I would end up using  !dumpil  sos command to get  the IL  of the method desc.  I am not an IL guru, I am much comfortable reading C# than IL. I could have used reflector to do the same thing, but here are couple of reasons for doing this.

  • I don’t have to open another application and I am comfortable with cdb.exe
  • I like to understand this library , which I could use in the future.

Here is a simple app that would return C# code.  I could have written a windbg extension, but I am lazy


using System;
using System.IO;
using System.Linq;
using System.Text;
using Mono.Cecil;
using Cecil.Decompiler.Languages;

namespace Conosole
{
 class Decompiler
 {
 static void Main(string[] args)
 {
 var function = args[1].Split(new[] { '.' });
 //MethodName
 var methodName = function.Last();
 //Typename
 var typeName = function.ElementAt(function.Length - 2);
 //Namespace
 var ns = function.TakeWhile((c, i) => i < function.Length - 2).Aggregate(new StringBuilder(),(sb,s) => sb.Append(s+".")).ToString().Trim(new [] {'.'}) ;

 Console.WriteLine((from module in AssemblyFactory.GetAssembly(args[0]).Modules.Cast<ModuleDefinition>()
 from type in module.Types.Cast<TypeDefinition>()
 from method in type.Methods.Cast<MethodDefinition>()
 where type.Namespace == ns && type.Name == typeName && method.Name == methodName
 select method.SourceCode()).First());
 }
 }
 public static class Extensions
 {
 public static string SourceCode(this MethodDefinition methodName)
 {
 var writer = new StringWriter();
 CSharp.GetLanguage(CSharpVersion.V3).GetWriter(new PlainTextFormatter(writer)).Write(methodName);
 return writer.ToString();
 }
 }
}

And here is the usage within windbg. I am using LinqPad as an example. Here is the !clrstack output

0:000> !CLRStack
OS Thread Id: 0x16e0 (0)
Child SP IP       Call Site
001ae758 7695438d [InlinedCallFrame: 001ae758] System.Windows.Forms.UnsafeNativeMethods.WaitMessage()
001ae754 61fc737a System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32) [f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Application.cs @ 2198]
001ae7f0 61fc6e2c System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext) [f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Application.cs @ 3422]
001ae848 61fc6c81 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext) [f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Application.cs @ 3306]
001ae878 61f5366d System.Windows.Forms.Application.Run(System.Windows.Forms.Form) [f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Application.cs @ 1496]
001ae88c 0038316b LINQPad.Program.Run(System.String, Boolean, Boolean, Boolean, System.String)
001ae96c 003813f1 LINQPad.Program.Go(System.String[])
001aeb84 00380554 LINQPad.Program.Start(System.String[])
001aebc4 0038034f LINQPad.ProgramStarter.Run(System.String[])
001aebd0 003800f5 LINQPad.Loader.Main(System.String[])
001aee18 6d7e21db [GCFrame: 001aee18]

I am going to try and decompile this method “003813f1 LINQPad.Program.Go”  into c#

Here is the output from !ip2md 003813f1 to get the method name and class address

0:000> !ip2md 003813f1
MethodDesc:   002c9024
Method Name:  LINQPad.Program.Go(System.String[])
Class:        0036f404
MethodTable:  002c9230
mdToken:      0600025e
Module:       002c2e9c
IsJitted:     yes
CodeAddr:     00380590
Transparency: Critical

The next command is to get the location of the assembly and here I am using the class address from the above command


.shell -ci "!dumpclass  0036f404" findstr File:

And then issued the following commands


.shell

d:\tools\Decompiler “D:\tools\LINQPad.exe” “LINQPad.Program.Go”

Here is the C# code within windbg

So next time, I might not be looking at IL Code inside the debugger.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: