Windows Internal
Última actualización
Última actualización
Un proceso mantiene y representa la ejecución de un programa; una aplicación puede contener uno o más procesos. Un proceso tiene muchos componentes en los que se divide para almacenarlo e interactuar con él. Los desglosan estos otros componentes: "Cada proceso proporciona los recursos necesarios para ejecutar un programa. Un proceso tiene un espacio de direcciones virtuales, código ejecutable, identificadores abiertos para objetos del sistema, un contexto de seguridad, un identificador de proceso único, variables de entorno, una clase de prioridad, tamaños mínimo y máximo del conjunto de trabajo y al menos un hilo de ejecución". Esta información puede parecer intimidante, pero esta sala pretende hacer este concepto un poco menos complejo.
Como se mencionó anteriormente, los procesos se crean a partir de la ejecución de una aplicación. Los procesos son fundamentales para el funcionamiento de Windows; la mayor parte de las funciones de Windows pueden abarcarse como una aplicación y tienen un proceso correspondiente. A continuación se muestran algunos ejemplos de aplicaciones predeterminadas que inician procesos.
MsMpEng (Microsoft Defender)
wininit (teclado y ratón)
lsass (almacenamiento de credenciales)
Los atacantes pueden atacar procesos para evadir detecciones y ocultar malware como procesos legítimos. A continuación se muestra una pequeña lista de posibles vectores de ataque que los atacantes podrían emplear contra procesos,
Inyección de proceso ( )
Proceso de vaciado ( )
Enmascaramiento de proceso ( )
Los procesos tienen muchos componentes; se pueden dividir en características clave que podemos utilizar para describir procesos a un alto nivel. La siguiente tabla describe cada componente crítico de los procesos y su propósito.
Componente del proceso
Objetivo
Espacio privado de direcciones virtuales
Direcciones de memoria virtual que se asignan al proceso.
Programa ejecutable
Define el código y los datos almacenados en el espacio de direcciones virtuales.
Manijas abiertas
Define identificadores de recursos del sistema accesibles al proceso.
Contexto de seguridad
El token de acceso define el usuario, los grupos de seguridad, los privilegios y otra información de seguridad.
Identificacion de proceso
Identificador numérico único del proceso.
Hilos
Sección de un proceso prevista para su ejecución.
También podemos explicar un proceso en un nivel inferior, ya que reside en el espacio de direcciones virtuales. La tabla y el diagrama siguientes muestran cómo se ve un proceso en la memoria.
Componente
Objetivo
Código
Código que ejecutará el proceso.
Variables globales
Variables almacenadas.
Montón de procesos
Define el montón donde se almacenan los datos.
Recursos de proceso
Define otros recursos del proceso.
Bloque de entorno
Estructura de datos para definir la información del proceso.
Es excelente tener esta información cuando profundizamos en la explotación y el abuso de las tecnologías subyacentes, pero aún son muy abstractas. Podemos hacer tangible el proceso observándolos en el Administrador de tareas de Windows . El administrador de tareas puede informar sobre muchos componentes e información sobre un proceso. A continuación se muestra una tabla con una breve lista de los detalles esenciales del proceso.
Valor/Componente
Objetivo
Ejemplo
Nombre
Definir el nombre del proceso, normalmente heredado de la aplicación.
conhost.exe
PID
Valor numérico único para identificar el proceso.
7408
Estado
Determina cómo se está ejecutando el proceso (en ejecución, suspendido, etc.)
Correr
Nombre de usuario
Usuario que inició el proceso. Puede denotar privilegio del proceso.
SISTEMA
Estos son con los que interactuaría más como usuario final o manipularía como atacante.
Los procesos son el núcleo de la mayoría de los componentes internos de Windows.
Un subproceso es una unidad ejecutable empleada por un proceso y programada en función de factores del dispositivo.
Los factores del dispositivo pueden variar según las especificaciones de la CPU y la memoria, los factores lógicos y de prioridad, entre otros.
Podemos simplificar la definición de hilo: "controlar la ejecución de un proceso".
Dado que los subprocesos controlan la ejecución, este es un componente comúnmente objetivo. El abuso de subprocesos se puede utilizar por sí solo para ayudar en la ejecución del código, o se utiliza más ampliamente para encadenar otras llamadas API como parte de otras técnicas.
Los subprocesos comparten los mismos detalles y recursos que su proceso principal, como código, variables globales, etc. Los subprocesos también tienen sus valores y datos únicos, que se describen en la siguiente tabla.
Componente
Objetivo
Stack (Pila)
Todos los datos relevantes y específicos del hilo (excepciones, llamadas a procedimientos, etc.)
Thread Local Storage(Thread Local Storage)
Consejos para asignar almacenamiento a un entorno de datos único
Stack Argument (Argumento de pila)
Valor único asignado a cada hilo.
Context Structure (Estructura de contexto)
Mantiene los valores de registro de la máquina mantenidos por el kernel.
Los subprocesos pueden parecer componentes básicos y simples, pero su función es fundamental para los procesos.
La memoria virtual es un componente crítico de cómo funcionan e interactúan los componentes internos de Windows entre sí. La memoria virtual permite que otros componentes internos interactúen con la memoria como si fuera memoria física sin riesgo de colisiones entre aplicaciones. El concepto de modos y colisiones se explica con más detalle en la tarea 8.
El administrador de memoria también utilizará páginas o transferencias para manejar la memoria. Las aplicaciones pueden utilizar más memoria virtual que la memoria física asignada; el administrador de memoria transferirá o paginará la memoria virtual al disco para resolver este problema. Puede visualizar este concepto en el siguiente diagrama.
El espacio de direcciones virtuales máximo teórico es de 4 GB en un sistema x86 de 32 bits.
El espacio de direcciones virtuales máximo teórico es de 256 TB en un sistema moderno de 64 bits.
La proporción exacta de diseño de direcciones del sistema de 32 bits se asigna al sistema de 64 bits.
La mayoría de los problemas que requieren configuración o AWE se resuelven con el máximo teórico aumentado.
Puede visualizar ambos diseños de asignación de espacio de direcciones a la derecha.
Aunque este concepto no se traduce directamente en conceptos o aspectos internos de Windows, es fundamental comprenderlo. Si se entiende correctamente, se puede aprovechar para ayudar a abusar de las partes internas de Windows.
Cuando se carga una DLL como función en un programa, la DLL se asigna como una dependencia. Dado que un programa depende de una DLL, los atacantes pueden apuntar a las DLL en lugar de a las aplicaciones para controlar algún aspecto de la ejecución o funcionalidad.
Las DLL se crean de la misma manera que cualquier otro proyecto/aplicación; sólo requieren una ligera modificación de sintaxis para funcionar. A continuación se muestra un ejemplo de una DLL del proyecto de biblioteca de vínculos dinámicos Win32 de Visual C++ .
A continuación se muestra el archivo de encabezado de la DLL ; definirá qué funciones se importan y exportan. Discutiremos la importancia (o la falta de) del archivo de encabezado en la siguiente sección de esta tarea.
Se ha creado la DLL , pero aún queda la pregunta de ¿cómo se utilizan en una aplicación?
Las DLL se pueden cargar en un programa mediante enlaces dinámicos en tiempo de carga o enlaces dinámicos en tiempo de ejecución .
Cuando se carga mediante enlaces dinámicos en tiempo de carga , se realizan llamadas explícitas a las funciones DLL desde la aplicación. Solo puede lograr este tipo de vínculo proporcionando un encabezado ( .h ) y un archivo de biblioteca de importación ( .lib ). A continuación se muestra un ejemplo de cómo llamar a una función DLL exportada desde una aplicación.
Cuando se carga mediante enlaces dinámicos en tiempo de ejecución , se utilizaLoadLibrary
una función independiente ( o ) para cargar la DLL en tiempo de ejecución. Una vez cargado, debe utilizar para identificar la función DLL exportada a la que llamar. A continuación se muestra un ejemplo de carga e importación de una función DLL en una aplicación.LoadLibraryEx``GetProcAddress
En el código malicioso, los actores de amenazas suelen utilizar enlaces dinámicos en tiempo de ejecución más que enlaces dinámicos en tiempo de carga. Esto se debe a que es posible que un programa malicioso necesite transferir archivos entre regiones de memoria, y transferir una única DLL es más manejable que importar utilizando otros requisitos de archivos.
Los ejecutables y las aplicaciones son una gran parte del funcionamiento interno de Windows en un nivel superior. El formato PE ( P ortable E xecutable) define la información sobre el ejecutable y los datos almacenados. El formato PE también define la estructura de cómo se almacenan los componentes de datos.
El formato PE ( Portable Ejecutable ) es una estructura general para archivos ejecutables y de objetos. Los archivos PE ( Portable Executable ) y COFF ( Common Object File F ormat ) conforman el formato PE .
Los datos PE se ven más comúnmente en el volcado hexadecimal de un archivo ejecutable. A continuación, dividiremos un volcado hexadecimal de calc.exe en secciones de datos PE.
La estructura de los datos de PE se divide en siete componentes,
El encabezado de DOS( DOS Header) define el tipo de archivo.
El encabezado de DOS define el formato del archivo como . El encabezado de DOS se puede ver en la sección de volcado hexadecimal a continuación.MZ
.exe
El código auxiliar de DOS (DOS Stub) es un programa que se ejecuta de forma predeterminada al principio de un archivo y que imprime un mensaje de compatibilidad. Esto no afecta ninguna funcionalidad del archivo para la mayoría de los usuarios.
El código auxiliar de DOS imprime el mensaje . El código auxiliar de DOS se puede ver en la sección de volcado hexadecimal a continuación.This program cannot be run in DOS mode
El encabezado del archivo PE (PE File Header) proporciona información del encabezado PE del binario. Define el formato del archivo, contiene la firma y el encabezado del archivo de imagen, y otros encabezados de información.
El encabezado del archivo PE es la sección con el resultado menos legible por humanos. Puede identificar el inicio del encabezado del archivo PE desde elPE
código auxiliar en la sección de volcado hexadecimal a continuación.
El encabezado opcional de imagen( Image Optional Header) tiene un nombre engañoso y es una parte importante del encabezado del archivo PE
Los diccionarios de datos (Data Dictionaries) son parte del encabezado opcional de la imagen. Apuntan a la estructura del directorio de datos de la imagen.
La Tabla de secciones(Section Table) definirá las secciones y la información disponibles en la imagen. Como se analizó anteriormente, las secciones almacenan el contenido del archivo, como código, importaciones y datos. Puede identificar la definición de cada sección en la tabla de la sección de volcado hexadecimal a continuación.
Ahora que los encabezados han definido el formato y la función del archivo, las secciones pueden definir el contenido y los datos del archivo.
Sección
Objetivo
.texto
Contiene código ejecutable y punto de entrada.
.datos
Contiene datos inicializados (cadenas, variables, etc.)
.rdata o .idata
Contiene importaciones ( API de Windows ) y DLL.
.reloc
Contiene información de reubicación.
.rsrc
Contiene recursos de la aplicación (imágenes, etc.)
.depurar
Contiene información de depuración
# Interacting with Windows Internals
Interactuar con las partes internas de Windows puede parecer desalentador, pero se ha simplificado drásticamente. La opción más accesible e investigada para interactuar con Windows Internals es la interfaz a través de llamadas API de Windows . La API de Windows proporciona funcionalidad nativa para interactuar con el sistema operativo Windows. La API contiene la API de Win32 y, con menos frecuencia, la API de Win64.
La mayoría de los componentes internos de Windows requieren interactuar con hardware físico y memoria.
El kernel de Windows controlará todos los programas y procesos y unirá todas las interacciones de software y hardware. Esto es especialmente importante ya que muchas partes internas de Windows requieren interacción con la memoria de alguna forma.
Una aplicación por defecto normalmente no puede interactuar con el kernel ni modificar el hardware físico y requiere una interfaz. Este problema se resuelve mediante el uso de modos de procesador y niveles de acceso.
Un procesador de Windows tiene un modo de usuario y de kernel . El procesador cambiará entre estos modos según el acceso y el modo solicitado.
El cambio entre el modo de usuario y el modo kernel a menudo se ve facilitado por llamadas al sistema y a la API . En la documentación, este punto a veces se denomina " Punto de conmutación ".
Modo de usuario
Modo núcleo
Sin acceso directo al hardware
Acceso directo al hardware
Crea un proceso en un espacio de direcciones virtuales privado.
Se ejecutó en un único espacio de direcciones virtuales compartido
Acceso a "ubicaciones de memoria propias"
Acceso a toda la memoria física.
Las aplicaciones iniciadas en modo de usuario o " terreno de usuario" permanecerán en ese modo hasta que se realice una llamada al sistema o se interactúe a través de una API . Cuando se realiza una llamada al sistema, la aplicación cambiará de modo. En la foto de la derecha se muestra un diagrama de flujo que describe este proceso.
Al observar cómo interactúan los idiomas con la API de Win32 , este proceso puede deformarse aún más; la aplicación pasará por el tiempo de ejecución del lenguaje antes de pasar por la API. El ejemplo más común es la ejecución de C# a través de CLR antes de interactuar con la API de Win32 y realizar llamadas al sistema.
Inyectaremos un cuadro de mensaje en nuestro proceso local para demostrar una prueba de concepto para interactuar con la memoria.
Los pasos para escribir un cuadro de mensaje en la memoria se describen a continuación,
Asigne memoria de proceso local para el cuadro de mensaje.
Escriba/copie el cuadro de mensaje en la memoria asignada.
Ejecute el cuadro de mensaje desde la memoria del proceso local.
En el paso uno, podemos utilizar OpenProcess
para obtener el identificador del proceso especificado.
En el paso dos, podemos utilizar VirtualAllocEx
para asignar una región de memoria con el búfer de carga útil.
En el paso tres, podemos utilizar WriteProcessMemory
para escribir la carga útil en la región de memoria asignada.
En el paso cuatro, podemos usar CreateRemoteThread
para ejecutar nuestra carga útil desde la memoria.
Existen múltiples utilidades disponibles que facilitan los procesos de observación; incluidos , y .
La memoria virtual proporciona a cada proceso un . Se utiliza un administrador de memoria para traducir direcciones virtuales a direcciones físicas. Al tener un espacio de direcciones virtuales privado y no escribir directamente en la memoria física, los procesos tienen menos riesgo de causar daños.
Este espacio de direcciones se divide por la mitad, la mitad inferior ( 0x00000000 - 0x7FFFFFFF ) se asigna a los procesos como se mencionó anteriormente. La mitad superior ( 0x80000000 - 0xFFFFFFFF ) se asigna a la utilización de la memoria del sistema operativo . Los administradores pueden modificar este diseño de asignación para aplicaciones que requieren un espacio de direcciones más grande a través de la configuración ( incrementarUserVA ) o .
Los describen una DLL como "una biblioteca que contiene código y datos que pueden ser utilizados por más de un programa al mismo tiempo".
Las DLL se utilizan como una de las funcionalidades principales detrás de la ejecución de aplicaciones en Windows. Según la , "El uso de DLL ayuda a promover la modularización del código, la reutilización del código, el uso eficiente de la memoria y la reducción del espacio en disco. Por lo tanto, el sistema operativo y los programas se cargan más rápido, se ejecutan más rápido y ocupan menos espacio en el disco de la computadora". ".
DLL Hijacking ()
DLL Side-Loading ()
DLL Injection ()
En esta sala solo proporcionaremos una breve descripción general del uso de algunas llamadas API específicas relevantes para los aspectos internos de Windows. Consulte la para obtener más información sobre la API de Windows .