【问题标题】:.NET functions disassembled.NET 函数反汇编
【发布时间】:2011-02-10 13:42:44
【问题描述】:

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

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

函数1

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

函数2

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

函数3

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 

还是?

【问题讨论】:

  • ebp 是用于局部变量的栈帧指针,所以 mov dword ptr [ebp-4],ecx 将 ecx 存储到一个局部变量中...源代码也是如此。

标签: c# .net disassembly il


【解决方案1】:

这个序幕分为两部分。

设置堆栈帧

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

push        ebp 
mov         ebp,esp

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

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

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

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

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

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

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

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

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

这类似于if (globalVar!=0) Call Function_65CC36CF。最有可能全局 var 指示静态构造函数是否已运行,并且调用是对静态构造函数的调用。


据我所知,你拆机时的cmets是正确的。


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

【讨论】:

  • 如果你有太多,你会得到一个 Stackoverflow ;)
【解决方案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

【讨论】:

    【解决方案3】:

    如果你在谈论 push 和 mov,它只是修复了调用堆栈。我不确定这些部分的其余部分在做什么。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-23
      • 2021-03-04
      • 2010-10-10
      • 2013-01-24
      • 2010-10-09
      • 2011-08-09
      • 1970-01-01
      • 2011-08-06
      相关资源
      最近更新 更多