¿Cuál es el propósito del código de operación CIL nop?

82

Estoy revisando MSIL y notando que hay muchas instrucciones nop en MSIL.

El artículo de MSDN dice que no realizan ninguna acción y se utilizan para llenar el espacio si el código de operación está parcheado. Se utilizan mucho más en versiones de depuración que en versiones de lanzamiento.

Sé que este tipo de declaraciones se utilizan en lenguajes ensambladores para alinear instrucciones posteriores, pero ¿por qué se necesitan los nops de MSIL en MSIL?

(Nota del editor: la respuesta aceptada se refiere a los NOP de código máquina, no a los NOP de MSIL / CIL sobre los que se preguntó originalmente).

Dan Goldstein
fuente
19
Existe una confusión masiva en estas respuestas entre la instrucción nop de MSIL emitida por el compilador del lenguaje en el ensamblado y las instrucciones nop x86 (en esa plataforma) emitidas por el compilador JIT cuando se ejecuta el ensamblado. [De hecho, la respuesta aceptada es sobre x86 nops y no tiene relación con MSIL.] Idealmente, esta pregunta debería dividirse en 2 preguntas diferentes: propósito de MSIL :: nop? y el propósito de la plataforma nativa nop?
Steve Steiner

Respuestas:

107

Los NOP tienen varios propósitos:

  • Permiten al depurador colocar un punto de interrupción en una línea incluso si se combina con otros en el código generado.
  • Permite al cargador parchear un salto con un desplazamiento de objetivo de diferente tamaño.
  • Permite alinear un bloque de código en un límite particular, lo que puede ser bueno para el almacenamiento en caché.
  • Permite el enlace incremental para sobrescribir fragmentos de código con una llamada a una nueva sección sin tener que preocuparse por el cambio de tamaño de la función general.
Anthony Williams
fuente
De Wikipedia : "Un NOP se usa más comúnmente para propósitos de sincronización, para forzar la alineación de la memoria, para prevenir peligros, para ocupar una ranura de retardo de bifurcación, para anular una instrucción existente, como un salto, o como un marcador de posición para ser reemplazado por instrucciones activas más adelante en el desarrollo del programa (o para reemplazar las instrucciones eliminadas cuando la refactorización sería problemática o llevaría mucho tiempo). En algunos casos, un NOP puede tener efectos secundarios menores; por ejemplo, en la serie de procesadores Motorola 68000, el NOP opcode provoca una sincronización de la canalización ".
mbomb007
10

Así es como la depuración usa los nops MSIL / CIL ( no el código de máquina x86nop ):

Los compiladores de lenguajes (C #, VB, etc.) utilizan nops para definir puntos de secuencia implícitos. Estos le indican al compilador JIT dónde asegurarse de que las instrucciones de la máquina se puedan asignar de nuevo a las instrucciones de IL.

La entrada del blog de Rick Byer sobre DebuggingModes.IgnoreSymbolStoreSequencePoints , explica algunos de los detalles.

C # también coloca Nops después de las instrucciones de llamada para que la ubicación del sitio de retorno en la fuente sea la llamada en lugar de la línea después de la llamada.

Steve Steiner
fuente
C # también coloca Nops después de las instrucciones de llamada para que la ubicación del sitio de retorno en la fuente sea la llamada en lugar de la línea después de la llamada. No estoy seguro de si entiendo esto. ¿Tiene alguna referencia disponible?
user492238
8

Proporciona una oportunidad para marcadores basados ​​en líneas (por ejemplo, puntos de interrupción) en el código donde una versión de versión no emitiría ninguno.

arpón
fuente
¿Los puntos de interrupción deben estar en un nop? ¿Por qué no poner un punto de interrupción en el código de operación normal?
Dan Goldstein
4
muchos códigos de operación regulares se optimizan en las versiones de lanzamiento. Eso estropearía sus puntos de interrupción a menos que hubiera un marcador de posición nop allí al que los puntos de interrupción todavía señalarían
Jimmy
1
En las compilaciones de depuración, también se usan para proporcionar una instrucción para interrumpir donde el código no tiene instrucción. Abriendo tirantes, por ejemplo.
Greg D
1
Es posible que desee agregar una referencia a blogs.msdn.com/oldnewthing/archive/2007/08/17/4422794.aspx como fuente.
Greg D
1
Bueno, gracias a Greg y Jimmy por convertir esto en una respuesta real.
harpo
6

También puede hacer que el código se ejecute más rápido, cuando se optimiza para procesadores o arquitecturas específicas:

Durante mucho tiempo, los procesadores emplean varias canalizaciones que funcionan aproximadamente en paralelo, por lo que se pueden superar dos instrucciones independientes al mismo tiempo. En un procesador simple con dos tuberías, el primero puede admitir todas las instrucciones, mientras que el segundo admite solo un subconjunto. Además, hay algunas paradas entre las tuberías cuando uno tiene que esperar el resultado de una instrucción anterior que aún no ha terminado.

En estas circunstancias, un nop dedicado puede forzar la siguiente instrucción en una tubería específica (la primera, o no la primera), y mejorar el emparejamiento de las siguientes instrucciones para que el costo del nop sea ​​más que amortizado.

Peterchen
fuente
5

¡Tipo! ¡No-op es increíble! Es una instrucción que no hace más que consumir tiempo. En la tenue edad oscura, lo usaría para hacer microajustes en la sincronización en bucles críticos o, lo que es más importante, como relleno en código auto modificable.

pedestal
fuente
Entiendo su uso cuando se ejecuta directamente en el hardware, pero MSIL está JIT.
Dan Goldstein
MSIL solo está JIT en sistemas que tienen JIT; MSIL no exige JIT.
zócalo
5

En un procesador en el que trabajé recientemente (durante cuatro años) se usó NOP para asegurarse de que la operación anterior terminara antes de que comenzara la siguiente. Por ejemplo:

cargar valor para registrar (toma 8 ciclos) nop 8 agregar 1 para registrar

Esto aseguró que el registro tuviera el valor correcto antes de la operación de adición.

Otro uso fue para completar las unidades de ejecución, como los vectores de interrupción que tenían que tener un cierto tamaño (32 bytes) porque la dirección para vector0 era, digamos 0, para el vector 1 0x20 y así sucesivamente, por lo que el compilador ponía NOP allí si necesario.

Makis
fuente
4

Podrían estar usándolos para admitir editar y continuar durante la depuración. Proporciona al depurador espacio para trabajar y reemplazar el código antiguo por el nuevo sin cambiar las compensaciones, etc.

Rob Walker
fuente
4
Implementé el soporte del depurador en VS para Editar y continuar (no implementé CLR o partes del compilador). Los nops son parte de la historia para garantizar las asignaciones correctas de la versión antigua a la nueva de un método (específicamente en los casos de saltos del código de manejo de excepciones). Sin embargo, la sustitución del MSIL se realiza en función de la función completa. Para el código administrado por CLR no es necesario "dejar espacio" en el msil para lograr esa parte. Lo que dice aquí es correcto con respecto a la edición nativa y continuar.
Steve Steiner
4

Un uso poco ortodoxo son los NOP-Slides , utilizados en exploits de desbordamiento de búfer.

mdm
fuente
estaba buscando esto :)
Suraj Jain
Corrija su enlace, no funciona, asegúrese siempre de haber enviado el enlace del archivo web, porque no darán 404 no encontrado.
Suraj Jain
Actualmente trabajando en shellcode y quería más contexto sobre NOP. Tuve que retroceder un poco, pero aquí hay un archivo: web.archive.org/web/20110124015428/http://www.phreedom.org:80/…
saniboy
4

50 años demasiado tarde, pero bueno.

Los Nop son útiles si está escribiendo código ensamblador a mano. Si tuviera que eliminar el código, podría eliminar los códigos de operación antiguos.

De manera similar, puede insertar un nuevo código sobrescribiendo algún código de operación y saltar a otro lugar. Allí coloca los códigos de operación sobrescritos e inserta su nuevo código. Cuando esté listo, salte hacia atrás.

A veces tenía que usar las herramientas disponibles. En algunos casos, esto era solo un editor de código de máquina muy básico.

Hoy en día con los compiladores las técnicas ya no tienen ningún sentido.

Sapo
fuente
3

Un uso clásico para ellos es que su depurador siempre pueda asociar una línea de código fuente con una instrucción IL.

Larry OBrien
fuente
Dado que msil se compila con JIT cuando se ejecuta, es posible perder esa asignación (por ejemplo, la instrucción nativa no tiene una instrucción MSIL única). Los NOP se utilizan como un mecanismo de comunicación desde el compilador del lenguaje al compilador JIT para preservar dicha asignación.
Steve Steiner
3

En la escena del descifrado de software, un método clásico para desbloquear una aplicación sería parchear con un NOP la línea que verifica la clave o el registro o el período de tiempo o cualquier otra cosa para que no haga nada y simplemente continúe iniciando la aplicación como si estuviera registrada. .

shoosh
fuente
5
Estoy bastante seguro de que las instrucciones no operativas no se inventaron para ayudar a las personas a piratear software :-)
Simon Howard
3

También he visto NOP en el código que se modifica para ofuscar lo que hace como marcador de posición (protección de copia muy antigua).

TheMarko
fuente
3

Como dijo ddaa, los nops le permiten tener en cuenta la variación en la pila, de modo que cuando sobrescribe la dirección de retorno, salta al trineo nop (muchos nops seguidos) y luego ingresa al código ejecutable correctamente, en lugar de saltar a algunos byte en la instrucción que no es el comienzo.

Alex Gartrell
fuente
1

Permiten que el enlazador reemplace una instrucción más larga (normalmente salto largo) por una más corta (salto corto). El NOP ocupa el espacio adicional: el código no se puede mover porque evitaría que funcionen otros saltos. Esto sucede en el momento del enlace, por lo que el compilador no puede saber si sería apropiado un salto largo o corto.

Al menos, ese es uno de sus usos tradicionales.

MarkR
fuente
1

Esta no es una respuesta a su pregunta específica, pero en los viejos tiempos podía usar un NOP para llenar una ranura de retardo de sucursal , si no lograba llenarla con una instrucción útil.

Chris Conway
fuente
1

¿Los compiladores de .NET alinean la salida de MSIL? Me imagino que podría ser útil para acelerar el acceso al IL ... Además, tengo entendido que está diseñado para ser portátil y se requieren accesos alineados en algunas otras plataformas de hardware.

Brian Knoblauch
fuente
1

El primer ensamblado que aprendí fue SPARC, así que estoy familiarizado con la ranura de retardo de la rama, si no puede llenarla con otra instrucción, generalmente la instrucción que iba a poner encima de la instrucción de la rama o incrementar un contador en bucles, usa un NOP.

No estoy familiarizado con el craqueo, pero creo que es común sobrescribir la pila usando NOP, por lo que no tiene que calcular exactamente dónde comienza su función maliciosa.

domingo
fuente
1

Usé NOP para ajustar automáticamente la latencia acumulada después de ingresar un ISR. Muy útil para clavar la sincronización.


fuente
-1

nopserá útil en la carga útil de explotación de corrupción de memoria. Por supuesto, nopes similar axchg eax, eax

No estrella
fuente
Otra respuesta ya mencionó diapositivas NOP para exploits de inyección de código. También lo hizo otra respuesta a esta misma pregunta. Esta respuesta no menciona cómo son útiles y no es obvio si aún no lo sabe.
Peter Cordes