设为首页收藏本站language 语言切换
查看: 1529|回复: 0
收起左侧

解密微软中间语言MSIL之调试程序

[复制链接]
发表于 2010-2-25 10:28:30 | 显示全部楼层 |阅读模式
<p >没有程序员敢保证没有经过调试的代码绝对没有错误,无论他/她智商多么高,开发出来的代码总是或多或少带有一些错误(当然是无意的:-))。这些错误可能是简单的语法错误或者复杂的逻辑错误。因此和其他语言一样,我们需要中间语言的调试工具/方法。由于中间语言是比较底层的语言,因此调试工具/方法对于程序员来说更加重要。<p >最简单的调试方法莫过于在程序中加入WriteLine方法,但是在中间语言中使用这种方法非常繁琐,因为调用WriteLine方法需要三行代码:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>ldstr      &quot;Hello World&quot;call       void [mscorlib]System.Console::WriteLine(string)ret</ccid_code></pre></td></tr></table></ccid_nobr><p >如果你需要调试一个比较大的应用程序,显然上面的方法行不通。幸运的是,微软在.Net中提供了两个调试工具用于调试.Net的程序集。<p ><center><font color="#000099"><strong>调试工具</strong></font></center><p >.Net提供了两个非常好的调试工具,分别是CLR调试器和运行库调试器:<p >· CLR调试器(DbgCLR.exe):提供图形界面帮助开发者调试程序。<p >· 运行库调试器(Cordbg.exe):使用运行库调试API,通过命令行对程序进行调试。<p >初看这两个工具提供的是相同的功能。但是事实上它们的功能还是有所区别的。DbgCLR.exe是一个Windows应用程序,提供了用户界面,并且很容易定义断点和即时窗口;而Cordbg.exe使一个命令行工具,它允许开发人员通过调试脚本的方式来调试程序。本文侧重于介绍DgbCLR.exe。<p ><center><font color="#000099"><strong>CLR调试器</strong></font></center><p >为了展示CLR调试器的功能,我们需要编写一个带有错误的程序。下面是一个C#的程序,编译后我们将通过利用ildasm.exe获得它的中间语言代码。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>using System;namespace ErrorneousApp{    class ErrorneousClass    {        [STAThread]        static void Main(string[] args)        {            int operand1;            int operand2;            int sum;            operand1 = int.Parse(args[0]);            operand2 = int.Parse(args[1]);            sum = Add(operand1 , operand2);            Console.WriteLine(sum);        }        private static int Add(int op1, int op2)        {            // 很明显的逻辑错误 :-)            return 5 * (op1 + op2);        }    }}</ccid_code></pre></td></tr></table></ccid_nobr><p >我们可以看到在这段代码中存在两个错误:<p >· 逻辑错误:Add方法返回了不正确的值<p >· 方法错误:如果调用方法是没有提供两个参数,程序将报错。<p >需要提醒大家的是,CLR调试器能够调试的错误远远不止以上两种。现在编译这段代码,生成可执行文件,然后用反汇编工具生成中间语言,其中去除了一些不重要的代码:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>.assembly extern mscorlib{  .publickeytoken = (B7 7A 5C 56 19 34 E0 89)  .ver 1:0:3300:0}.assembly ErrorneousApp{  .ver 1:0:1026:17140}.module ConsoleApplication1.exe.namespace ErrorneousApp{  .class private auto ansi beforefieldinit ErrorneousClass         extends [mscorlib]System.Object  {    .method private hidebysig static void             Main(string[] args) cil managed    {      .entrypoint      .locals init ([0] int32 operand1, [1] int32 operand2, [2] int32 sum)        ldarg.0        ldc.i4.0        ldelem.ref        call       int32 [mscorlib]System.Int32:arse(string)        stloc.0        ldarg.0        ldc.i4.1        ldelem.ref        call       int32 [mscorlib]System.Int32:arse(string)        stloc.1        ldc.i4.5        ldloc.0        ldloc.1        add        mul        stloc.2        ldloc.2        call       void [mscorlib]System.Console::WriteLine(int32)        ret    } // end of method ErrorneousClass::Main    .method private hidebysig static int32             Add(int32 op1, int32 op2) cil managed    {      .locals init ([0] int32 CS$00000003$00000000)        ldc.i4.5        ldarg.0        ldarg.1        add        mul        stloc.0        ldloc.0        ret    } // end of method ErrorneousClass::Add    .method public hidebysig specialname rtspecialname             instance void  .ctor() cil managed    {        ldarg.0        call       instance void [mscorlib]System.Object::.ctor()        ret    } // end of method ErrorneousClass::.ctor  } // end of class ErrorneousClass} // end of namespace ErrorneousApp</ccid_code></pre></td></tr></table></ccid_nobr><p >在使用代码以前先解释一下代码。我们使用了Consle.WriteLine和Int.Parse方法,这两个方法定义在外部程序集mscorlib中,因此我们需要创建一个对它们的引用。通过使用带external参数的assembly命令我们可以达到这个目的。<p >然后代码中通过class命令定义了ErrorneousClass类,并在该类中用method命令创建了Main方法,该方法是程序的入口方法。接着用local命令初始化了三个本地变量:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>.locals init ([0] int32 operand1, [1] int32 operand2, [2] int32 sum)</ccid_code></pre></td></tr></table></ccid_nobr><p >接下来需要给这些本地变量赋值。代码通过ldelm命令从程序的参数数组中提取相应的值赋给变量,但是在赋值之前需要用ldarg将参数(由指定索引值引用)加载到堆栈上,然后ldc命令将真正的值推送到计算堆栈上。ldc.i4.0的表示将0作为int32类型推送到计算堆栈上。接下来代码调用System.Int32.Parse方法将字符串转换为整数。当所有的变量都完成初始化后,代码调用Add方法计算operand1和operand2的和。最后通过调用System.Console.WriteLine方法来显示计算结果。<p >Add方法的实现也很简单。需要提醒大家的是由于MSIL工作在基于堆栈的内存结构上,因此最后使用的变量需要最先保存。由于算法是将两个数相加在乘以5,因此需要先用ldc.i4.5命令将5放入堆栈中,然后加载两个被操作的数,使用add命令计算它们的和(add命令自动将堆栈中最顶层的两个数相加),将得到的结果放回堆栈中。最后代码调用mul命令将两个数相乘。<p >如果仔细察看中间代码,我们会发现在Main方法中并没有直接调用Add方法。这是因为C#编译器用内嵌代码替代了对静态方法的调用。<p ><center><font color="#000099"><strong>调试MSIL代码</strong></font></center><p >调试中间代码的同时我们需要程序数据库文件ErrorneuosApp.pdb,在该文件中包含了调试和工程状态信息。我们可以利用ilasm工具来获得该文件。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><pre><ccid_code>ilasm errorneousApp.il /debug</ccid_code></pre></td></tr></table></ccid_nobr><p >现在运行DbgClr.exe并打开ErroneousApp.il文件(如图一所示),然后设定需要调试的可执行文件(如图二所示)。现在就可以调试程序了。开发人员可以设定断点,查看寄存器和内存中的值等。下面让我们一一了解这些功能。<p ><center><img  src="http://www.hh010.com/upload_files/article/244/9_lcxofj93021.jpg"></center><p ><center>图一 CLR调试器</center><p ><center><img  src="http://www.hh010.com/upload_files/article/244/9_wfvgv293022.jpg"></center><p ><center>图二 选择要调试的程序</center><p align="center"><font color="FF0000" >1</font>2<span class="content01">下一页&gt;&gt;</span></p></p>
您需要登录后才可以回帖 登录 | 论坛注册

本版积分规则

QQ|Archiver|手机版|小黑屋|sitemap|鸿鹄论坛 ( 京ICP备14027439号 )  

GMT+8, 2025-4-10 13:15 , Processed in 0.056678 second(s), 24 queries , Redis On.  

  Powered by Discuz!

  © 2001-2025 HH010.COM

快速回复 返回顶部 返回列表