[TUTORIAL] Reversing del controlador XignCode3 - Parte 4.1 - Registro de rutinas de notificación y devolución de llamada

  • 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
127
Ubicación
Localhost
Mejores respuestas
0
LV
0
 
1588822783328.png
En primer lugar, si solo estás entrando en este tema, te recomiendo que leas esta publicación de GuidedHacking que te dará mucha información sobre este Anti-Cheat.

Pensé que podría cubrir todo sobre las rutinas de notificación y devolución de llamada en una publicación, mi problema. Para que sea breve, estoy dividiendo esta parte del análisis en varias publicaciones.

Como breve resumen les dejo aquí el enlace a las publicaciones anteriores:
Cuando estábamos analizando DriverEntry, identificamos dos funciones que eran responsables de diferentes tipos de registros de devoluciones de llamada ( fn_InitRegistrationNotifyAndCallbackRoutines y fn_RegisterCreateProcessNotifyRoutine ). Los mencioné pero no profundizamos en lo que hacen.

¿Por lo que pasaremos?
  • Identificar mutex y spinlocks
  • Aprenda cómo se registran las rutinas de notificación utilizando la API win
  • Aprenda cómo se crea PCREATE_PROCESS_NOTIFY_ROUTINE
  • Aprenda cómo el controlador maneja los diferentes estados de error que pueden aparecer durante la ejecución.
Introducción
Dado que el objetivo es analizar el controlador y darle la información que necesita para aprender el resto por su cuenta. No voy a profundizar en lo que son las rutinas de notificación y devolución de llamada. Aquí tienes algunos enlaces interesantes:
Lo más importante que debe comprender es el hecho de que los Anti-trampas necesitan controlar lo que está sucediendo en su sistema. Por lo tanto, van a registrar estas llamadas rutinas de notificación y devolución de llamada. Esto les permitirá ejecutar operaciones previas y posteriores cuando se desencadena un evento, por ejemplo, se ha creado un nuevo proceso, se ha cargado una nueva DLL, etc.

fn_InitRegistrationNotifyAndCallbackRoutines (0x140003550)

1588822974571.png

Hay algunas funciones que inicializan variables, spinlocks y matrices que aún no hemos visto. Todas esas variables se usan en múltiples funciones del controlador, y lo que generalmente sucede es que descubriremos el significado de esas (si alguna vez lo hacemos) mientras revertimos otras funciones que aún no hemos visto.

Como las funciones son cada vez más grandes y complejas, evitaré explicar algunas cosas básicas sobre el desarrollo de controladores, como las inicializaciones de mutex, las asignaciones, etc.

Comencemos con esta función: puede ver la función original aquí ( código asm y c ) . Y aquí el resultado final de la inversión (trata de no consentirte). Como siempre, tómate tu tiempo para tratar de entenderlo por ti mismo.

Al comienzo de la función tenemos algunas inicializaciones de búfer y mutex. Todavía no sabemos para qué son esas variables, pero podemos reconocerlas debido a cómo se utilizan esas variables; por ejemplo, como parámetro para funciones relacionadas con mutex como KeInitializeMutex o códigos de operación de ensamblaje como lock xadd :

1588822996076.png

Después de eso, encontraremos la primera función interesante sub_140003C38 , que decidí llamar j_fn_ConfigWindowsVersion . Verá esta función invertida en la siguiente publicación. En pocas palabras, la función identifica la versión actual de Windows e inicializa una serie de variables de desplazamiento con información sobre algunas estructuras de kernel particulares. Si esta función no falla, la ejecución continúa:

C++:
result = j_fn_ConfigWindowsVersion();
 if ( result < 0 )
   return result;
 fn_InitWeirdVariables_();
 fn_InitWeirdVariables2_();
 status_PsSetCreateProcessNotifyRoutine = 0;
 status_PsSetCreateProcessNotifyRoutineEx = 0;

Vamos a ignorar fn_InitWeirdVariables_ y fn_InitWeirdVariables2_ , dado que esas funciones solo inicializan algunos spinlocks y variables extrañas que el controlador usa más adelante. No estamos interesados en eso por ahora. En realidad, si sabes lo que están haciendo en estas líneas, házmelo saber, porque no sé qué tipo de hechicería están lanzando (creo que es una forma de generar un GUID para cada proceso, pero quién sabe):

C++:
ProcessId >>= 2;
v3 = (ProcessId >> 5) & 0x1FF;
v4 = ~(1 << (0x1F - (ProcessId & 0x1F)));

Después de eso, se inicializan dos variables NTSTATUS : status_PsSetCreateProcessNotifyRoutine y status_PsSetCreateProcessNotifyRoutineEx. Vamos a ver que si falla el primer intento de registrar NotifyRoutine usando PsSetCreateProcessNotifyRoutineEx , van a usar estas variables para controlar el flujo de ejecución y hacer un segundo intento usando PsSetCreateProcessNotifyRoutineEx .

C++:
RtlInitUnicodeString(&DestinationString, L"PsSetCreateProcessNotifyRoutineEx");
PsSetCreateProcessNotifyRoutineEx = MmGetSystemRoutineAddress(&DestinationString);
fn_pPsSetCreateProcessNotifyRoutineEx = PsSetCreateProcessNotifyRoutineEx;

La dirección de PsSetCreateProcessNotifyRoutineEx se recupera y se almacena en una variable. Si este valor variable no es NULL, se realiza el primer intento:

C++:
if ( PsSetCreateProcessNotifyRoutineEx )
  {
    ntStatus = PsSetCreateProcessNotifyRoutineEx(fn_CreateProcessNotifyRoutineExImp, 0i64);
    v6 = status_PsSetCreateProcessNotifyRoutineEx;
    if ( ntStatus >= 0 )
      v6 = 1;
    status_PsSetCreateProcessNotifyRoutineEx = v6;
  }
  else
  {
    v6 = status_PsSetCreateProcessNotifyRoutineEx;
  }

Podemos ver que se llama a PsSetCreateProcessNotifyRoutineEx . En el primer parámetro, envía la rutina ( fn_CreateProcessNotifyRoutineExImp ) para que se ejecute siempre que se cree o salga un nuevo proceso; y en el segundo parámetro, establece que la Rutina de notificación debe registrarse en lugar de eliminarse. Como puede ver, la misma función se utiliza para crear y eliminar una rutina de notificación.

Avanzando un poco más en la función, si el primer intento falla, veremos que PsSetCreateProcessNotifyRoutine se llama como un segundo intento:

C++:
if ( !v6 )
{
  ntStatus_1 = PsSetCreateProcessNotifyRoutine(fn_CreateProcessNotifyRoutine, 0i64);
  if ( ntStatus_1 < 0 )
  {
    status_PsSetCreateProcessNotifyRoutine = 0;
    goto LABEL_13;
  }
  status_PsSetCreateProcessNotifyRoutine = 1;
}

Nuevamente, hemos identificado otra de las rutinas de devolución de llamada: fn_CreateProcessNotifyRoutine.

fn_CreateProcessNotifyRoutine y fn_CreateProcessNotifyRoutineExImp

Si verificamos fn_CreateProcessNotifyRoutineExImp y fn_CreateProcessNotifyRoutine , notaremos que ambos son envoltorios de las rutinas reales:

C++:
void __fastcall fn_CreateProcessNotifyRoutineExImp(PEPROCESS Process, __int64 ProcessId, PVOID CreateInfo)
{
  if ( CreateInfo )                             //  If CreateInfo parameter is NULL, the specified process is exiting.
    fn_Analyze_CreateProcessNotifyRoutine(ProcessId);
  else
    fn_Analyze_ExitProcessNotifyRoutine(ProcessId);
}

Los parámetros recibidos por PCREATE_PROCESS_NOTIFY_ROUTINE se explican en la documentación . En base a eso, podemos identificar cómo esta función decide si se invoca la devolución de llamada debido a que se está creando o eliminando un proceso.

fn_Analyze_CreateProcessNotifyRoutine y fn_Analyze_ExitProcessNotifyRoutine son grandes funciones que analizaremos más adelante

Volver a fn_InitRegistrationNotifyAndCallbackRoutines
Finalmente, se intenta el último registro de devolución de llamada:

C++:
ntStatus_1 = fn_RegisterCallbackFunction();

if ( ntStatus_1 < 0 )                         // In case the registerCallbackFunction failed, we need to remove teh Notify routines previously registered.
{

  if ( status_PsSetCreateProcessNotifyRoutineEx && fn_pPsSetCreateProcessNotifyRoutineEx )
    fn_pPsSetCreateProcessNotifyRoutineEx(fn_CreateProcessNotifyRoutineExImp, 1u);// 2nd Parameter equal to 1 == remove
  if ( status_PsSetCreateProcessNotifyRoutine )
  {
    LOBYTE(_RemoveRoutine) = 1;
    PsSetCreateProcessNotifyRoutine(fn_CreateProcessNotifyRoutine, _RemoveRoutine);
  }
  goto label_exit;

}

Sin embargo, como puede ver, si registerCallbackFunction falló, la NotifyRoutine creada anteriormente debe eliminarse antes de salir. En ese caso, establecen _RemoveRoutine en 1 y luego vuelven a llamar a PsSetCreateProcessNotifyRoutine para eliminarlo.

fn_RegisterCallbackFunction se revertirá en las siguientes publicaciones, así como en las otras dos funciones.

Próximos pasos
  • Parte 4.2 - Administrar versiones de ventanas
  • Parte 4.3 - Uso de ObRegisterCallbacks
  • Parte 4.4 - Análisis de las rutinas de notificación
Creditos
Niemad

Enlace origen

Twitter