assembly - que - clr
¿Cuál es el propósito del opcode de nop? (19)
Voy a través de MSIL y me doy cuenta de que hay muchas instrucciones de nop . El artículo de MSDN dice que no toman ninguna medida y se utilizan para llenar el espacio si el código de operación está parchado. Se usan mucho más en compilaciones de depuración que versiones de lanzamiento. Sé que este tipo de declaraciones se utilizan en los lenguajes ensambladores para garantizar que un código de operación se ajuste a un límite de palabras, pero ¿por qué es necesario en MSIL?
¡Tipo! ¡No-operativo es increíble! Es una instrucción que no hace más que consumir tiempo. En las oscuras edades oscuras lo usarías para hacer microajustes en el tiempo en bucles críticos o, más importante aún, como relleno en el código de auto modificación.
¿Los compiladores .NET alinean la salida de MSIL? Me imagino que podría ser útil para acelerar el acceso a la IL ... Además, tengo entendido que está diseñado para ser portátil y que se requieren accesos alineados en algunas otras plataformas de hardware.
50 años demasiado tarde, pero oye.
Los Nop son útiles si está escribiendo código de ensamblado a mano. Si tuviera que eliminar el código, podría anotar los códigos de operación anteriores.
Similarmente, podría insertar un nuevo código al sobrescribir algún opcode y saltar a otro lugar. Allí colocas los códigos de operación sobrescritos e insertas tu nuevo código. Cuando estás listo, saltas hacia atrás.
A veces tenías que usar las herramientas que estaban disponibles. En algunos casos, este fue 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 sentido.
Así es cómo se utilizan los nops por depuración:
Los compiladores de lenguaje (C #, VB, etc.) utilizan Nops para definir puntos de secuencia implícitos. Éstos le dicen al compilador JIT dónde asegurarse de que las instrucciones de la máquina se puedan mapear a las instrucciones IL.
La entrada del blog de Rick Byer en DebuggingModes.IgnoreSymbolStoreSequencePoints , explica algunos de los detalles.
C # también coloca Nops después de las instrucciones de la 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.
Como dijo ddaa, los nops te permiten contabilizar la varianza en la pila, de modo que cuando sobrescribes la dirección de retorno salta al trineo nop (muchos nops en una fila) y luego golpea el código ejecutable correctamente, en lugar de saltar a algunos byte en la instrucción que no es el comienzo.
En la escena de descifrado de software, un método clásico para desbloquear una aplicación sería parchar con un NOP la línea que busca la clave o el registro o período de tiempo o lo que sea, así no haría nada y simplemente continuar iniciando la aplicación como si estuviera registrada .
En un procesador en el que trabajé recientemente (durante cuatro años), NOP se utilizó para garantizar que la operación anterior finalizara antes de que comenzara la siguiente operación. 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 agregar.
Otro uso fue completar las unidades de ejecución, como los vectores de interrupción que tenían que ser de 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 puso NOP allí si necesario.
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 bifurcación , si no lograba llenarlo con una instrucción que de otro modo sería útil.
La primera asamblea que aprendí fue SPARC, así que estoy familiarizado con la ranura de retardo de rama, si no puede completarla con otra instrucción, por lo general, la instrucción que iba a poner encima de la instrucción de bifurcación o incrementar un contador en bucles, usted usa un NOP.
No estoy familiarizado con las grietas, pero creo que es común sobrescribir la pila usando NOP, por lo que no debe calcular exactamente dónde comienza su función maliciosa.
Los NOP sirven para varios propósitos:
- Permiten que el depurador coloque un punto de interrupción en una línea, incluso si se combina con otros en el código generado.
- Permite que el cargador aplique un parche a un salto con una compensación de objetivo de diferente tamaño.
- Permite que un bloque de código se alinee en un límite particular, lo que puede ser bueno para el almacenamiento en caché.
- Permite el enlace incremental para sobrescribir trozos de código con una llamada a una nueva sección sin tener que preocuparse por la función general que cambia el tamaño.
Nop es esencial en la carga de los exploits de desbordamiento de búfer.
Permiten que el enlazador reemplace una instrucción más larga (típicamente salto largo) por una más corta (salto corto). El NOP toma el espacio extra: el código no se podía mover ya que evitaría que otros saltos funcionaran. Esto ocurre en tiempo de enlace, por lo que el compilador no puede saber si un salto largo o corto sería apropiado.
Al menos, ese es uno de sus usos tradicionales.
Podrían estar usándolos para ayudar a editar y continuar durante la depuración. Proporciona al depurador espacio para trabajar para reemplazar el código anterior por nuevo sin cambiar los desplazamientos, etc.
Proporciona una oportunidad para los marcadores basados en línea (por ejemplo, puntos de interrupción) en el código donde una versión de lanzamiento no emitiría ninguno.
También he visto NOPs en el código que se modifica para ofuscar lo que hace como marcador de posición (protección de copia antigua).
También puede hacer que el código se ejecute más rápido, al optimizar para procesadores o arquitecturas específicos:
Los procesadores durante mucho tiempo emplean varias tuberías que funcionan aproximadamente en paralelo, por lo que se pueden exceder 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 solo admite un subconjunto. Además, hay algunas paradas entre las tuberías cuando uno tiene que esperar el resultado de una instrucción previa que aún no ha finalizado.
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.
Un uso algo poco ortodoxo son los NOP-Slides , que se utilizan en los exploits de desbordamiento de búfer.
Un uso clásico para ellos es para que su depurador siempre pueda asociar una línea de código fuente con una instrucción IL.
Usé NOP para ajustar automágicamente la latencia acumulada después de ingresar un ISR. Muy útil para clavar el tiempo muerto.