Procesos
Se define como proceso al contenedor utilizado por el sistema operativo que, mediante un conjunto de recursos, ejecuta la instancia o lógica de un programa. Un proceso en sí mismo es pasivo; no ejecuta código directamente, sino que sirve de marco de trabajo.
Para entender cómo funciona un proceso, se deben tener en cuenta los siguientes componentes esenciales:
- Programa: El código ejecutable o lógica (un archivo .exe o .dll) que se mapea dentro del espacio de memoria virtual del proceso.
- Espacio de memoria virtual (privado): Un conjunto de direcciones virtuales asignadas exclusivamente al proceso para almacenar su código, datos, la pila (stack) y el montón (heap).
- Contexto de seguridad (Token de acceso): Un objeto que identifica la identidad del usuario, sus grupos y los privilegios del proceso. El sistema operativo lo utiliza para validar si el proceso tiene permiso para acceder a determinados recursos (archivos, claves de registro, etc.).
- Hilos (Threads): La unidad mínima de ejecución. Son los que realmente ejecutan el código del programa dentro del espacio de memoria. Aunque nacen dentro del proceso, es el kernel del sistema operativo quien se encarga de administrarlos y asignarles tiempo de CPU.
Process Environment Block (PEB)
Los procesos en Windows presentan una structo estructura interna que almacena información sobre sí mismos conocida como PEB. A diferencia de otras estructuras de control que gestiona el kernel, el PEB reside en el espacio de memoria de usuario (User Mode) [[2. Arquitectura de Windows]]. Esto significa que el propio proceso puede leer y modificar muchos de sus campos directamente sin interactuar con el sistema operativo.
Sirve como una tabla de referencia que contiene información relevante acerca del proceso, como el ID del proceso, parámetros, librerías DLL utilizadas, ubicación del programa ejecutable y más.
A continuación se lista de forma parcial la estructura PEB (winternl.h)
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;En la structse pueden observar miembros y elementos relevantes a la hora de analizar un proceso:
BeingDebugged: Un solo byte que se establece en1si el proceso está siendo analizado por un depurador (como x64dbg o WinDbg).Ldr: Un puntero hacia a la estructura PEB_LDR_DATA. Esta estructura almacena información sobre las DLLs y módulos cargados (direcciones en memoria de las librerías, tamaño, etc).
typedef struct _PEB_LDR_DATA {
ULONG _Length_;
BOOLEAN _Initialized_;
PVOID _SsHandle_;
LIST_ENTRY _InLoadOrderModuleList_;
LIST_ENTRY _InMemoryOrderModuleList_;
LIST_ENTRY _InInitializationOrderModuleList_;
} PEB_LDR_DATA, *PPEB_LDR_DATA;ProcessParameters: Puntero a una estructura (RTL_USER_PROCESS_PARAMETERS) que almacena información sobre los parámetros de ejecución del proceso, como los argumentos de la línea de comando, ruta del directorio actual, nombre del binario, etc.
typedef struct _RTL_USER_PROCESS_PARAMETERS {
ULONG _MaximumLength_;
ULONG _Length_;
ULONG _Flags_;
ULONG _DebugFlags_;
PVOID _ConsoleHandle_;
ULONG _ConsoleFlags_;
HANDLE _StdInputHandle_;
HANDLE _StdOutputHandle_;
HANDLE _StdErrorHandle_;
UNICODE_STRING _CurrentDirectoryPath_;
HANDLE _CurrentDirectoryHandle_;
UNICODE_STRING _DllPath_;
UNICODE_STRING _ImagePathName_;
UNICODE_STRING _CommandLine_;
PVOID _Environment_;
ULONG _StartingPositionLeft_;
ULONG _StartingPositionTop_;
ULONG _Width_;
ULONG _Height_;
ULONG _CharWidth_;
ULONG _CharHeight_;
ULONG _ConsoleTextAttributes_;
ULONG _WindowFlags_;
ULONG _ShowWindowFlags_;
UNICODE_STRING _WindowTitle_;
UNICODE_STRING _DesktopName_;
UNICODE_STRING _ShellInfo_;
UNICODE_STRING _RuntimeData_;
RTL_DRIVE_LETTER_CURDIR _DLCurrentDirectory[0x20]_;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;Si fuéramos a buscar información sobre las
structsmencionadas anteriormente en la documentación de Windows se observaría que la mayoría de miembros se encuentran en estadoReserved.Esto significa que Microsoft decide no publicar información acerca de estos miembros con el objetivo de evitar exponer demasiada información del sistema operativo.
Sin embargo, existen proyectos como ntinternals.net , https://vergiliusproject.com o https://www.geoffchappell.com/studies/windows/ cuyo objetivo es obtener la función y nombre de cada miembro.
Thread Environment Block (TEB)
El Thread Environment Block (TEB) es otra estructura relevante identificada dentro de un proceso en el sistema operativo Windows. Sirve para almacenar el estado y la información específica de cada hilo.
Mientras que un proceso tiene un único PEB, tendrá tantos TEBs como hilos en ejecución posea. Al igual que el PEB, el TEB reside en el espacio de usuario, lo que permite al hilo acceder a sus propios datos de configuración de forma rápida y sin necesidad de realizar transiciones al kernel mediante llamadas al sistema (syscalls).
Dentro del desarrollo de malware y capacidades ofensivas, el TEB sirve como herramienta para localizar el PEB en memoria, gestionar las excepciones de los hilos y obtener identificadores relevantes.
A continuación, se muestran los campos más relevantes del TEB:
typedef struct _TEB {
NT_TIB NtTib;
PVOID EnvironmentPointer;
CLIENT_ID ClientId;
PVOID ActiveRpcHandle;
PVOID ThreadLocalStoragePointer;
PPEB ProcessEnvironmentBlock;
ULONG LastErrorValue;
// ... (omitidos miembros reservados o internos del sistema)
} TEB, *PTEB;
Se listan a continuación algunos de los miembros más relevantes de la struct:
NtTib(Thread Information Block): Es la primera estructura almacenada dentro del TEB. Contiene información estructural sobre el hilo, como los límites de la pila (donde empiezan y terminan elStackBasey elStackLimit). Esto es crucial para técnicas ofensivas que analizan o modifican la pila (Stack Spoofing o Stack Walking). Además, en sistemas x86 (32 bits), elNtTibapunta al primer elemento de la lista del SEH (Structured Exception Handling).ClientId: Esta estructura contiene elUniqueProcess(el codiciado PID o Process ID) y elUniqueThread(el TID o Thread ID). Como mencionamos antes, si un binario necesitara conocer su propio PID (o el del hilo actual) de forma sigilosa, puede extraerlo directamente de aquí sin llamar a las APIs vigiladas comoGetCurrentProcessId()oGetCurrentThreadId().ProcessEnvironmentBlock: Puntero hacia el PEB. Como el PEB no tiene una dirección fija en memoria debido al ASLR (Address Space Layout Randomization), la forma estándar y limpia en que el programa localiza el PEB en el espacio de usuario es consultando este campo del TEB.ThreadLocalStoragePointer(TLS): Apunta al almacenamiento local del hilo. Los callbacks de TLS (TLS Callbacks) son funciones que se ejecutan antes de que el punto de entrada principal (el main oWinMain) del programa comience a correr. En el caso de malware, se suele utilizar los callbacks de TLS para ejecutar código de inyección o técnicas de evasión antes de que los depuradores o soluciones de seguridad como antivirus y EDRs analicen el binario.
Accediendo al Thread Environment Block
Dado que no podemos usar APIs comunes (de la forma habitual) si queremos pasar desapercibidos, los procesadores modernos proporcionan un camino directo mediante registros de segmento. El código en ensamblador (ASM) o C puede aprovechar estos registros para leer la dirección base del TEB del hilo actual:
- En sistemas de 32 bits (x86): El TEB se encuentra apuntado por el registro de segmento FS.
- En sistemas de 64 bits (x64): El TEB se encuentra apuntado por el registro de segmento GS.
Por ejemplo, para saltar del TEB al PEB en una arquitectura de 64 bits, el binario debería ejecutar una instrucción equivalente a esta (leyendo el offset 0x60 donde vive el puntero al PEB dentro del TEB):
PPEB pPeb = (PPEB)__readgsqword(0x60);


