WinDbg查看C#程序方法汇编代码

一. 测试代码

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            //声明
            int number;
            string str;
            MyObject myObjet;

            //初始化
            number = 666;
            str = "Hello World";
            myObjet = new MyObject();
        }
    }

    public class MyObject
    {
        public int x;
        public int y;
        public int z;
    }
}

由于之前的测试都是使用 x64 的程序,见于我们平时的使用的代码都是 x8 6的我们就使用 32 位编辑的程序进行调试,使用 .Net 4.0 的版本。

现在我们开始调试吧!

二. 使用Visual Studio获得反汇编

如果我们要简单的获得程序的汇编代码,我们在VS中就可以实现,在任意的位置打上断点运行,如下图:

反汇编代码为:

--- D:\博客园文章\如何用WinDbg查看CSharp指定函数的汇编代码\Demo\Demo\Program.cs -------------------
        {
017C4160  push        ebp  
017C4161  mov         ebp,esp  
017C4163  push        edi  
017C4164  push        esi  
017C4165  push        ebx  
017C4166  sub         esp,40h  
017C4169  mov         esi,ecx  
017C416B  lea         edi,[ebp-4Ch]  
017C416E  mov         ecx,10h  
017C4173  xor         eax,eax  
017C4175  rep stos    dword ptr es:[edi]  
017C4177  mov         ecx,esi  
017C4179  mov         dword ptr [ebp-3Ch],ecx  
017C417C  cmp         dword ptr ds:[14ECE38h],0  
017C4183  je          017C418A  
017C4185  call        72B9FCD0  
017C418A  xor         edx,edx  
017C418C  mov         dword ptr [ebp-44h],edx  
017C418F  xor         edx,edx  
017C4191  mov         dword ptr [ebp-48h],edx  
017C4194  xor         edx,edx  
017C4196  mov         dword ptr [ebp-40h],edx  
017C4199  nop  
            //声明
            int number;
            string str;
            MyObject myObjet;

            //初始化
            number = 666;
017C419A  mov         dword ptr [ebp-40h],29Ah  
            str = "Hello World";
017C41A1  mov         eax,dword ptr ds:[41923ACh]  
017C41A7  mov         dword ptr [ebp-44h],eax  
            myObjet = new MyObject();
017C41AA  mov         ecx,5D911D0h  
017C41AF  call        011430F4  
017C41B4  mov         dword ptr [ebp-4Ch],eax  
017C41B7  mov         ecx,dword ptr [ebp-4Ch]  
017C41BA  call        017C3708  
017C41BF  mov         eax,dword ptr [ebp-4Ch]  
017C41C2  mov         dword ptr [ebp-48h],eax  
        }

同时我们可以看到一些注释的对应代码:

接着进入正题,我们用 WinDbg 来调试查看一下汇编代码!

(重要★★★)三. WinDbg

1.  由于要调试 x86 的32位程序,所以使用 x86 的 WinDbg 版本。目前最新版本的 WinDbg 可以从微软的官网上下载到,这里调试使用的版本为:

2. 使用 WinDbg 打开位于工程下的 Demo.exe 程序,选择 " File " -> "Open Executable "(或者按 Ctrl + E),如下图:

 选择要打开的 Demo.exe:

3. 先配置符号路径并加载符号,然后使用 " .reload " 命令

 没有提示任何的错误。然后我们加载 Demo 的符号,将 Debug 目录下面的 Demo.pdb 复制到符号目录路径下面,如下:

加载 Demo 的模块和符号,输入 ld Demo

它提示不能验证文件的检验和,我们不要去管这个提示就可以了。

4. 接着,输入命令 " sxe ld clrjit ",让 WinDbg 在载入 clrjit.dll 后中断。这一点非常重要,有以下两点好处:

  • 可以在 clrjit.dll 载入之后中断下来,以便可以在 Main 函数之前设置断点;
  • 可以防止载入 sos.dll 后出现神奇的失效的情况;

如下图:

这边 sxe 的语法是:

  • sxe ld [要载入的模块名称]
  • sxe ld:[要载入的模块名称]
  • sxe ld [要载入的模块名称.dll]
  • sxe ld:[要载入的模块名称.dll]

上面的 4 个都可以,效果是一样的。

作用:可以在加载对应模块的时候下断点。

5. 执行命令 g,继续执行程序,程序会在加载完 clrjit.dll 后中断下来:

6. 使用 " .loadby sos clr " 来载入 sos.dll

7. 用 " .chain " 来查看一下 sos.dll 是否被正确载入,如下图:

8. 由于我们知道我们程序主函数入口点的签名,所以我们直接下断点 " !bpmd Demo Demo.Program.Main" ,如下图:

注:这边在以前的 WinDbg 中是可以用模块的全名,如 " !bpmd Demo.exe Demo.Program.Main ",但是在我当前使用的最新版本的 WinDbg 中提示为

提示模块名错误。

9. 在下完断点之后,再次使用 g 来继续运行程序,让程序在进入 Main 函数时被中断:

可以看到第一行汇编代码 nop,它的虚拟地址是 00ea0873。

10. 使用 " !u 00ea0873 " 显示从这个地址开始的所有汇编代码,如下:

当前所在行的前部有 " >>> " 进行了标记。除了有一些乱码之外,别的都和 VS 中的反汇编一致,有的地方还有一些人性化的提示,如 " Hello World " 的堆地址:

" 854DE4h(MT:Demo.MyObject)" 是 854DE4h 是 MyObject 的方发表地址:

等等。

下面是 WinDbg的反汇编:

Normal JIT generated code
Demo.Program.Main(System.String[])
Begin 00ea0848, size 5d

D:\²©¿ÍÔ°ÎÄÕÂ\ÈçºÎÓÃWinDbg²é¿´CSharpÖ¸¶¨º¯ÊýµÄ»ã±à´úÂë\Demo\Demo\Program.cs @ 12:
00ea0848 55              push    ebp
00ea0849 8bec            mov     ebp,esp
00ea084b 83ec14          sub     esp,14h
00ea084e 33c0            xor     eax,eax
00ea0850 8945ec          mov     dword ptr [ebp-14h],eax
00ea0853 894dfc          mov     dword ptr [ebp-4],ecx
00ea0856 833df042850000  cmp     dword ptr ds:[8542F0h],0
00ea085d 7405            je      00ea0864
00ea085f e86cf4cf71      call    clr!CreateHistoryReader+0x749d0 (72b9fcd0) (JitHelp: CORINFO_HELP_DBG_IS_JUST_MY_CODE)
00ea0864 33d2            xor     edx,edx
00ea0866 8955f4          mov     dword ptr [ebp-0Ch],edx
00ea0869 33d2            xor     edx,edx
00ea086b 8955f0          mov     dword ptr [ebp-10h],edx
00ea086e 33d2            xor     edx,edx
00ea0870 8955f8          mov     dword ptr [ebp-8],edx
>>> 00ea0873 90              nop

D:\²©¿ÍÔ°ÎÄÕÂ\ÈçºÎÓÃWinDbg²é¿´CSharpÖ¸¶¨º¯ÊýµÄ»ã±à´úÂë\Demo\Demo\Program.cs @ 19:
00ea0874 c745f89a020000  mov     dword ptr [ebp-8],29Ah

D:\²©¿ÍÔ°ÎÄÕÂ\ÈçºÎÓÃWinDbg²é¿´CSharpÖ¸¶¨º¯ÊýµÄ»ã±à´úÂë\Demo\Demo\Program.cs @ 20:
00ea087b 8b0544238303    mov     eax,dword ptr ds:[3832344h] ("Hello World")
00ea0881 8945f4          mov     dword ptr [ebp-0Ch],eax

D:\²©¿ÍÔ°ÎÄÕÂ\ÈçºÎÓÃWinDbg²é¿´CSharpÖ¸¶¨º¯ÊýµÄ»ã±à´úÂë\Demo\Demo\Program.cs @ 21:
00ea0884 b9e44d8500      mov     ecx,854DE4h (MT: Demo.MyObject)
00ea0889 e866289aff      call    008430f4 (JitHelp: CORINFO_HELP_NEWSFAST)
00ea088e 8945ec          mov     dword ptr [ebp-14h],eax
00ea0891 8b4dec          mov     ecx,dword ptr [ebp-14h]
00ea0894 ff15044e8500    call    dword ptr ds:[854E04h] (Demo.MyObject..ctor(), mdToken: 06000003)
00ea089a 8b45ec          mov     eax,dword ptr [ebp-14h]
00ea089d 8945f0          mov     dword ptr [ebp-10h],eax

D:\²©¿ÍÔ°ÎÄÕÂ\ÈçºÎÓÃWinDbg²é¿´CSharpÖ¸¶¨º¯ÊýµÄ»ã±à´úÂë\Demo\Demo\Program.cs @ 22:
00ea08a0 90              nop
00ea08a1 8be5            mov     esp,ebp
00ea08a3 5d              pop     ebp
00ea08a4 c3              ret

11. 当然我们在 Main 函数中断的时候,也可以通过打开 " 反汇编 " 窗口进行查看,如下图:

菜单选择 " View " -> " Disassembly ",当然也可以使用快捷键 " Alt + 7 ":

打开 " 反汇编 " 窗口,当前断下的位置为紫色底显示:

最好还是使用 " !u "命令的汇编,因为会有一些友好提示,比较好分析。

12. 最后,通过命令查看一下主函数中的参数和局部变量的初始化情况,输入 " !clrstack -a ",如下图:

看到了 PARAMETERS 是 Main 函数的参数,而 LOCALS 是函数中的三个临时的局部变量,但是它们都没有被初始化,都指向 0x00000000(null)。

13. 通过使用 p 执行下一条汇编指令(单步),并且配合 " !clrstack -a " 或者 "!clrstack -l " 可以一直单步,直到三个局部变量(LOCALS)有值,如下图:

 

14. 分析:第一个 0x005aefa0 = 0x0000029a 是

int number = 666;

而后面的分别是 string 和 MyObject 的堆的地址。使用 " !dumpobj 0x02832354 " 和 " !dumpobj 0x02832378 " 来查看一下:

第一个就是

string str = “Hello World”;

而第二个就是 MyObject ,有三个 Int32 类型的字段,如下图:

三. 遇到的问题

在有的文章上说,使用 " !clrstack -i -a " 可以看到临时变量的名称的符号信息,但是我这边在调试的时候一直提示错误,这个是怎么回事能?

 不知道是不是符号服务器配置或者是WinDbg有问题,看之后能不能解决再贴在本篇下面。

 四. 工程代码

工程代码及本文 Word 版下载

posted @ 2020-02-24 19:06  霁雪湖上三映月  阅读(1163)  评论(0)    收藏  举报