Fundamentos de Arquitectura 2

Nivel Principiante

Continuando con lo visto en el post anterior esta vez hablaremos de otros temas que nos servirán para buffer overflows, shellcode, análisis de malware, entre muchas cosas más. La teoría es algo aburrida pero es necesaria para luego entrar a la parte divertida.

Proceso de memoria

Cuando un proceso se ejecuta, este es organizado en la memoria tal como se muestra:

Proceso Memoria

El proceso es dividido en cuatro regiones: Text, Data, Heap y Stack.

La parte Text, o segmento de instrucción, es fijada por el programa y contiene el código del programa (instrucciones). Esta región es marcada como read-only, debido a que el programa no debería cambiar durante la ejecución.

La región Data es dividida en inicializada y no inicializada. Datos inicializados incluyen items tales como variables declaradas que son predefinidas y pueden ser modificadas. Datos no inicializados, llamados Block Started by Symbol (BSS), también inicializa variables que son inicializadas a cero o no tiene inicialización explicita (por ejemplo static int x).

El siguiente es Heap e inicia después del segmento BSS. Durante su ejecución el programa puede solicitar mas espacio en memoria mediante llamadas al sistema vía brk y sbrk, y utilizados por malloc, realloc y free. Por lo tanto el tamaño de los datos de esta región puede ser extendido. En otros post profundizamos sobre este tema.

La ultima región de la memoria es Stack, la cual es la mas importante para nuestros propósitos.

Pila (Stack)

La pila es un bloque de memoria tipo LIFO (Last-in-First-out). Esta ubicada en la parte mas alta de la memoria. La pila puede ser usada para guardar las direcciones de retorno de una función, pasar los argumentos de una funcion, almacenar variables locales, entre otros. El propósito del registro ESP (Stack Pointer) es identificar la parte superior de la pila y si es modificada cada vez que un valor ingresa pushed in (PUSH), o sale popped out (POP).

Antes de ver como la pila trabaja y como operar en ella, es importante entender como la pila crece. La pila puede crecer hacia arriba, hacia las direcciones de memoria mas altas y también puede crecer hacia abajo, hacia las direcciones de memoria mas bajas.

En resumen, la pila es una estructura LIFO y las operaciones mas fundamentales son PUSH y POP. El puntero principal para estas operaciones es ESP, el cual contiene las direcciones de memoria para lo mas alto de la pila y cambia durante cada operación PUSH y POP.

instrucción PUSH: PUSH E

Proceso PUSH: Un PUSH es ejecutado, y el registro ESP es modificado.

Valor de inicio: ESP apunta a lo mas alto de la pila

Proceso: La instrucción PUSH resta 4 (en 32 bits) u 8 (en 64 bits) del ESP y escribe los datos a la dirección de memoria del ESP, luego actualiza el ESP a lo mas alto de la pila. Recuerde que la Pila crece hacia atrás, por lo tanto PUSH resta 4 u 8, con el fin de apuntar a la ubicación de memoria mas baja de la pila. Si no lo restamos, la operación PUSH sobreescribira la ubicación actual apuntada por ESP (lo mas alto) y podremos perder datos.

Valor final: ESP apunta a la parte superior de la pila -4

PUSH

Ahora, un ejemplo mas detallado de la instrucción PUSH:

Valor de inicio: ESP apunta a la dirección de memoria 0x0046A3B0

Proceso: El programa ejecuta la instrucción PUSH 1. ESP disminuye en 4, llegando a ser 0x0046A3AF, y el valor 1 sera empujado a la pila.

Valor final: ESP apunta a la siguiente dirección de memoria: 0x0046A3AF

PUSH1.png

Proceso POP: Un POP es ejecutado, y el registro ESP es modificado.

Valor de inicio: ESP apunta a la parte superior de la pila (Previo ESP +4)

Proceso: La operación POP es lo opuesto a PUSH y recupera datos de lo mas alto de la Pila. Por lo tanto los datos contenidos en la ubicación de la dirección en el ESP (la parte superior de la pila) es recuperada y almacenada (usualmente en otro registro). Después de la operación POP el valor de ESP es incrementado, en x86 por 4 o en x64 por 8.

Valor final: ESP apunta a la parte superior de la pila (igual que la ubicación anterior antes del PUSH).

POP

Ahora, un ejemplo mas detallado de la instrucción POP:

Valor de inicio: ESP contiene el valor de la dirección de memoria. Después de PUSH 1, ESP apunta a la siguiente dirección de memoria: 0x0046A3AF

Proceso: El programa ejecuta la instrucción inversa POP EAX. El valor (00000001) contenido en la dirección del ESP sera sacado de la pila y sera copiado en el registro EAX. Luego ESP es actualizado agregando 4 y llegara a ser 0x0046A3B0.

Valor final: ESP apunta a la siguiente dirección de 0x0046A3B0. El regresa a su valor original.

POP1.png

Cuando el valor es sacado de la pila no es borrado o puesto es cero. El se mantendrá en la pila hasta que otra instrucción lo sobreescriba.

Ya que conocemos mas sobre la Pila, en el siguiente post analizaremos como trabajan los procedimientos y funciones, esto es importante ya que los procedimientos y funciones alteran el flujo normal del proceso.

D3N.

Black Hat Hacker, White Hat Hacker o Penetration Tester

A veces puede existir alguna confusión con los términos utilizados para describir a un hacker. Para un mejor entendimiento estas son las diferencias:

  • Black Hat Hacker: esta persona solo necesita encontrar una simple falla o debilidad en cualquier área de un sistema para atacarlo y utilizar la información para beneficio personal o en mala fe (por ejemplo vender información clasificada / confidencial).
  • White Hat Hacker / Ethical Hacker: esta persona encuentra una simple falla en el sistema pero utiliza la información para mejorar el sistema (por ejemplo reportar la falla a la empresa).
  • Penetration Tester: esta persona es más experto a los otros debido a que tiene que encontrar todas las fallas en un sistema, anotarlas, crear un reporte e incluir detalles de como el hackeo o ataque fue ejecutado y como puede ser replicado. Ademas involucra una metodología, evaluación de riesgos y el alcance de una prueba de penetración está enfocada a un área particular de un sistema, red o  aplicación.

La gran mayoría de los temas que veremos en futuros posts son aplicables para todos los tipos descritos anteriormente, en algunos casos hablaremos sobre temas que únicamente aplican para Pentesting y también veremos como defendernos ante ciertos tipos de ataques.

Feliz hacking!!

D3N

 

Fundamentos de Arquitectura 1

Nivel Principiante

Empezaremos con conceptos y términos importantes que nos ayudaran a entender los ataques y exploits que después usaremos.

Veremos CPU, instrucciones, registros, lenguaje máquina y ensamblador, memoria y muchas cosas más.

CPU, ISA y Ensamblador

La unidad central de procesamiento (CPU) es la parte encargada de ejecutar el lenguaje máquina de un programa. El lenguaje máquina o código máquina, es el conjunto de instrucciones que el CPU procesa. Cada instrucción es un comando primitivo que ejecuta una operación especifica tal como mover datos, cambiar el flujo de ejecución del programa, desarrollar operaciones aritméticas y lógicas entre otros.

Las instrucciones del CPU son representadas en formato hexadecimal (HEX). Debido a su complejidad es imposible para las personas utilizar en su formato natural, por lo tanto, el lenguaje maquina es traducido en código nemotécnico (un lenguaje mas legible). Esto es llamado lenguaje ensamblador (ASM). Los dos mas populares son NASM (Netwide Assembler) y MASM (Microsoft Macro Assembler). El ensamblador que vamos a usar es NASM.

En este ejemplo vemos las diferencias entre ambos:

Machine language vs Assembler

Cada CPU tiene su propia arquitectura del conjunto de instrucciones (ISA). ISA es lo que un programador (o un compilador) debe entender y usar para escribir un programa correctamente para un CPU especifico. En otras palabras ISA es lo que un programador ve: memoria, registros, instrucciones, entre otros.

Uno de los mas comunes ISA es el conjunto de instrucciones (o arquitectura) X86, originada del Intel 8086. El acronimo X86 identifica procesadores de 32 bits, mientras X64 (conocido como X86_64 o AMD64) identifica las versiones de 64 bits. (En otros posts veremos con mas detalle este tema).

Registros

El numero de bits, 32 o 64, se refiere al ancho de los registros del CPU. Cada CPU tiene un conjunto fijo de registros que son accedidos cuando sean requeridos. Se puede imaginar a los registros como las variables temporales usadas por el CPU para conseguir y almacenar datos.

Aunque la mayoría de los registros son pequeñas porciones de memoria en el CPU y sirven para almacenar datos temporalmente, nos enfocaremos en un grupo especifico de registros: The General Purpose Registers (GPRs).

La siguiente tabla resume los 8 registros de propósito general. Note que la nomenclatura se refiere a la arquitectura X86.

Nomenclatura X86La nomenclatura para el viejo CPU de 8 bits tiene registros de 16 bits dividido en dos partes: un bit bajo, identificado por una L (Low) al final del nombre, y un bit alto, identificado por una H (High) al final del nombre.

La nomenclatura de 16 bits combina la L y la H, y las reemplaza con una X. Mientras que para los registros Stack Pointer, Base Pointer, Source Pointer y Destination únicamente elimina la L.

En los 32 bits, el acronimo del registro utiliza una E al inicio, significa extendido. Mientras que la arquitectura de 64 bits, la E es reemplazada con una R.

Lo siguiente resume las nomenclaturas utilizadas:

Nomenclatura Arquitectura

En adición a los 8 registros de propósito general, existe otro registro que es importante: EIP (nomenclatura X86). Instruction Pointer (EIP) controla la ejecución del programa almacenando un puntero a la dirección de la siguiente instrucción (lenguaje máquina) que sera ejecutada, en otras palabras le indica al CPU en donde es la siguiente instruccion.

En el siguiente post de arquitectura veremos el proceso de memoria, la pila, endianness y NOPs.

Pura Vida!!!

D3N.