portable net ilspy c# .net disassembly il

c# - net - ilspy



Funciones.NET desmontadas (3)

Dado que todos sus métodos son estáticos: el código se usa para verificar si el inicializador estático de la clase todavía se ha ejecutado.

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

Al desensamblar las funciones de .NET, observo que todas comienzan con un patrón similar. ¿Qué hace este código inicial?

Este código aparece antes del código real para lo que se supone que la función debe hacer. ¿Es algún tipo de verificación de conteo de parámetros?

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

[Editar] ¿Entonces esta es una descripción correcta de esto?

//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

¿o?


Este prólogo tiene dos partes.

Configurando el marco de pila

Esto almacena el registro EBP actual en la pila y luego asigna el valor del apilador (ESP) a EBP.

push ebp mov ebp,esp

Si hay variables locales que están almacenadas en la pila (es decir, no hay suficiente espacio en los registros disponibles), se moverá ESP por su tamaño para construir el cuadro de pila de la función actual.

Y al final de la función verá estas operaciones invertidas, por lo que se restaurará el marco de pila de la función anterior.

EBP siempre debe apuntar al comienzo de la pila de la función actual
ESP hasta el final (que tiene una dirección más baja en x86 porque la pila crece hacia abajo).

Esto es parte de las convenciones de llamadas comunes y es necesario para el despliegue de la pila cuando se lanza una excepción. Esto no es específico para .net y lo usan la mayoría de las convenciones de llamada en Windows / x86.

Después de configurar el fotograma de la pila, es común almacenar algunos registros en la pila. Esto se debe a que es posible que desee utilizar ciertos registros como variables temporales, pero la convención de llamada requiere que su función los conserve. Así que necesitas respaldarlos en la pila. Los registros que deben conservarse y los que se pueden modificar dependen de la convención de llamada que utilice.

Al referirse a las variables locales en la pila, puede usar [ebp-x] donde ebp apunta al comienzo del cuadro de pila, y x es un desplazamiento que indica dónde se almacena la variable en el cuadro de pila. Alternativamente, puede usar [esp+y] con un desplazamiento desde el final del cuadro de apilamiento.

Llamada al constructor estático / inicializador.

Como danbystrom notó, la segunda parte es probablemente la llamada a un constructor / inicializador estático. Dado que no se llama al constructor estático en el inicio del programa, sino en el primer acceso, todo acceso para el que el jitter no pueda garantizar que el constructor estático ya se haya ejecutado debe verificar si se ha llamado y, a continuación, lo llama, en caso contrario.

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

Esto es algo así como if (globalVar!=0) Call Function_65CC36CF . Donde lo más probable es que la var global indique si el constructor estático se ha ejecutado, y la llamada es una llamada al constructor estático.

Por lo que sé, tus comentarios sobre el desmontaje son correctos.

Consulte esta entrada de blog de OldNewThing en cuadros de pila: Cómo rescatar un seguimiento de pila roto: Recuperar la cadena EBP


Si estás hablando sobre el empuje y el mov, simplemente está arreglando la pila de llamadas. No estoy seguro de lo que el resto de esos segmentos está haciendo fuera de mi cabeza.