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).
Respuestas:
Los NOP tienen varios propósitos:
fuente
Así es como la depuración usa los nops MSIL / CIL ( no el código de máquina x86
nop
):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.
fuente
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.
fuente
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.
fuente
¡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.
fuente
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.
fuente
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.
fuente
Un uso poco ortodoxo son los NOP-Slides , utilizados en exploits de desbordamiento de búfer.
fuente
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.
fuente
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.
fuente
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. .
fuente
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).
fuente
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.
fuente
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.
fuente
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.
fuente
¿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.
fuente
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.
fuente
Usé NOP para ajustar automáticamente la latencia acumulada después de ingresar un ISR. Muy útil para clavar la sincronización.
fuente
nop
será útil en la carga útil de explotación de corrupción de memoria. Por supuesto,nop
es similar axchg eax, eax
fuente