[TUTORIAL] Reversing del controlador XignCode3 - Parte 3 - Análisis de la funcion



282
Me Gusta
140
Temas

c0de

MOV EAX, EDX
Registrado
19/4/20
Temas
140
Mensajes
283
Ubicación
Localhost
1588820688518.png
Si no ha leído la publicación anterior, puede encontrarla aquí: Parte 2: análisis de las funciones de inicio . En esta publicación, finalmente analizaremos la función Dispatcher. Recuerde que identificamos esta función en la primera publicación . Esta función tiene el objetivo principal de procesar cualquier paquete de solicitud de E/S (IRP) y, en este caso, manejar cualquier solicitud del código de función principal
Please, Acceder or Registrarse to view URLs content!
.

¿Por lo que pasaremos?
  1. Aprenda cómo se implementan las rutinas de Dispatcher.
  2. Para revertir el método de análisis que maneja las solicitudes IRP_MJ_WRITE.
  3. Identifique estructuras personalizadas utilizadas por el controlador y cree nuevos tipos locales en IDA.
  4. Comprenda cómo el controlador distribuye los diferentes identificadores codificados atravesando una matriz con estructuras personalizadas.
Introducción

Al invertir las cosas relacionadas con Windows, la lectura de la documentación de MS debería ser obligatoria. Por lo general, proporcionan mucha información útil que le ahorrará mucho tiempo, como las estructuras que usan, los parámetros y los ejemplos de código. Algo que me gusta hacer es examinar los ejemplos disponibles en sus repositorios y buscar códigos similares. Los desarrolladores de controladores suelen reutilizar una gran cantidad de código de esos ejemplos. Buscar patrones similares en nuestro binario podría proporcionarnos mucha información sobre el contexto y las funciones a las que se llama dentro de un fragmento de código similar. Al final de esta publicación, encontrará una lista de enlaces útiles.

fn_DriverIOCTLDispatcher (0x140004604)

Primero, tome unos minutos para analizar el
Please, Acceder or Registrarse to view URLs content!
inicial
Please, Acceder or Registrarse to view URLs content!
y el
Please, Acceder or Registrarse to view URLs content!
. Un controlador puede proporcionar múltiples rutinas de despacho, en este caso, implementaron solo un código de función principal, como vimos antes.

Please, Acceder or Registrarse to view quote content!

Podemos ver que recibe dos parámetros, pero ¿sabemos cuáles son esos parámetros? Sí, la documentación de MS explica que las
Please, Acceder or Registrarse to view URLs content!
despachador
Please, Acceder or Registrarse to view URLs content!
reciben como primer parámetro un PDEVICE_OBJECT y como segundo un puntero a una estructura IRP (IDA identificará este por sí mismo).

1588820989256.png

En la línea 16, el controlador controla que la longitud del búfer de entrada es igual a 0x270 , parece que es la única longitud posible.

Tenga cuidado al analizar la siguiente línea:

C++:
Please, Acceder or Registrarse to view codes content!

Como puede ver a continuación, MasterIrp comparte el mismo desplazamiento con IrpCount y SystemBuffer dentro de una unión (
Please, Acceder or Registrarse to view URLs content!
):

C++:
Please, Acceder or Registrarse to view codes content!

En este caso, el Despachador está intentando recuperar el SystemBuffer de la solicitud de IRP; no hace referencia al puntero MasterIrp. Necesitamos arreglar eso adecuadamente. En IDA PRO, esto se puede hacer fácilmente haciendo clic derecho-> Seleccionar campo de unión:

1588821133250.png

Ahora que hemos identificado dónde se almacena el búfer de entrada, veamos cómo se analiza:

C++:
Please, Acceder or Registrarse to view codes content!

Podemos ver que se espera que el primer DWORD sea igual a 0x270 (mismo valor de la longitud); Entonces, el siguiente DWORD debe tener algún tipo de valor mágico que coincida con 0x345821AB . Si se cumplen ambos requisitos, la función sub_140001E00 se llama enviando el búfer en el primer parámetro, y una referencia a una estructura aún desconocida en el segundo parámetro. Decidí llamar a esta función fn_DispatchIOCTLMethod y la analizaremos en la siguiente sección.

Después de cambiar el nombre de las variables, podemos concluir que la estructura del búfer de entrada sería algo como esto:

Código:
Please, Acceder or Registrarse to view codes content!

Como todavía no estamos analizando todos los métodos de envío, pero quiero proporcionarle un análisis completo del búfer de entrada, le mostraré la estructura completa que está utilizando el controlador. La estructura final sería así:

C++:
Please, Acceder or Registrarse to view codes content!

Esta función hace algunas cosas más, pero ignorémoslas por ahora.

fn_DispatchIOCTLMethod (0x140001E00)

1588821221355.png

Esta función es pequeña, y veremos que después de configurar los tipos correctamente y cambiar el nombre, todo se vuelve mucho más claro.

Lo primero que deberíamos hacer es agregar la estructura previamente definida DrvInputBuffer a IDA Pro. En la subvista "Tipos locales", es posible hacer "Clic derecho-> Insertar", allí puede copiar y pegar la definición de estructura:

1588821332840.png

Siguiente paso, cambie el nombre del primer parámetro para que sea un puntero a esa estructura haciendo "clic derecho en la variable -> Convertir a estructura *".

Si ocultamos los moldes, obtendremos algo como esto:

C++:
Please, Acceder or Registrarse to view codes content!

Si ha leído las publicaciones anteriores, puede reconocer dword_14000A240. Cambiamos el nombre de esta variable en la segunda publicación, mientras analizábamos fn_InitDispatchMethodArray : esta era la variable FunctionsCount .

Lo mismo sucede con dword_140009E40, que se renombró a IOCTLFunctionArray en la misma publicación, y es una matriz que contiene múltiples estructuras DispatcherStruct :

C++:
Please, Acceder or Registrarse to view codes content!

En base a eso, podemos identificar el siguiente comportamiento: Primero, la aplicación valida que IOCTLFunctionArray se haya inicializado comparando FunctionsCount con NULL. Luego, itera en un ciclo while, comparando el valor del índice de cada elemento con el DWORD en el desplazamiento 0xC del búfer de entrada (definido en la estructura como FnIndex ). Aumentan el contador hasta que alcanza el valor máximo almacenado en FunctionsCount . Si hay una coincidencia entre los índices, la función almacenada en DispatcherStruct-> FnPtr es call; enviando los mismos dos parámetros de fn_DispatchIOCTLMethod: SystemBuffer y la referencia a una estructura aún desconocida.

Esta sería la función final:

C++:
Please, Acceder or Registrarse to view codes content!

Próximos pasos
  • Análisis de NotifyRoutines (fn_InitRegistrationNotifyAndCallbackRoutines y fn_RegisterCreateProcessNotifyRoutine)
  • Cargue estructuras de kernel con archivos .h y tipos locales
Enlaces útiles
Creditos
Niemand

Enlace de origen
Please, Acceder or Registrarse to view URLs content!

Twitter
Please, Acceder or Registrarse to view URLs content!
 

Adjuntos

  • 1588820679684.png
    1588820679684.png
    201.9 KB · Visitas: 1