.NET函数被反汇编

时间:2011-02-10 13:42:44

标签: c# .net disassembly il

在反汇编.NET函数时,我注意到它们都是以类似的模式开始的。 这个初始代码做了什么?

此代码出现在函数应该执行的实际代码之前。 它是某种参数计数验证吗?

func1的

private static void Foo(int i)
{
   Console.WriteLine("hello");
}

00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  push        eax 
00000004  mov         dword ptr [ebp-4],ecx 
00000007  cmp         dword ptr ds:[005C14A4h],0 
0000000e  je          00000015 
00000010  call        65E0367F 
//the console writleline code follows here and is not part of the question

FUNC2

static private void Bar()
{
   for (int i = 0; i < 1000; i++)
   {
      Foo(i);
   }
}

00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  push        eax 
00000004  cmp         dword ptr ds:[006914A4h],0 
0000000b  je          00000012 
0000000d  call        65CC36CF 
// the for loop code follows here

FUNC3

private static void Foo()
{
   Console.WriteLine("hello");
}

00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  cmp         dword ptr ds:[005614A4h],0 
0000000a  je          00000011 
0000000c  call        65E3367F 

[编辑] 这是对它的正确描述吗?

//fix stackframe
00000000  push        ebp 
00000001  mov         ebp,esp 
//store eax so it can be used locally
00000003  push        eax 
//ensure static ctor have been called
00000004  cmp         dword ptr ds:[006914A4h],0 
//it has been called, ignore it
0000000b  je          00000012
//it hasn't been called, call it now 
0000000d  call        65CC36CF 

3 个答案:

答案 0 :(得分:19)

本序言分为两部分。

设置堆栈框架

这会将当前EBP寄存器存储在堆栈中,然后将堆栈指针(ESP)的值分配给EBP。

push        ebp 
mov         ebp,esp

如果堆栈中存在局部变量(即寄存器中没有足够的空间可用),那么ESP将按其大小移动以构建当前函数的堆栈帧。

在函数结束时,您将看到这些操作相反,因此前一个函数的堆栈帧将被恢复。

EBP应始终指向当前函数
的堆栈帧的开头 ESP到最后(在x86上的地址较低,因为堆栈向下增长)。

这是常见调用约定的一部分,在抛出异常时需要进行堆栈展开。这不是.net特定的,并且被大多数调用约定用于windows / x86。

设置堆栈帧后,它常用于在堆栈中存储一些寄存器。那是因为您可能希望将某些寄存器用作临时变量,但调用约定需要您的函数来保存它们。所以你需要将它们备份到堆栈中。哪些寄存器必须保留,哪些可以修改取决于您使用的调用约定。

当引用堆栈上的局部变量时,可以使用[ebp-x],其中ebp指向堆栈帧的开头,x是一个偏移量,表示存储变量的堆栈帧中的位置。或者,您可以使用[esp+y]与堆栈帧末尾的偏移量。

调用静态构造函数/初始化程序

由于danbystrom注意到第二部分很可能是对静态构造函数/初始化程序的调用。由于静态构造函数不是在程序启动时调用,而是在第一次访问时调用,因此抖动无法保证静态构造函数已经执行的每次访问都需要检查它是否已被调用,然后如果没有则调用它。 / p>

00000004  cmp         dword ptr ds:[006914A4h],0 
0000000b  je          00000012 
0000000d  call        65CC36CF

这就像if (globalVar!=0) Call Function_65CC36CF。最可能全局变量指示静态构造函数是否已运行,并且该调用是对静态构造函数的调用。


据我所知,您对反汇编的评论是正确的。


在stackframes上查看此OldNewThing博客条目:How to rescue a broken stack trace: Recovering the EBP chain

答案 1 :(得分:2)

由于您的方法都是静态的:代码用于检查类的静态初始化程序是否尚未执行。

00000007  cmp    dword ptr ds:[005C14A4h],0  ; test if static initializer has executed
0000000e  je     00000015                    ; skip call to initializer if already done
00000010  call   65E0367F                    ; call static initializer
00000015  ....                               ; continue with the method's code

答案 2 :(得分:0)

如果你正在谈论push和mov,它只是修复了callstack。我不确定这些细分市场的其余部分是什么作用。