Making an Image Easier to Debug


I am doing security review for a managed application which is obfuscated. So I am doing a lot of   disassembling code at runtime using Windbg. One of the issues is that code gets JIT optimized because of the retail build. This makes it harder for me debug when mapping it back. Realized  that I could turnoff  JIT Optimization’s using the ini file.

[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0

Another use of feature which I guess wasn’t really intended for.


Updating .NET String in memory with Windbg


In this post I would show a simple trick to update .NET strings in memory with Windbg. The caveat is make sure the string that you’re updating is long enough to fit into the string buffer. If not there would be a memory corruption.

Here is a simple windows form application with title “Good”

The goal is to update the title from “Good” to “Bad”.


button1.Click += (s,b) => Text = _caption;

I am updating the title in the button click.

Here is the actual string object within the debugger

0:006> !do 0294d0a0
Name:        System.String
MethodTable: 59b9fb64
EEClass:     598d8bb0
Size:        22(0x16) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\
v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      Good
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
59ba2b30  40000ed        4         System.Int32  1 instance        4 m_stringLength
59ba1f80  40000ee        8          System.Char  1 instance       47 m_firstChar
59b9fb64  40000ef        8        System.String  0   shared   static Empty
    >> Domain:Value  004b0308:02941228 <<

I would be using the e  command to update the memory. The ezu command is used for updating  Null-terminated Unicode string .

Notice the first character starts in the 8th offset from the above. So we would have start updating the string only from the 8th offset. The first 8 bytes of object are for syncblock index and method table pointer.

Here is the command to update the string memory.

ezu 0294d0a0+8 “Bad”

And the updated form title.

Who is is blocking my UI Thread? Diagnosing the cause using Windbg


It so happens most of the applications block the UI thread and do sync I/O, which is most common reason for “Not Responding” window. Here is a post http://blogs.msdn.com/b/nathannesbit/archive/2010/12/28/detecting-ui-thread-misuse.aspx that tries helping in detecting this. I like to handle this from bottom of the stack because we have a cool tool called debugger.

The approach is simple as having a break-point on a function like “KERNEL32!WaitFor*” and checking if the current thread is a UI thread. This could also be done for other functions like Sleep on UI thread by having a break-point on “KERNELBASE!SleepEx”.

Here are steps to determine if a thread is a STA thread / UI Thread. This is information is stored in TEB structure (thread environment block). Here is the output of the teb on the UI Thread

0:000> dt ntdll!_TEB @$teb
+0x000 NtTib            : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId         : _CLIENT_ID
+0x028 ActiveRpcHandle  : (null)
+0x02c ThreadLocalStoragePointer : 0x7efdd02c
+0x030 ProcessEnvironmentBlock : 0x7efde000 _PEB
+0x034 LastErrorValue   : 0
+0x038 CountOfOwnedCriticalSections : 0
+0x03c CsrClientThread  : (null)
+0x040 Win32ThreadInfo  : (null)
+0x044 User32Reserved   : [26] 0
+0x0ac UserReserved     : [5] 0
+0x0c0 WOW32Reserved    : 0x751b2320
+0x0c4 CurrentLocale    : 0x409
+0x0c8 FpSoftwareStatusRegister : 0
+0x0cc SystemReserved1  : [54] (null)
+0x1a4 ExceptionCode    : 0
+0x1a8 ActivationContextStackPointer : 0x001a07d0 _ACTIVATION_CONTEXT_STACK
+0x1ac SpareBytes       : [36]  “”
+0x1d0 TxFsContext      : 0xfffe
+0x1d4 GdiTebBatch      : _GDI_TEB_BATCH
+0x6b4 RealClientId     : _CLIENT_ID
+0x6bc GdiCachedProcessHandle : (null)
+0x6c0 GdiClientPID     : 0
+0x6c4 GdiClientTID     : 0
+0x6c8 GdiThreadLocalInfo : (null)
+0x6cc Win32ClientInfo  : [62] 0
+0x7c4 glDispatchTable  : [233] (null)
+0xb68 glReserved1      : [29] 0
+0xbdc glReserved2      : (null)
+0xbe0 glSectionInfo    : (null)
+0xbe4 glSection        : (null)
+0xbe8 glTable          : (null)
+0xbec glCurrentRC      : (null)
+0xbf0 glContext        : (null)
+0xbf4 LastStatusValue  : 0xc0000139
+0xbf8 StaticUnicodeString : _UNICODE_STRING “”
+0xc00 StaticUnicodeBuffer : [261]  “”
+0xe0c DeallocationStack : 0x00320000
+0xe10 TlsSlots         : [64] (null)
+0xf10 TlsLinks         : _LIST_ENTRY [ 0x0 - 0x0 ]
+0xf18 Vdm              : (null)
+0xf1c ReservedForNtRpc : 0x001d8c70
+0xf20 DbgSsReserved    : [2] (null)
+0xf28 HardErrorMode    : 0
+0xf2c Instrumentation  : [9] (null)
+0xf50 ActivityId       : _GUID {00000000-0000-0000-0000-000000000000}
+0xf60 SubProcessTag    : (null)
+0xf64 EtwLocalData     : (null)
+0xf68 EtwTraceData     : (null)
+0xf6c WinSockData      : (null)
+0xf70 GdiBatchCount    : 0x7efdb000
+0xf74 CurrentIdealProcessor : _PROCESSOR_NUMBER
+0xf74 IdealProcessorValue : 0x1010000
+0xf74 ReservedPad0     : 0 ”
+0xf75 ReservedPad1     : 0 ”
+0xf76 ReservedPad2     : 0x1 ”
+0xf77 IdealProcessor   : 0x1 ”
+0xf78 GuaranteedStackBytes : 0x1000
+0xf7c ReservedForPerf  : (null)
+0xf80 ReservedForOle   : 0x001ffd50

I have shown only the partial output because we are interested only in “ReservedForOle” member which is in the oxf80 offset. Within this structure in “0xc” offset contains the information on whether it is STA / MTA / Unkown and here is a write up on this from John Robbins http://www.microsoft.com/msj/1099/bugslayer/bugslayer1099.aspx.  Though the posts mentions STA as 0x80 and MTA as 0x140 with current version of windows value of STA is 81 and MTA is 141.
With this information it was pretty easy to create a script which will give us a call-stack if a UI Thread is blocking.

bm KERNEL32!WaitFor* ".if (poi(@$teb+0xf80) != 0) { .if (poi(poi(@$teb+0xf80)+0xc) = 81) {!clrstack;g} .else {g}} .else {gh}"
bp KERNELBASE!SleepEx ".if (poi(@$teb+0xf80) != 0) { .if (poi(poi(@$teb+0xf80)+0xc) = 81) {!clrstack;g} .else {g}} .else {gh}"

Here is a example call-stack from the above break-point which indicates that we are blocking on the UI Thread

OS Thread Id: 0x76c (0)
Child SP IP       Call Site
0029e74c 775c118e [InlinedCallFrame: 0029e74c]
0029e748 5affbc00 DomainBoundILStubClass.IL_STUB_PInvoke(System.Net.Sockets.AddressFamily, System.Net.Sockets.SocketType, System.Net.Sockets.ProtocolType, IntPtr, UInt32, System.Net.SocketConstructorFlags)
0029e74c 5afa72e4 [InlinedCallFrame: 0029e74c] System.Net.UnsafeNclNativeMethods+OSSOCK.WSASocket(System.Net.Sockets.AddressFamily, System.Net.Sockets.SocketType, System.Net.Sockets.ProtocolType, IntPtr, UInt32, System.Net.SocketConstructorFlags)
0029e7a4 5afa72e4 System.Net.Sockets.Socket.InitializeSockets()
0029e7f4 5afcc3ca System.Net.NetworkAddressChangePolled..ctor()
0029e808 5afcc326 System.Net.AutoWebProxyScriptEngine+AutoDetector.Initialize()
0029e838 5af7534d System.Net.AutoWebProxyScriptEngine+AutoDetector.get_CurrentAutoDetector()
0029e83c 5af75263 System.Net.AutoWebProxyScriptEngine..ctor(System.Net.WebProxy, Boolean)
0029e858 5af75202 System.Net.WebProxy.UnsafeUpdateFromRegistry()
0029e868 5af751c8 System.Net.WebProxy..ctor(Boolean)
0029e86c 5af74c79 System.Net.Configuration.DefaultProxySectionInternal..ctor(System.Net.Configuration.DefaultProxySection)
0029e8b0 5af748d2 System.Net.Configuration.DefaultProxySectionInternal.GetSection()
0029e8e4 5afcbf76 System.Net.WebRequest.get_InternalDefaultWebProxy()
0029e914 5afcbc86 System.Net.HttpWebRequest..ctor(System.Uri, System.Net.ServicePoint)
0029e92c 5afcbbcb System.Net.HttpRequestCreator.Create(System.Uri)
0029e938 5afcb772 System.Net.WebRequest.Create(System.Uri, Boolean)
0029e95c 5af94cad System.Net.WebRequest.Create(System.String)
0029e96c 0087056e WindowsFormsApplication1.Form1.<.ctor>b__0(System.Object, System.EventArgs) [C:\Users\naveen\Documents\Visual Studio 2010\Projects\WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs @ 15]
0029e9ac 592b4ae8 System.Windows.Forms.Control.OnClick(System.EventArgs)
0029e9c4 592b70a2 System.Windows.Forms.Button.OnClick(System.EventArgs)
0029e9dc 59846174 System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs)
0029e9f8 598195b5 System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32)
0029ea8c 59bda1bf System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
0029ea90 59be18dd [InlinedCallFrame: 0029ea90]
0029eae4 59be18dd System.Windows.Forms.ButtonBase.WndProc(System.Windows.Forms.Message ByRef)
0029eb28 5931de00 System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)
0029eb34 593070f3 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)
0029eb3c 59307071 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
0029eb50 59306fb6 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
0029ecf4 01010a35 [InlinedCallFrame: 0029ecf4]

Correlating between .NET and native thread in Windbg


I recently saw a stackoverflow question where  someone wanted to know how they could correlate between  managed and native threads within Windbg.

Here is the managed thread object within the debugger

0:004> !do 02a1d6c4
Name:        System.Threading.Thread
MethodTable: 672e001c
EEClass:     67018ed8
Size:        48(0x30) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
672c8a78  4000720        4 ….Contexts.Context  0 instance 00000000 m_Context
672db4b8  4000721        8 ….ExecutionContext  0 instance 00000000 m_ExecutionContext
672df9fc  4000722        c        System.String  0 instance 02a1a220 m_Name
672dfed0  4000723       10      System.Delegate  0 instance 00000000 m_Delegate
672e63f4  4000724       14 …ation.CultureInfo  0 instance 00000000 m_CurrentCulture
672e63f4  4000725       18 …ation.CultureInfo  0 instance 00000000 m_CurrentUICulture
672df638  4000726       1c        System.Object  0 instance 00000000 m_ThreadStartArg
672daa7c  4000727       20        System.IntPtr  1 instance   542560 DONT_USE_InternalThread
672e29c8  4000728       24         System.Int32  1 instance        2 m_Priority
672e29c8  4000729       28         System.Int32  1 instance        3 m_ManagedThreadId
672cb76c  400072a      18c …LocalDataStoreMgr  0   shared   static s_LocalDataStoreMgr
>> Domain:Value  0049f148:NotInit  <<
672ce328  400072b        c …alDataStoreHolder  0   shared TLstatic s_LocalDataStore
>> Thread:Value <<

The present thread’s @$teb (Thread Environment Block) is 7efac000

0:004> ? @$teb Evaluate expression: 2130374656 = 7efac000

The DONT_USE_InternalThread is pointer to the native thread. Dumping the raw memory of the pointer should give us more information we are looking for.

0:004> dd poi(02a1d6c4+20)
00542560  67e9ee88 0000b220 00000000 056ef42c
00542570  00000000 00000000 00000000 00000003
00542580  00000000 00542588 00542588 00542588
00542590  00000000 00000000 baad0000 004a4f30
005425a0  7efac000 baadf00d 00000000 00000000
005425b0  00024dac 00000000 00000000 00000000
005425c0  00000000 baadf00d 00541ba0 00544290
005425d0  00544298 00000200 00544290 00544580

The pointer to teb is in the 40th offset of the  DONT_USE_InternalThread and here is the script that would get teb for each managed thread.

.foreach ($thread {!dumpheap -mt 672e001c -short}) { .if ( poi(${$thread}+20) != 0) {.printf "%p \n",dwo(poi(${$thread}+20)+40)}}

0:004> .foreach ($thread {!dumpheap -mt 672e001c -short}) { .if ( poi(${$thread}+20) != 0) {.printf “%p \n”,dwo(poi(${$thread}+20)+40) }}
7efdd000
7efac000
7ef9a000
7ef97000
7ef2f000
7ef26000
7efd7000
7ef20000
7ef1d000
7ef1d000
7ef0e000
7efa3000
7ef2c000

So with the above we could dump the teb structure using dt ntdll!_TEB command. In the next post I will demonstrate how this can be used to debug some cool stuff :) Read more of this post

Conditional BreakPoint based on callstack within Windbg – .NET


Someone recently asked me “How to have a break-point on a method based on certain function in the call-stack?”

Here is the sample code to demonstrate this

using System;
using System.Threading.Tasks;
using System.Data.SqlClient;
namespace Test
{
    class Program
    {
        string connectionString = @"Data Source=.\sqlexpress;Initial Catalog=Tfs_Configuration;Integrated Security=True";
        public void Bar()
        {
            using (var c = new SqlConnection(connectionString))
            {
                c.Open();
                var command = new SqlCommand(@"update [tbl_AccessMapping] set [DisplayName] = @param", c);
                command.Parameters.Add(new SqlParameter("param", "Bar"));
                command.ExecuteNonQuery();
            }
        }
        public void Foo()
        {
            using (var c = new SqlConnection(connectionString))
            {
                c.Open();
                var command = new SqlCommand(@"update [tbl_AccessMapping] set [DisplayName] = @param", c);
                command.Parameters.Add(new SqlParameter("param", "Foo"));
                command.ExecuteNonQuery();
            }
        }
        static void Main(string[] args1)
        {
            var s = new Program();
            Parallel.For(0, 2, (i) => s.Bar());
            Parallel.For(0, 2, (i) => s.Foo());
            Console.Read();
        }
    }
}

The requirement is to have a break-point on “ExecuteNonQuery” but it should break only if it is invoked from “Foo” and not from “Bar”.

Launched the exe within windbg and loaded sos,sosex and set a bp on System.Data.SqlClient.SqlCommand.ExecuteNonQuery suing !mbm

And when the break-point hits the first time updated the bp using

bs 0  $$>a<“d:\Debuggersx86\ConditionalBP.txt” Foo

Here are the contents of ConditionalBP.txt

ad /q Contains
aS /c Contains .shell -ci "!CLRStack" FINDSTR $arg1
.block {
            .if ($spat("${Contains}","*${$arg1}*"))
                {
                 !CLRStack
                }
           .else
                { 
                g
                }
     }
ad /q Contains

Saving Dynamic Assembly in .NET 4.0 using Windbg


I recently had to debug a .NET 4.0 process which was loading the dependent assemblies using the AppDomain.AssemblyResolve event. The dependent assemblies were stored within the executable. I couldn’t disassemble the code to look for the dependent assembly because the exe was obfuscated. FYI the dynamic assembly cannot be saved using !SaveModule and here is the reason for this read the comments especially from Evian. Unlike psscor2.dll the sos for .NET 4.0 does not have a !dumpdynamicassembly with a save option.

Here is the sample code to demonstrate this.


using System;
using System.Reflection;
using TestLib;
namespace Test
{
 class Foo1
 {
 int[] s = new int[2];
 int v = 100;
 public Foo1()
 {
 Console.WriteLine(new Class1().Foo());
 }
 static void Main(string[] args1)
 {
 AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
 {
 String resourceName = "ConsoleApplication13." +new AssemblyName(args.Name).Name + ".dll";
 using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
 {
 Byte[] assemblyData = new Byte[stream.Length];
 stream.Read(assemblyData, 0, assemblyData.Length);
 return Assembly.Load(assemblyData);
 }
 };
 System.IO.File.Delete(@"C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication13\bin\Debug\TestLib.dll");
 var s = new Foo1();
 Console.Read();
 }
 }
}

I knew the Assembly had to be loaded using  System.Reflection.Assembly.Load(Byte[]) ,so ended setting a break-point on the method using command !mbm *Assembly.Load* on the launch of the executable.

Here are the output of args and local variables for the above break-point

0:000> !mdv
Frame 0x0: (System.Reflection.Assembly.Load(Byte[])):
[A0]:rawAssembly:0x025fc374 (System.Byte[])
[L0]:<?>

Notice the “rawAssembly” argument which has the assembly contents.  Here are the raw memory contents of the address using dd 0x025fc374

0:000> dd 0x025fc374
025fc374  6a764994 00001000 00905a4d 00000003
025fc384  00000004 0000ffff 000000b8 00000000
025fc394  00000040 00000000 00000000 00000000
025fc3a4  00000000 00000000 00000000 00000000
025fc3b4  00000000 00000080 0eba1f0e cd09b400
025fc3c4  4c01b821 685421cd 70207369 72676f72
025fc3d4  63206d61 6f6e6e61 65622074 6e757220
025fc3e4  206e6920 20534f44 65646f6d 0a0d0d2e

  1. 6a764994 :- Is the Array’s  Method Table
  2. 00001000 : – Is the Array size
  3. The rest are the array contents.

Unlike the reference type arrays, the value type arrays  don’t have a DWORD for Method table of its contents. With this information I could dump the contents from memory in to disk using .writemem command.


.writemem c:\temp\assembly.bin @ecx+8 L?(poi(@ecx+@$ptrsize)*@$ptrsize)

In x86 @ecx register contains argument for rawAssembly. The  @ecx+8 is the start  position of the first byte and that is the reason for using this as the start position for .writemem. The poi(@ecx+@$ptrsize) contains the array size which in our case is 0001000 and multiply it by @$ptrsize which is 4 in x86. The expression (poi(@ecx+@$ptrsize)*@$ptrsize) would in our case result to 4000 bytes.

The assembly.bin would contain data in hex format which has to be converted in to binary format. Here is the code to convert from Hex to Binary format.

Assembly.Load( File.ReadAllBytes(@"c:\temp\assembly.bin")
 .Select(x =>
 Convert.ToByte(
 int.Parse((x.ToString("X")),NumberStyles.HexNumber)
 )).ToArray())
.FullName.Dump();

Dumping Generic List in .NET within Windbg


Most of the code uses List<T> for storing items.  The present solutions don’ t have a way to dump List<T> within windbg. Even though sosex has an option to dump the List<T> using !mdt it still doesn’t meet the scripting requirements. For example here is an output using sosex “!mdt -e 029a91c0″

0:000> !mdt -e 029a91c0
029a91c0 (System.Collections.Generic.List`1[[Test.Foo, Test]])
Count = 2
[0] 029a9200 (Test.Foo)
[1] 029a9210 (Test.Foo)

I would have preferred to get the contents of the “Foo” object instead of just the address of Foo. So wrote a script to do that.


$$ pointer to the array within the List
r @$t5 = poi(${$arg1}+@$ptrsize)

 .if (@$ptrsize = 8 )
 {
    r @t7 = 20 
 } 
 .else 
 { 
    r @$t7 = 10
 }

 .for (r $t0=0; @$t0 &lt; poi(@$t5+@$ptrsize); r$t0=@$t0+1 )
 {
     .if(@$t0 = 0)
     {
         $$ First occurence of the element in the array would be in the 20 offset for x64 and 10 offset for x86
         r$t1=@$t7
     }
     .else
     {
         $$ the rest of the elements would be in the 8th offset for x64 and 4th offset for x86
         r$t1= @$t7+(@$t0*@$ptrsize)
     }
     $$ Check for null before trying to dump
     .if (poi((@$t5-@$ptrsize)+@$t1) = 0 )
     {
     .continue
     }
     .else
     {
     .printf &quot;%N \n&quot; ,poi((@$t5-@$ptrsize)+@$t1)
     }
 }                                  

This script should work in x86 and x64. To use the above script copy to a file and invoke it like this passing the address of List<T>

$$>a<"d:\Debuggersx86\dumplist.txt" 029a91c0

Here is the output from the above command.

0:000> $$>a<"d:\Debuggersx86\dumplist.txt" 029a91c0
029A9200
029A9210

Now with this script I can use !mdt to get the contents of the “Foo” object.

.foreach ($obj {$$>a<"d:\Debuggersx86\dumplist.txt" 029a91c0}) {!mdt $obj}

0:000> .foreach ($obj {$$>a<"d:\Debuggersx86\dumplist.txt" 029a91c0}) {!mdt $obj}
029a9200 (Test.Foo)
counter:0x1 (System.Int32)
Name:029a917c (System.String: "test")
029a9210 (Test.Foo)
counter:0x2 (System.Int32)
Name:029a9198 (System.String: "test2")

This is one of the scripts that I would use often. Hope it is useful to others also.

Why isn’t the !bpmd in sos / windbg not working?


I recently noticed another blog post refer to one of my post. The issue was, sos wasn’t enabling the break-points on non-jitted functions. The classic example being “Main”.  Thanks to Steve I have been using sosex and not sos for setting break-points.

From my previous post you can understand how CLR is using clrn/CLRNotificationException to notify sos/sosex on JIT. With this information when I looked at the rotor code, I noticed an interesting member variable “g_dacNotificationFlags”. So I decided to check the value of this variable when using !bpmd from sos and !mbm from sosex.


.if (dwo(mscorwks!g_dacNotificationFlags) = 0) {.echo bp not set } .else {.echo bp set}

It was “0” when using sos and “1” when using sosex. Now I had to change the value to “1” and check if the break-point becomes active when using sos’s !bpmd.  FYI I don’t have private symbols and haven’t seen CLR Code. Here is the code to set the value to “1”.


ed mscorwks!g_dacNotificationFlags 00000001

And not to my surprise the !bpmd seems to work for non-jitted function with the above hack. FYI we don’t have to resort to this to get !bpmd to work. If the !bpmd is set after load of mscorjit/clrjit it would work as expected.

Using sosex within windbg to understand IL and Assembly code


Sometimes when debugging managed code within the debugger I would like to see the C# code ,the IL translation for the managed code and the Assembly code for the IL. For example I recently learned that callvirt MSIL instruction must do the null-check before invoking method.

C:\Users\naveen\Documents\Visual Studio 2010\Projects\ConsoleApplication13\Program.cs @ 18:
00bc26d8 8b4dec          mov     ecx,dword ptr [ebp-14h]
00bc26db 3909            cmp     dword ptr [ecx],ecx //NULL Check
00bc26dd ff1508a82900    call    dword ptr ds:[29A808h] (System.String.ToLower(), mdToken: 0600031d)
00bc26e3 8945e8          mov     dword ptr [ebp-18h],eax
00bc26e6 8b45e8          mov     eax,dword ptr [ebp-18h]
00bc26e9 8945ec          mov     dword ptr [ebp-14h],eax

I am not an assembly code expert. The above output is from “!u” sos command. It doesn’t show the c# code except the line number and it is missing IL translation.

The “!mu” from sosex does what I want. It is not yet documented because it is not yet stable as per the output of the command. Here is the output for the same call-stack using sosex’s !mu.

0:000> !mu
THIS COMMAND IS UNDOCUMENTED AND NOT YET STABLE.
test = test.ToLower();
IL_001a: ldloc.0  (test)
IL_001b: callvirt System.String::ToLower
00bc26d8 8b4dec          mov     ecx,dword ptr [ebp-14h]
00bc26db 3909            cmp     dword ptr [ecx],ecx
00bc26dd ff1508a82900    call    dword ptr ds:[29A808h]
00bc26e3 8945e8          mov     dword ptr [ebp-18h],eax
IL_0020: stloc.0  (test)
00bc26e6 8b45e8          mov     eax,dword ptr [ebp-18h]
00bc26e9 8945ec          mov     dword ptr [ebp-14h],eax

The above output has c#,IL and assembly.

Windbg trick – Having custom name for user-defined pseudo-registers


There are 20 user-defined pseudo-registers ($t0, $t1, …, $t19) in windbg/cdb . To have scripts with variable names as @$t0 and @$t1 isn’t helpful for readability. The trick to avoid this is by using the “aS” command.

Here is an example, for a loop variable I would like to use a variable name like “i” instead of “@$t0″ and to use “i”  as a variable  here is the command

aS i “@$t0″

Now”i” is just an alias for “@$t0″.  Here is another example of using “i” in the comparison statement

j (${i} =0) ‘.echo is zero’ ; ‘.echo is not zero’

This is the command to remove the alias without evaluating it.

ad ${/v:i}

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: