Categorías
Uncategorized

Introducción a ensamblador

Cuando nosotros escribimos nuestro código fuente en un lenguaje de alto nivel este al ser compilado es traducido a código maquina el cual es ejecutado por el procesador. Cuando realizamos ingeniería inversa tomamos un ejecutable ya compilado y realizamos el desensamblado del mismo para proceder con el análisis.

¿Qué es desensamblar?

Desensamblar es el proceso de traducir el código de máquina de un programa en lenguaje ensamblador. Es decir, el proceso de convertir el código de máquina en una representación legible por humanos, que es más fácil de entender y analizar que el código de máquina.
Un desensamblador toma el archivo ejecutable del programa y lo analiza para identificar las instrucciones de código de máquina y su correspondiente representación en lenguaje ensamblador. El resultado es un archivo de texto legible por humanos que muestra el código ensamblador del programa.
Es importante destacar que el proceso de desensamblar no siempre produce un código ensamblador idéntico al código fuente original, ya que el código de máquina puede ser optimizado y reordenado por el compilador durante la compilación. Por lo tanto, el código ensamblador producido por el desensamblador puede requerir cierta interpretación y análisis para comprender completamente su funcionamiento.

¿Qué es el lenguaje ensamblador?

El ensamblador (también conocido como «Assembly» en inglés) es un lenguaje de programación de bajo nivel utilizado para escribir programas que operan directamente en la arquitectura de una computadora. El ensamblador utiliza una sintaxis que está muy cerca del lenguaje de máquina, lo que permite al programador escribir instrucciones que se traducen directamente en códigos de máquina ejecutables por el procesador.

Antes de ver las instrucciones en ensamblador debemos conocer lo que son los registros, Los registros del procesador son pequeñas áreas de almacenamiento en la CPU que se utilizan para realizar operaciones aritméticas y lógicas, y para almacenar temporalmente datos y direcciones de memoria.
Existen varios registros entre los cuales destacamos: registros de propósito general, registros de coma flotante y registros de propósito especifico.

Registros de propósito General (GPR)

Los registros de propósito general son utilizados para almacenar temporalmente datos y direcciones de memoria. Algunos de estos cumplen propósitos específicos en determinadas instrucciones.
Entre los registros de propósito general para la arquitectura de 64 bits tenemos los registros RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP y los registros desde el R8 al R15

Registros de punto Flotante

Los registros de punto flotante son utilizados para realizar operaciones matemáticas con números de punto flotante en la arquitectura de 64 bits tenemos los registros de la serie XMM, YMM y ZMM

Registros de Propósito Especifico

Entre los registros de propósito especifico tenemos los registros RFLAGS y RIP. El registro RIP es utilizado para contener la dirección de la próxima instrucción que se va a ejecutar y el registro RFLAGS es utilizado para contener el estado de la CPU y contiene diferentes banderas que indican el resultado de las operaciones entre otras cosas

Si por ejemplo el resultado de una resta nos da cero el registro RFLAGS colocara la bandera que indica que el resultado es cero (Zero Flag) a 1 indicando que el resultado de la operación anterior fue cero. O si por ejemplo el resultado de la resta da un numero negativo la bandera del registro RFLAGS que indica que el resultado es un numero negativo (Sign Flag) será colocada a 1 en caso contrario es decir el resultado dio un numero positivo la bandera que indica que el resultado es negativo será colocada a 0

Instrucciones en ensamblador

Las instrucciones del lenguaje ensamblador son el conjunto de instrucciones que son interpretadas directamente por el procesador estas las podemos clasificar en, instrucciones de movimiento de datos, instrucciones de comparación, instrucciones aritméticas, instrucciones de control de flujo, instrucciones de llamada y retorno, instrucciones de manejo de pila, instrucciones lógicas y de desplazamiento de bits

instrucciones de movimiento de datos

Las instrucciones de movimiento de datos nos permiten mover desde la fuente al destino estas pueden ser de movimiento incondicional o movimiento condicional

Instrucciones de movimiento incondicional

Las instrucciones de movimiento incondicional son instrucciones que se encargan de mover datos desde la fuente al destino incondicionalmente es decir que no tienen en cuenta los FLAGS o banderas antes de mover entre estas instrucciones tenemos LEA, MOV y XCHG

MOV

La instrucción MOV nos permite mover datos desde fuente a destino en caso de que la fuente se encuentre entre corchetes (por ejemplo, modo de direccionamiento indirecto) se calculará la dirección y se moverá el contenido de la dirección a la fuente

sintaxis

mov destino, origen

ejemplo

mov eax, 1 ; mueve el valor 1 al registro EAX
mov ebx, eax ; mueve el valor de EAX a EBX
mov [memoria], eax ; mueve el valor de EAX a la dirección de memoria indicada
mov al, [ebx] ; mueve el byte almacenado en la dirección apuntada por EBX al registro AL
mov dword ptr [eax], 0 ; mueve el valor 0 a la dirección de memoria indicada por el registro EAX

LEA

La instrucción Lea (Load effective Address) es una instrucción utilizada para calcular la dirección efectiva, esta instrucción va a calcular la dirección del segundo operando (el operando fuente) y lo va a almacenar en el primer operando (destino) es decir calculará la dirección contenida entre corchetes y moverá la dirección no el contenido de la dirección

sintaxis

lea registro, dirección_de_memoria

ejemplo

lea ebx, [memoria] ; carga la dirección de memoria en EBX
lea eax, [ebx+4] ; carga la dirección de memoria desplazada por 4 bytes de la dirección almacenada en EBX en EAX
lea edx, [ebx+eax*2] ; carga la dirección de memoria desplazada por el doble del valor almacenado en EAX desde la dirección almacenada en EBX en EDX

XCHG

Exchange Esta instrucción intercambia los contenidos de registros/memoria con registros

sintaxis

xchg destino, origen

ejemplo

xchg eax, ebx ; intercambia el contenido de los registros EAX y EBX

MOVSX

La instrucción MOVSX (move sign extended) es una instrucción que mueve desde una fuente más pequeña a un destino más grande y el espacio sobrante lo rellena con el bit de signo de la fuente

sintaxis

movsx registro_destino, origen

ejemplo

movsx eax, byte ptr [memoria] ; mueve el byte almacenado en la dirección de memoria indicada a EAX y lo extiende a 32 bits
movsx ebx, word ptr [memoria] ; mueve la palabra almacenada en la dirección de memoria indicada a EBX y lo extiende a 32 bits
movsx edx, al ; extiende el valor del registro AL (8 bits) a 32 bits y lo mueve a EDX 

MOVZX

La instrucción MOVZX (move zero extended) es una instrucción que mueve desde una fuente más pequeña (la cual puede ser un Word o byte) a un destino más grande y el espacio sobrante lo rellena con ceros

sintaxis

movzx registro_destino, origen

ejemplo

movzx eax, byte ptr [memoria] ; mueve el byte almacenado en la dirección de memoria indicada a EAX y lo extiende a 32 bits con ceros
movzx ebx, word ptr [memoria] ; mueve la palabra almacenada en la dirección de memoria indicada a EBX y lo extiende a 32 bits con ceros
movzx edx, al ; extiende el valor del registro AL (8 bits) a 32 bits con ceros y lo mueve a EDX

Instrucciones de movimiento condicional

Las instrucciones de movimiento condicional son instrucciones que verifican las banderas de estado o flags antes de mover en caso de que la condición necesaria para el movimiento no se cumpla es decir las banderas que esta verifica no están seteadas el movimiento no se realizará

instrucciones de comparación

las instrucciones de comparación realizan la comparación de dos operandos sin modificar ninguno de ellos, pero actualizando las banderas de estado dependiendo del resultado de la comparación
entre las instrucciones de comparación tenemos TEST y CMP

TEST

La instrucción test realiza la operación AND entre el primer y segundo operando sin alterar ninguno, pero modificando las banderas de estado dependiendo del resultado de la operación AND

sintaxis

test destino, origen

ejemplo

test eax, ebx ; realiza la operación AND lógica entre EAX y EBX y establece las banderas según el resultado
test al, 0xFF ; realiza la operación AND lógica entre el registro AL y el valor hexadecimal 0xFF, estableciendo las banderas según el resultado
test [memoria], 0x10 ; realiza la operación AND lógica entre el valor almacenado en la dirección de memoria indicada y el valor hexadecimal 0x10, estableciendo las banderas según el resultado

CMP

La instrucción CMP (Compare) realiza una resta entre el primer y segundo operando (operando1-operando2) actualiza las banderas de estado y no modifica ninguno de los operandos

sintaxis

cmp operando1, operando2

ejemplo

cmp eax, ebx ; compara los valores de los registros EAX y EBX y establece las banderas según el resultado
cmp al, 0xFF ; compara el valor del registro AL con el valor hexadecimal 0xFF y establece las banderas según el resultado
cmp [memoria], ebx ; compara el valor almacenado en la dirección de memoria indicada con el valor del registro EBX y establece las banderas según el resultado

instrucciones aritméticas

las instrucciones aritméticas como su nombre lo indica nos permiten realizar operaciones aritméticas tales como la suma, resta, multiplicación o división

ADD

La instrucción ADD suma el operando fuente más el operando de destino y el resultado de la suma queda almacenado en el operando de destino

sintaxis

add destino, origen

ejemplo

add eax, ebx ; suma el valor del registro EBX al valor del registro EAX y almacena el resultado en EAX
add [memoria], eax ; suma el valor del registro EAX al valor almacenado en la dirección de memoria indicada y almacena el resultado en esa dirección de memoria
add al, 0x10 ; suma el valor hexadecimal 0x10 al valor del registro AL y almacena el resultado en AL

SUB

La instrucción SUB le resta al operando de destino la fuente (destino-fuente) y el resultado de la resta es almacenado en el operando de destino

sintaxis

sub destino, origen

ejemplo

sub eax, ebx ; resta el valor del registro EBX del valor del registro EAX y almacena el resultado en EAX
sub [memoria], eax ; resta el valor del registro EAX del valor almacenado en la dirección de memoria indicada y almacena el resultado en esa dirección de memoria
sub al, 0x10 ; resta el valor hexadecimal 0x10 del valor del registro AL y almacena el resultado en AL

MUL

Esta instrucción realiza la multiplicación sin signo del operando con el registro RAX y el resultado de la multiplicación es almacenado en RDX:RAX (la parte alta en RDX y la parte baja en RAX)

sintaxis

mul operando

ejemplo

mul eax ; multiplica el valor del registro EAX por el valor del registro EAX y almacena los 32 bits más significativos del resultado en EDX y los 32 bits menos significativos en EAX
mul ebx ; multiplica el valor del registro EBX por el valor del registro EAX y almacena los 32 bits más significativos del resultado en EDX y los 32 bits menos significativos en EAX
mul dword [memoria] ; multiplica el valor almacenado en la dirección de memoria indicada por el valor del registro EAX y almacena los 32 bits más significativos del resultado en EDX y los 32 bits menos significativos en EAX

IMUL

La instrucción IMUL realiza la multiplicación de números con signo y esta puede tener uno dos o tres operandos cuando esta tiene un operando se comporta similar a MUL pero en este caso multiplicando con signo el registro RAX por el operando y el resultado se almacena en el registro RDX:RAX. Con dos operandos multiplica la fuente por el destino y el resultado se almacena en la fuente. Con tres operandos multiplica el segundo operando con el tercer operando el cual debe ser una constante y el resultado lo almacena en el primer operando

sintaxis

imul operando
imul destino, operando
imul destino, operando, const

ejemplo

imul eax, ebx ; multiplica el valor del registro EBX por el valor del registro EAX y almacena el resultado en EAX
imul ecx, dword [memoria] ; multiplica el valor almacenado en la dirección de memoria indicada por el valor del registro ECX y almacena el resultado en ECX
imul rax, rbx ; multiplica el valor del registro RBX por el valor del registro RAX y almacena el resultado en RAX

DIV

Esta instrucción realiza la división de números sin signo de los registros RDX:RAX (dividendo) entre el operando (divisor), el resultado es almacenado en el registro RAX y el residuo en el registro RDX

sintaxis

div operando

ejemplo

div ebx ; divide el valor del registro EDX:EAX (valor de 64 bits) por el valor del registro EBX y almacena el cociente en EAX y el resto en EDX
div dword [memoria] ; divide el valor almacenado en la dirección de memoria indicada por el valor del registro EAX y almacena el cociente en EAX y el resto en EDX

IDIV

Esta instrucción realiza la división con signo de los registros RDX:RAX (dividendo) entre el operando (divisor), el resultado es almacenado en el registro RAX y el residuo en el registro RDX

sintaxis

idiv operando

ejemplo

idiv ebx ; divide el valor del registro EDX:EAX (valor de 64 bits) por el valor del registro EBX y almacena el cociente en EAX y el resto en EDX
idiv dword [memoria] ; divide el valor almacenado en la dirección de memoria indicada por el valor del registro EAX y almacena el cociente en EAX y el resto en EDX

instrucciones de control de flujo

estas instrucciones nos permiten alterar el flujo del programa entre estas instrucciones tenemos las instrucciones de salto incondicional y salto condicional

instrucciones de salto incondicional

JMP

La instrucción JMP salta a la dirección de memoria especificada por el operando incondicionalmente es decir salta sin tener en cuenta las banderas o FLAGS

sintaxis

jmp destino

ejemplo

etiqueta:
; instrucciones
jmp etiqueta ; salta de vuelta a la etiqueta
; más instrucciones

Instrucciones de salto condicional

Las instrucciones de salto condicional verifican determinadas banderas y si la condición se satisface es decir las banderas que chequea están seteadas salta a la dirección indicada por el operando, en caso contrario (la condición no se satisface) la instrucción no salta

instrucciones de llamada y retorno

las instrucciones de llamada y retorno nos permiten entrar y retornar de las funciones

CALL

La instrucción CALL salta a la dirección apuntada por el operando, pero antes coloca en el stack la dirección de retorno es decir la dirección siguiente a donde fue ejecutada

sintaxis

call subrutina

ejemplo

subrutina:
    ; instrucciones
    ret ; retorna de la subrutina

call subrutina ; llama a la subrutina indicada y salta a la dirección de memoria donde comienza la subrutina

RET

La instrucción RET saltará a la dirección de retorno es decir la dirección colocada en el stack por la instrucción CALL

sintaxis

ret

ejemplo

subrutina:
    ; instrucciones
    ret ; retorna de la subrutina

call subrutina ; llama a la subrutina indicada y salta a la dirección de memoria donde comienza la subrutina 

instrucciones de manejo de pila

las instrucciones de manejo de pila nos permiten colocar y retirar valores de la pila o stack

PUSH

La instrucción PUSH coloca valores en el stack

sintaxis

push operando

ejemplo

push eax ; coloca el valor del registro eax en la pila
push 10 ; coloca el valor 10 en la pila
push dword [memoria] ; coloca el valor almacenado en la dirección de memoria "memoria" en la pila

POP

La instrucción POP retira valores del stack

sintaxis

pop destino

ejemplo

pop eax ; saca el valor de la cima de la pila y lo coloca en el registro eax
pop dword [memoria] ; saca el valor de la cima de la pila y lo coloca en la dirección de memoria "memoria"

instrucciones lógicas

Las instrucciones Lógicas nos permiten realizar operaciones lógicas entre las cuales tenemos AND, OR, NOT, XOR

AND

La instrucción AND nos permite realizar la operación lógica AND entre el operando destino y fuente y el resultado de esta operación es almacenado en el operando de destino

sintaxis

and destino, origen

ejemplo

and eax, ebx ; realiza la operación lógica "AND" entre los valores de los registros eax y ebx, y almacena el resultado en eax
and dword [memoria], 0xFF ; realiza la operación lógica "AND" entre el valor almacenado en la dirección de memoria "memoria" y la constante 0xFF, y almacena el resultado en la misma dirección de memoria

OR

La instrucción OR nos permite realizar la operación lógica OR entre el operando destino y fuente y el resultado de esta operación es almacenado en el operando de destino

sintaxis

or destino, origen

ejemplo

or eax, ebx ; realiza la operación lógica "OR" entre los valores de los registros eax y ebx, y almacena el resultado en eax
or dword [memoria], 0x7F ; realiza la operación lógica "OR" entre el valor almacenado en la dirección de memoria "memoria" y la constante 0x7F, y almacena el resultado en la misma dirección de memoria

NOT

Esta instrucción realiza la negación del operando

sintaxis

not destino

ejemplo

not eax ; realiza la operación lógica "NOT" en el valor del registro eax, y almacena el resultado en eax
not byte [memoria] ; realiza la operación lógica "NOT" en el valor almacenado en la dirección de memoria "memoria", y almacena el resultado en la misma dirección de memoria

XOR

La instrucción XOR nos permite realizar la operación lógica XOR entre el operando destino y fuente y el resultado de esta operación es almacenado en el operando de destino. Un uso frecuente de esta operación es colocar un registro a cero.

sintaxis

xor destino, origen

ejemplo

xor eax, eax ; coloca el registro eax a cero
xor eax, ebx ; realiza la operación lógica "XOR" entre los valores de los registros eax y ebx, y almacena el resultado en eax
xor byte [memoria], 0xff ; realiza la operación lógica "XOR" entre el valor almacenado en la dirección de memoria "memoria" y el valor 0xff, y almacena el resultado en la misma dirección de memoria

instrucciones de desplazamiento de bits

Las instrucciones de desplazamiento de bits como su nombre lo indica son utilizadas para desplazar bits, un uso muy frecuente es para manejar valores de campos de bits o para multiplicar o dividir entre potencias de 2

SHL y SAL

Las instrucciones SHL (Shift logical Left) y SAL (Shift artitmenical Left) desplazan os bits hacia la izquierda el número de veces indicado por la fuente, rellenando los nuevos lugares con ceros

sintaxis

shl destino, fuente
sal destino, fuente

ejemplo

mov eax, 0x00000001 ; mueve el valor hexadecimal 0x00000001 al registro eax
shl eax, 1 ; desplaza los bits en eax un bit a la izquierda
mov ebx, 0xffff0000 ; mueve el valor hexadecimal 0xffff0000 al registro ebx
sal ebx, 4 ; desplaza los bits en ebx cuatro bits a la izquierda, realizando un desplazamiento aritmético

desplazar hacia la izquierda equivale a multiplicar por una potencia de dos

SHR

La instrucción SHR (Shift logical Right) desplaza los bits hacia la derecha el número de veces indicado por la fuente, rellenando los nuevos lugares con ceros

sintaxis

shr destino, fuente

ejemplo

mov eax, 0x12345678 ; coloca el valor 0x12345678 en eax
shr eax, 4 ; desplaza 4 bits a la derecha

SAR

La instrucción SAR (Shift Aritmetical Right) desplaza los bits hacia la derecha el número de veces indicado por la fuente y rellena los nuevos lugares con el bit de signo que tenía el destino

sintaxis

sar destino, fuente

ejemplo

mov eax, 0xFFFF0000 ; coloca el valor 0xFFFF0000 en eax
sar eax, 16 ; desplaza 16 bits a la derecha

desplazar hacia la derecha equivale a dividir entre una potencia de dos

ROR y ROL

Las instrucciones ROR (Rotate right) y ROL (Rotate left) realizan una rotación de bits a la derecha e izquierda respectivamente

sintaxis

ror destino, fuente
rol destino, fuente

ejemplo

mov eax, 0x0F0F0F0F ; coloca el valor 0x0F0F0F0F en eax
ror eax, 8 ; rota los bits de eax 8 posiciones a la derecha
mov eax, 0x0F0F0F0F ; coloca el valor 0x0F0F0F0F en eax
rol eax, 8 ; rota los bits de eax 8 posiciones a la izquierda

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *