用WinDbg进行CLI探险(2) 调试本机代码,RuntimeType

先看下面这样一段代码:

 1         static void Main(string[] args)
2 {
3 Console.WriteLine("Start .....");
4 Test();
5 }
6
7 static void Test()
8 {
9 Console.WriteLine("Test ...");
10 Console.ReadLine();
11 Regiest(typeof(implementclass));
12 }
13
14 static void Regiest(Type type)
15 {
16 }

我们现在要做的是用windbgDebug到Test函数里面,然后看看发生了什么,运行代码:

Windbg下载地址: http://www.microsoft.com/whdc/devtools/debugging/default.mspx

然后我们可以运行一个叫做: Process Explorer的软件看到当前所有运行的进程:

下载地址:http://technet.microsoft.com/en-us/sysinternals/bb896653

打开Winddbg,

打开菜单栏中的debug菜单,关闭SourceMode选项,进入汇编调试模式,

 Attach到进程号为3516的进程中去,然后运行

.loadby sos clr命令加载符号集。

sos的部分指令集可以参看:

http://msdn.microsoft.com/zh-cn/library/bb190764(vs.80).aspx

0:004>  !name2ee ConsoleApplication1.exe!ConsoleApplication1.Program.Test
Module:      00232e9c
Assembly:    ConsoleApplication1.exe
Token:       06000002
MethodDesc:  002337fc
Name:        ConsoleApplication1.Program.Test()
JITTED Code Address: 004600b0

我们可以看到 MethodDesc: 002537fc
于是看这个地址的IL代码:

0:004> !dumpil 002337fc
ilAddr = 00232064
IL_0000: nop
IL_0001: ldstr "Test ..."
IL_0006: call System.Console::WriteLine
IL_000b: nop
IL_000c: call System.Console::ReadLine
IL_0011: pop
IL_0012: ldtoken ExtensionProject.implementclass
IL_0017: call System.Type::GetTypeFromHandle
IL_001c: call ConsoleApplication1.Program::Regiest
IL_0021: nop
IL_0022: ret

这个显然正是我们要找的,再看JIT产生的汇编本地代码:

0:004> !u 002337fc
Normal JIT generated code
ConsoleApplication1.Program.Test()
Begin 004600b0, size 55

F:\Lab\ClientBin\TestCompressedFile\ConsoleApplication1\Program.cs @ 19:
004600b0 55              push    ebp
004600b1 8bec            mov     ebp,esp
004600b3 83ec08          sub     esp,8
004600b6 33c0            xor     eax,eax
004600b8 8945fc          mov     dword ptr [ebp-4],eax
004600bb 833d3c31230000  cmp     dword ptr ds:[23313Ch],0
004600c2 7405            je      004600c9
004600c4 e87e670869      call    clr!JIT_DbgIsJustMyCode (694e6847)
004600c9 90              nop

F:\Lab\ClientBin\TestCompressedFile\ConsoleApplication1\Program.cs @ 20:
004600ca 8b0d3420a402    mov     ecx,dword ptr ds:[2A42034h] ("Test ...")
004600d0 e8d76f1e5e      call    mscorlib_ni+0x2570ac (5e6470ac) (System.Console.WriteLine(System.String), mdToken: 06000954)
004600d5 90              nop

F:\Lab\ClientBin\TestCompressedFile\ConsoleApplication1\Program.cs @ 21:
004600d6 e8dda67b5e      call    mscorlib_ni+0x82a7b8 (5ec1a7b8) (System.Console.ReadLine(), mdToken: 06000946)
004600db 90              nop

F:\Lab\ClientBin\TestCompressedFile\ConsoleApplication1\Program.cs @ 22:
004600dc 8d4dfc          lea     ecx,[ebp-4]
004600df ba88432300      mov     edx,234388h (MT: ExtensionProject.implementclass)
004600e4 e8d4910869      call    clr!JIT_GetRuntimeTypeHandle (694e92bd)
004600e9 8d45fc          lea     eax,[ebp-4]
004600ec ff30            push    dword ptr [eax]
004600ee e8d1d91169      call    clr!RuntimeTypeHandle::GetTypeFromHandle (6957dac4)
004600f3 8945f8          mov     dword ptr [ebp-8],eax
004600f6 8b4df8          mov     ecx,dword ptr [ebp-8]
004600f9 ff1510382300    call    dword ptr ds:[233810h] (ConsoleApplication1.Program.Regiest(System.Type), mdToken: 06000003)
004600ff 90              nop

F:\Lab\ClientBin\TestCompressedFile\ConsoleApplication1\Program.cs @ 23:
00460100 90              nop
00460101 8be5            mov     esp,ebp
00460103 5d              pop     ebp
00460104 c3              ret

汇编代码的开始出正好是MethodAddress中的,JITTED Code Address: 004600b0

 然后点击菜单栏中的View,打开Desassembly窗口。

我们的关键代码正是这一行:

F:\Lab\ClientBin\TestCompressedFile\ConsoleApplication1\Program.cs @ 22:

用bp 004600dc在该处设置断点。我们可以看到红色的断点:

 

输入g继续运行,然后键入回车走过Console.ReadLine,可以看到代码在断点处停下。

然后一直执行到Call GetType(004600ee)指令结束。

 此时寄存器的值为:

 我们找到栈顶指针esp-4 22f18c处能看到01a4f0e4这个值,也就是eax中的值

 

再对该处所存地址执行!do命令

0:000> !do 01a4f0e4
Name:        System.RuntimeType
MethodTable: 5e70f5c0
EEClass:     5e448974
Size:        24(0x18) bytes
Type Name:   ExtensionProject.implementclass
Type MT:     00234388
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
5e70d814  4000532       e4 ...tion.MemberFilter  0   shared   static FilterAttribute
    >> Domain:Value  002b4248:NotInit  <<
5e70d814  4000533       e8 ...tion.MemberFilter  0   shared   static FilterName
    >> Domain:Value  002b4248:NotInit  <<
5e70d814  4000534       ec ...tion.MemberFilter  0   shared   static FilterNameIgnoreCase
    >> Domain:Value  002b4248:NotInit  <<
5e70f568  4000535       f0        System.Object  0   shared   static Missing
    >> Domain:Value  002b4248:NotInit  <<
5e711d48  4000536      b60          System.Char  1   shared   static Delimiter
    >> Domain:Value  002b4248:NotInit  <<
5e6c6ba8  4000537       f4      System.Object[]  0   shared   static EmptyTypes
    >> Domain:Value  002b4248:NotInit  <<
5e700b64  4000538       f8 ...Reflection.Binder  0   shared   static defaultBinder
    >> Domain:Value  002b4248:NotInit  <<
5e7012ec  4000542        4 ...che.InternalCache  0 instance 00000000 m_cachedData
5e70f568  4000543        8        System.Object  0 instance 00000000 m_keepalive
5e70a9ac  4000544        c        System.IntPtr  1 instance        0 m_cache
5e70a9ac  4000545       10        System.IntPtr  1 instance   234388 m_handle
5e70e320  4000546       fc ...pe+TypeCacheQueue  0   shared   static s_typeCache
    >> Domain:Value  002b4248:00000000 <<
5e70f5c0  4000547      100   System.RuntimeType  0   shared   static ValueType
    >> Domain:Value  002b4248:01a43470 <<
5e70f5c0  4000548      104   System.RuntimeType  0   shared   static EnumType
    >> Domain:Value  002b4248:01a43488 <<
5e70f5c0  4000549      108   System.RuntimeType  0   shared   static ObjectType
    >> Domain:Value  002b4248:01a434a0 <<
5e70f5c0  400054a      10c   System.RuntimeType  0   shared   static StringType
    >> Domain:Value  002b4248:01a434b8 <<
5e70f5c0  400054b      110   System.RuntimeType  0   shared   static DelegateType
    >> Domain:Value  002b4248:01a434d0 <<
5e70f5c0  400054c      114   System.RuntimeType  0   shared   static s_typedRef
    >> Domain:Value  002b4248:01a434e8 <<
5e70ee58  400054d      118 ...pe+ActivatorCache  0   shared   static s_ActivatorCache
    >> Domain:Value  002b4248:00000000 <<
5e71a69c  400054e      11c  System.OleAutBinder  0   shared   static s_ForwardCallBinder
    >> Domain:Value  002b4248:00000000 <<

我们注意到 RuntimeType的内容中有很多Domain:Value 这样的显示,让我们不禁考虑到RuntimeType是否是可以跨Domain的,不过这一点今天就不讨论了。 

补充:要说明的是,我最开始以为这个RuntimeType的Object是004600ee这里的GetTypeFromHandle方法生成的,但实际上是在前面004600e4行的JIT_GetRunTypeHandle方法生成的。而且我们也知道esp-4这个位置实际上已经出栈了。在我后来的实验中我发现早在调用JIT_GetRunTypeHandle这个方法之后就已经在[ebp-4]处,也就是栈底第一个元素处生成了指向RuntimeType的Object的指针。这个时候执行!do就已经可以看到上面的结果了。那么第二个函数JIT_GetRunTypeHandle到底是做什么的呢。看下面这个截图:

我对clr!RuntimeTypeHandle::GetTypeFromHandle之前和之后的RuntimeType进行了对比,没有找到表面上的区别,但是从上面的截图中我推测GetTypeFromHandle这个语句可能是对虚函数表进行了一些操作,绑定准确类型重载的方法,但这也只是个推测而已。

最后,由于这个函数在被我们Attatch的时候已经被调用,所以JIT已经生成了本地代码,我们可以直接用bp在本地代码中设置断点,如果我们想在本地代码没有生成时设置断点应该怎么办呢?

0:004> !name2ee ConsoleApplication1.exe!ConsoleApplication1.Program.Test
Module:      00182e9c
Assembly:    ConsoleApplication1.exe
Token:       06000002
MethodDesc:  001837fc
Name:        ConsoleApplication1.Program.Test()
Not JITTED yet. Use !bpmd -md 001837fc to break on run.
最后一行显示可以Not JITTED yet表示尚未生成本地代码,并提示我们可以用!bpmd -md 001837fc来设置断点。

但是其实还有另一种方法,它涉及到CLI内部的存储结构,这个留待以后再介绍。

 

 

 

 

posted on 2011-09-13 09:45  老哈  阅读(442)  评论(0编辑  收藏  举报

导航