[TUTORIAL] Reversing del controlador XignCode3 - Parte 4.2 - Verificación de la versión de Windows

  • Hola Invitado, si deseas saber que es lo que paso con nuestro servidor de discord, puedes ingresar al siguiente enlace Discord


195
Me Gusta
68
Temas

c0de

MOV EAX, EDX
Registrado
19 Abr 2020
Mensajes
125
Ubicación
Localhost
Mejores respuestas
0
LV
0
 
1588826958999.png
Publicación rápida para analizar una función particular donde el controlador XC3 administra las diferentes versiones de Windows y brinda soporte a cada una de ellas.

Introducción

Como ya sabrá, en Windows, los desplazamientos dentro de la estructura del kernel diferente pueden cambiar de una versión a la siguiente.

Las estructuras críticas del núcleo como EPROCESS, KTHREAD, etc., tienen mucha información sobre sus atributos. Los Anti-Cheats generalmente acceden a esa información para verificar manualmente la información sobre procesos, sistema y memoria por su cuenta, sin la necesidad de usar la API de Windows.

Si este controlador está siendo utilizado por cientos de miles de usuarios, deben determinar correctamente las compensaciones correctas de cada atributo al que desean acceder para cada versión. Y esta función inicializa algunas variables, que luego son utilizadas por el resto de las funciones.

Publicaciones anteriores
¿Por lo que pasaremos?
  1. Cómo los controladores de este tipo admiten versiones múltiples de Windows
  2. Cómo se mantienen las compensaciones codificadas utilizadas para acceder a las estructuras del núcleo.
  3. Identificar las estructuras del núcleo que se utilizan.
1588827063033.png

j_fn_ConfigWindowsVersion (0x 140003C38)
Vale la pena mencionar que solo proporcionaré algunos ejemplos de las compensaciones que están administrando. Identificar cada uno de ellos lleva mucho tiempo y te dejo esa parte como tarea. 😉 Los que he identificado, se han utilizado en otras funciones que invertí manualmente antes.

Esta función es básicamente un salto a la implementación real:

C++:
.text:0000000140003C38 ; =============== S U B R O U T I N E =======================================
.text:0000000140003C38
.text:0000000140003C38 ; Attributes: thunk
.text:0000000140003C38
.text:0000000140003C38 sub_140003C38   proc near               ; CODE XREF: sub_140003550+9A↑p
.text:0000000140003C38                 jmp     sub_14000646C
.text:0000000140003C38 sub_140003C38   endp
.text:0000000140003C38
.text:0000000140003C38 ; ---------------------------------------------------------------------------
.text:0000000140003C3D                 align 20h

Si echamos un vistazo a sub_14000646C, notaremos que la función básicamente está obteniendo la versión actual del sistema operativo. Luego, basándose en la versión mayor y menor, y el número de compilación, inicializan un grupo de variables globales.

C++:
PsGetVersion(&MajorVersion, &MinorVersion, &BuildNumber, 0i64);

Según la documentación, PsGetVersion devuelve las versiones Major, minor y build en el primer, segundo y tercer parámetro, respectivamente. Lo que sucede más adelante es una serie de "si y más", donde determinan la versión exacta y el número de compilación:

C++:
dword_14000CDEC = BuildNumber;
  if ( MajorVersion == 10 )
  {
    if ( !MinorVersion )
    {
      if ( BuildNumber >= 10586 )
      {
        if ( BuildNumber > 10586 )
        {
          if ( BuildNumber > 14393 )
          {
            if ( BuildNumber > 15063 )
            {
              if ( BuildNumber <= 16299 )
              {
                dword_14000CDE8 = 13;
                qword_14000CDF0 = off_140009BA0;
                qword_14000CDF8 = off_140009BE8;
                qword_14000CE00 = off_140009C08;
                qword_14000CE08 = off_140009C10;
                qword_14000CE10 = off_140009C18;
                qword_14000CE18 = off_140009C20;
                qword_14000CE20 = off_140009C48;
                v0 = off_140009C58;

Se están inicializando una serie de 8 variables globales con punteros de función que recuperan algún desplazamiento particular de diferentes estructuras.

Centrémonos en un caso: qword_14000CDF0 se está inicializando con off_140009BA0 , que contiene una referencia a otra función:

C++:
.data:0000000140009BA0 off_140009BA0   dq offset sub_1400062D4 ; DATA XREF: sub_14000646C+23E↑o

Podemos ver cómo la función toma el valor de rcx (primer parámetro) y agrega 0x418 :

C++:
.text:00000001400062D4 ; =============== S U B R O U T I N E =======================================
.text:00000001400062D4
.text:00000001400062D4
.text:00000001400062D4 sub_1400062D4   proc near               ; DATA XREF: .data:off_140009520↓o
.text:00000001400062D4                                         ; .data:off_140009A00↓o ...
.text:00000001400062D4                 mov     rax, [rcx+418h]
.text:00000001400062DB                 retn
.text:00000001400062DB sub_1400062D4   endp

Aquí es donde las cosas se ponen más difíciles. Para identificar qué estructura es la que se envía a través de RCX, necesitamos encontrar un caso en el que se esté usando qword_14000CDF0 :

1588827218804.png

Para resumir y evitar que las cosas sean aún más complejas, vamos a terminar con el siguiente caso (por supuesto, después de cambiar el nombre y algunos análisis):

C++:
if ( PsLookupProcessByProcessId(ProcessId, &_pEPROCESS) >= 0 )// Retrieve reference to EPROCESS structure
  {
    v2 = _pEPROCESS;
    if ( MmIsAddressValid(_pEPROCESS) && fn_GetObjectTable(v2) )
    {

fn_GetObjectTable es nuestra función qword_14000CDF0 , y esta será la pista que nos dirá que la función EPROCESS es la que se envía como parámetro. Tenga en cuenta que PsLookupProcessByProcessId se llama antes y el segundo parámetro se usa para almacenar la estructura EPROCESS , como explica la documentación.

El desplazamiento 0x418 pertenece al atributo ObjectTable , un puntero a la estructura _HANDLE_TABLE dentro del núcleo.

Nota al margen
ObjectTable se usa mucho para enumerar y analizar manualmente los MANEJOS de un proceso. Los AC usualmente toman esta tabla y acceden a cada identificador existente para determinar si otros procesos tienen un MANGO para el proceso del juego; o si el MANGO del proceso como lsass.exe o csrss.exe ha sido manipulado.

Próximos pasos
  • Parte 4.3 - Uso de ObRegisterCallbacks
  • Parte 4.4 - Análisis de las rutinas de notificación
Creditos
Niemand

Enlace origen

Twitter