La semana pasada, comenzamos con una introducción hacia el análisis estático de los códigos maliciosos, en donde repasamos algunos conceptos iniciales y muy importantes para entender de qué manera podemos investigar sin ejecutar una amenaza las acciones que realiza. En el post de hoy, vamos a continuar avanzando sobre la arquitectura x86, sus componentes principales y funciones.
Las arquitecturas de las computadoras más modernas siguen, hasta hoy en día, laarquitectura de Von Neumann que cuentan con tres componentes principales, que a pesar del paso de los años no han cambiado:
- CPU: Es la unidad central de procesamiento, se encarga de ejecutar el código del programa o Sistema Operativo.
- Memoria Principal (RAM): Almacena los datos y el código que se cargarán en la CPU para su ejecución.
- Sistema de Entrada y Salida: Es la interfaz con los dispositivos como discos rígidos, teclados, monitores y demás.
A continuación podemos ver un diagrama que los representa:
A modo general, dentro de la estructura de esta arquitectura, la CPU contiene ciertos componentes para realizar tareas específicas. La unidad de control recibe desde la memoria RAM las instrucciones que debe ejecutar a través de un registro en particular, el Instruction Pointer (IP), que en pocas palabras apunta a la próxima instrucción a ejecutar. Los registros, son utilizados por la CPU para almacenar datos, valores o direcciones de memoria que acortan el tiempo que tardaría la CPU en ir a buscarlos directamente a la RAM. Existen registros que cumplen funciones específicas y otros de uso general.
Una vez que se cuentan con las instrucciones la ALU (del inglés, Aritmetic Logic Unit) es la encargada de ejecutarlas y almacenar el resultado directamente en la memoria RAM o en los registros. Este proceso de búsqueda y ejecución de una instrucción tras otras se repite a medida que se ejecuta un programa.
Memoria Principal
Ahora que mencionamos sobre cómo se ejecuta una instrucción en la CPU (más sobre registros e instrucciones luego), tenemos que introducir cómo se divide las regiones de memoria de un Programa y para que se utiliza cada una. La memoria de un programa se puede dividir en 4 secciones principales:
- Data: La sección de datos de un programa hace referencia a una región específica de memoria. Contiene lo que se conoce como las variables estáticas que no cambian con la ejecución del programa. También en esta sección se encuentran las variables globales, que están disponibles desde cualquier parte del programa.
- Code: En esta región de memoria se almacena el código que se eejcuta del prgroama donde se alojan todas las intrucciones que se van a ejecutar.
- Heap: El heap es una región de memoria que se utiliza para alocar nuevos valores durante la ejecución del programa como así también para eliminarlos una vez que se dejaron de utilizar. El heap es una memoría dinámica y su contenido varía a medida que se ejecuta el programa
- Stack (Pila): La pila se utiliza para alojar las variables locales, parámetros y valores de retorno de una función como así también contiene las direcciones de retorno entre una llamada a una función y otra, siendo múy útil para controlar el flujo de ejecución del programa.
Algo importante que deben recordar es que estas regiones de memoria no se encuentran en zonas contigüas de memoria y que su ubicación puede cambiar, estando enregiones más bajas o altas de memoria.
Instrucciones
Hasta acá, hablamos sobre instrucciones que se buscan desde la memoria RAM y son ejecutadas por la CPU, sin embargo no entramos en detalle de qué es una instrucción y cómo está compuesta. Las instrucciones en su conjunto son las que forman el programa en sí, y pueden contener cero o más operadores.
Una instrucción se identifica por palabras especiales como mov, push, pop, call, add, etc, y corresponden a un opcode (código de operación) específico. Cuando en los posts que leen en este blog del Laboratorio de ESET, ven una imagen de un debugger o un desensamblador, es posible que identifiquen los opcodes e instrucciones como se puede ver en la siguiente imagen:
Continuaremos analizando y entendiendo cada una de las instrucciones que se ejecutan para así comprender las operaciones que puede realizar un código malicioso cuando intenta infectar un sistema. Además buscaremos comprender de qué manera se utilizan llamadas a funciones, para ocultar los códigos maliciosos y otras amenazas. Todo esto y mucho más en el próximo post sobre Análisis estático de códigos maliciosos.
Imagen de Museo8bits en Wikimedia. Licencia CC0 1.0