Ha pasado un año más o menos desde la última vez que tomé una clase de ensamblaje. En esa clase, estábamos usando MASM con las bibliotecas Irvine para facilitar la programación.
Después de haber revisado la mayoría de las instrucciones, dijo que la instrucción NOP esencialmente no hacía nada y no preocuparse por usarla. De todos modos, se trataba de medio término y él tiene un código de ejemplo que no se ejecuta correctamente, por lo que nos dijo que agreguemos una instrucción NOP y funcionó bien. Le pregunté por qué después de clase y qué hizo realmente, y él dijo que no lo sabía.
Alguien sabe?
Respuestas:
Muchas veces
NOP
se usa para alinear direcciones de instrucciones. Esto generalmente se encuentra, por ejemplo, al escribir Shellcode para explotar el desbordamiento del búfer o formatear la vulnerabilidad de la cadena .Supongamos que tiene un salto relativo a 100 bytes hacia adelante y realice algunas modificaciones al código. Lo más probable es que tus modificaciones alteren la dirección del objetivo del salto y, como tal, también deberías cambiar el salto relativo mencionado anteriormente. Aquí, puede agregar
NOP
s para avanzar la dirección de destino. Si tiene múltiplesNOP
s entre la dirección de destino y la instrucción de salto, puede eliminar lasNOP
s para tirar de la dirección de destino hacia atrás.Esto no sería un problema si está trabajando con un ensamblador que admite etiquetas. Simplemente puede hacer
JXX someLabel
(donde JXX es un salto condicional) y el ensamblador reemplazará elsomeLabel
con la dirección de esa etiqueta. Sin embargo, si simplemente modifica el código de máquina ensamblado (los códigos de operación reales) a mano (como a veces sucede al escribir código de shell), también debe cambiar la instrucción de salto manualmente. O lo modifica, o luego mueve la dirección del código de destino usandoNOP
s.Otro caso de uso para la
NOP
instrucción sería algo llamado trineo NOP . En esencia, la idea es crear una gama lo suficientemente grande de instrucciones que no causen efectos secundarios (comoNOP
o incrementando y luego decrementando un registro) pero aumenta el puntero de instrucción. Esto es útil, por ejemplo, cuando se quiere saltar a un determinado código cuya dirección no se conoce. El truco consiste en colocar dicho trineo NOP frente al código objetivo y luego saltar a algún lugar hacia dicho trineo. Lo que sucede es que, con suerte, la ejecución continúa desde la matriz que no tiene efectos secundarios y atraviesa instrucciones por instrucción hasta que alcanza el código deseado. Esta técnica se usa comúnmente en las vulnerabilidades de desbordamiento de búfer mencionadas anteriormente y especialmente para contrarrestar medidas de seguridad como ASLR .Aún otro uso particular para la
NOP
instrucción es cuando uno está modificando el código de algún programa. Por ejemplo, puede reemplazar partes de saltos condicionales conNOP
sy, como tal, eludir la condición. Este es un método de uso frecuente al " descifrar " la protección de copia del software. Como mínimo, se trata de eliminar la construcción del código de ensamblaje para laif(genuineCopy) ...
línea de código y reemplazar las instrucciones conNOP
s y .. ¡Voilà! ¡No se realizan controles ni copias no originales!Tenga en cuenta que, en esencia, ambos ejemplos de shellcode y cracking hacen lo mismo; modifique el código existente sin actualizar las direcciones relativas de las operaciones que dependen del direccionamiento relativo.
fuente
Se puede usar un nop en un intervalo de retraso cuando no se puede reordenar ninguna otra instrucción para colocarla allí.
En MIPS, esto sería un error porque en el momento en que jr estaba leyendo el registro v0, el registro v0 aún no se ha cargado con el valor de la instrucción anterior.
La forma de solucionar esto sería:
Esto llena los espacios comerciales después de la palabra de carga y las instrucciones de registro de salto con un nop para que la instrucción de palabra de carga se complete antes de que se ejecute el comando de registro de salto.
Lectura adicional: un poco sobre el llenado SPARC de las ranuras de retraso . De ese documento:
Tenga en cuenta la tercera opción en qué poner en la ranura de retraso. El error que viste probablemente era alguien que llenaba una de las cosas que no se deben poner en la ranura de retraso. Poner un nop en esa ubicación solucionaría el error.
Nota: después de volver a leer la pregunta, esto fue para x86, que no tiene ranuras de retraso (la bifurcación en cambio solo detiene la canalización). Entonces esa no sería la causa / solución del error. En los sistemas RISC, esa podría haber sido la respuesta.
fuente
Al menos una razón para usar NOP es la alineación. Los procesadores x86 leen los datos de la memoria principal en bloques bastante grandes, y el inicio del bloque para leer siempre está alineado, por lo que si uno tiene un bloque de código, se leerá mucho, este bloque debe estar alineado. Esto dará como resultado una pequeña aceleración.
fuente
0x1002
, porque todavía hay 14 bytes de instrucciones en el bloque alineado de 16B que contiene la dirección de destino, pero no está bien saltar0x099D
.Un propósito para NOP (en ensamblaje general, no solo x86) es introducir demoras de tiempo. Por ejemplo, desea programar un microcontrolador que debe emitir a algunos LED con un retraso de 1 s. Este retraso puede implementarse con NOP (y sucursales). Por supuesto, podría usar ADD u otra cosa, pero eso haría que el código sea más ilegible; o tal vez necesites todos los registros.
fuente
loop
instrucción para bucles de retardo , no NOP.En general, en el 80x86, no se requieren instrucciones de NOP para la corrección del programa, aunque ocasionalmente en algunas máquinas un NOP colocado estratégicamente puede hacer que el código se ejecute más rápidamente. En el 8086, por ejemplo, el código se buscaría en fragmentos de dos bytes, y el procesador tenía un búfer interno de "captación previa" que podía contener tres de estos fragmentos. Algunas instrucciones se ejecutarían más rápido de lo que se podrían obtener, mientras que otras instrucciones tardarían un tiempo en ejecutarse. Durante las instrucciones lentas, el procesador intentaría llenar el búfer de captación previa, de modo que si las siguientes instrucciones fueran rápidas, podrían ejecutarse rápidamente. Si la instrucción que sigue a la instrucción lenta comienza en un límite de palabra par, se buscarán los siguientes seis bytes de instrucciones; si comienza en un límite de bytes impar, solo se capturarán previamente cinco bytes.
Tales problemas de alineación de memoria pueden afectar la velocidad del programa, pero generalmente no afectarán la corrección. Por otro lado, hay algunos problemas relacionados con la captación previa en aquellos procesadores más antiguos en los que un NOP podría afectar la corrección. Si una instrucción altera un byte de código que ya se ha obtenido previamente, el 8086 (y creo que el 80286 y el 80386) ejecutarán la instrucción obtenida previamente aunque ya no coincida con lo que hay en la memoria. Agregar un NOP o dos entre la instrucción que altera la memoria y el byte de código que se altera puede evitar que el byte de código se recupere hasta que se haya escrito. Tenga en cuenta, por cierto, que muchos esquemas de protección de copia explotan este tipo de comportamiento; tenga en cuenta también, sin embargo, que este comportamiento no está garantizado. Las diferentes variaciones del procesador pueden manejar la captación previa de manera diferente, algunos pueden invalidar bytes capturados previamente si se modifica la memoria de la cual fueron leídos, y las interrupciones generalmente invalidarán el búfer de captación previa; el código se volverá a buscar cuando vuelvan las interrupciones.
fuente
Hay un caso específico x86 que aún no se describe en otras respuestas: manejo de interrupciones. Para algunos estilos, puede haber secciones de código cuando las interrupciones están deshabilitadas porque el código principal funciona con algunos datos compartidos con los manejadores de interrupciones, pero es razonable permitir interrupciones entre dichas secciones. Si uno ingenuamente escribe
esto no procesará interrupciones pendientes porque, citando a Intel:
así que esto se reescribirá al menos como:
En la segunda variante, todas las interrupciones pendientes se procesarán solo entre NOP y CLI. (Por supuesto, puede haber muchas variantes alternativas, como duplicar la instrucción STI. Pero el NOP explícito es más obvio, al menos para mí).
fuente
NOP significa Sin Operación
Generalmente se usa para insertar o eliminar código de máquina o para retrasar la ejecución de un código en particular.
También utilizado por crackers y depuradores para establecer puntos de interrupción.
Entonces, probablemente haciendo algo como: XCHG BX, BX también dará como resultado lo mismo.
Me parece que hubo pocas operaciones que todavía estaban en proceso y, por lo tanto, causó un error.
Si está familiarizado con VB, puedo darle un ejemplo:
Si crea un sistema de inicio de sesión en vb y carga 3 páginas juntas: Facebook, YouTube y Twitter en 3 pestañas diferentes.
Y use 1 botón de inicio de sesión para todos. Podría dar un error si su conexión a Internet es lenta. Lo que significa que una de las páginas aún no se ha cargado. Así que ponemos Application.DoEvents para superar esto. De la misma manera en el ensamblaje se puede usar NOP.
fuente