Archive for June 2010
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
- 64b56c28 - Array’s Method table pointer
- 00000002 - Array’s length ( this will be used later)
- 001938b0 -Array contents method table pointer ( Test class)
- 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
- 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.
- The next statement is “r$t1 = 0″ is initializing another pseudo register to zero
- 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).
- The “.echo ************” is just for line separation
- 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 8768Free 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;
}
}
}
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 2dC:\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 nopC:\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 nopC:\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 nopC:\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.








