Si no ha leído la publicación anterior, puede encontrarla aquí . Hoy continuaremos analizando algunas funciones que DriverEntry está utilizando.
En la parte 1 identificamos la función Dispatcher del controlador, así como dos funciones que inicializaban algunas variables para el controlador ( fn_InitDispatchMethodArray y fn_ObtainKernelFunctions ). Vamos a revertir cada uno de ellos y analizar rápidamente qué están haciendo. Esto nos ayudará a comprender las funciones de Dispatcher implementadas en este controlador.
¿Por lo que pasaremos?
El código original para esta función se puede encontrar aquí: descompilado y asm . Y el resultado final aquí (trata de no consentirte aún).
Vamos a ver ahora en esta función que se está inicializando una especie de estructura personalizada:
Podemos resolver esto, porque si prestamos atención al ensamblaje, veremos que primero asignan un valor int a una dirección de memoria en particular, y luego mueven 8 bytes más para escribir un puntero a una función dentro del binario. . Llamaremos a esta estructura IOCTLFunctionArray. ¿Una matriz? solo sigue leyendo =) Esta matriz desempeñará un papel importante al enviar una solicitud.
La estructura sería algo como esto:
Y en IDA Pro:
Este proceso se repite varias veces en esta función, 25 veces exactamente. Es por eso que lo llamamos matriz , se almacenan 25 veces la misma estructura (con diferentes valores, por supuesto) dentro de una matriz.
El valor 25 también se está almacenando en una variable que decidí renombrar de dword_14000A240 a FunctionsCount :
Más adelante veremos cómo se usa esta variable en el Dispatcher, pero podemos intentar adivinarla. En función de esta función, podemos deducir que el controlador tiene una lista de todos los métodos disponibles que se pueden invocar, y si se proporciona algún tipo de valor de índice, podría ser posible invocarlos.

El resultado final sería algo como esto . Tenga en cuenta que algunas funciones han cambiado de nombre desde que comencé a invertirlas antes. Veremos algunos interesantes en las siguientes publicaciones.
fn_ObtainKernelFunctions (0x140002A18)
La siguiente función es simple. Para continuar con la inicialización, el controlador necesita la dirección de algunas rutinas particulares:

Al hacer esto, pueden asegurarse de que esas funciones estén disponibles en la versión en ejecución de Windows y obtener un puntero a ellas. Solo necesitan almacenarlos en una variable y luego usarlos para llamar a cualquiera de esas rutinas convirtiéndolas en la definición de función adecuada. Puede detectar esto fácilmente también en la función de ensamblaje:
No hay mucha diferencia entre el código original y el resultado final. El código era algo obvio para esta función.
Próximos pasos
Niemand
Enlace original
niemand.com.ar
Twitter
twitter.com
En la parte 1 identificamos la función Dispatcher del controlador, así como dos funciones que inicializaban algunas variables para el controlador ( fn_InitDispatchMethodArray y fn_ObtainKernelFunctions ). Vamos a revertir cada uno de ellos y analizar rápidamente qué están haciendo. Esto nos ayudará a comprender las funciones de Dispatcher implementadas en este controlador.
¿Por lo que pasaremos?
- Algunos mecanismos básicos de inicialización de este controlador
- Identifique lo que parece ser una estructura personalizada utilizada para indexar y almacenar todos los métodos disponibles.
- Identifique cómo se ubican las direcciones de función en la memoria. Por ejemplo, ObRegisterCallbacks . Vamos a hablar de esto en las siguientes publicaciones.
El código original para esta función se puede encontrar aquí: descompilado y asm . Y el resultado final aquí (trata de no consentirte aún).
Vamos a ver ahora en esta función que se está inicializando una especie de estructura personalizada:
C++:
.text:0000000140001617 mov cs:dword_140009E40, 306h
.text:0000000140001621 mov cs:qword_140009E48, rax
.text:0000000140001628 lea rax, sub_14000101C
.text:000000014000162F mov cs:qword_140009E58, rax
.text:0000000140001636 lea rax, sub_140001CC8
.text:000000014000163D mov cs:qword_140009E68, rax
Podemos resolver esto, porque si prestamos atención al ensamblaje, veremos que primero asignan un valor int a una dirección de memoria en particular, y luego mueven 8 bytes más para escribir un puntero a una función dentro del binario. . Llamaremos a esta estructura IOCTLFunctionArray. ¿Una matriz? solo sigue leyendo =) Esta matriz desempeñará un papel importante al enviar una solicitud.
La estructura sería algo como esto:
C++:
typedef struct DispatcherStruct {
int Index;
char padding[4];
PVOID FnPtr;
};
Y en IDA Pro:
C++:
00000000 DispatcherStruct struc ; (sizeof=0x10, mappedto_424)
00000000 ; XREF: .data:_IOCTLFunctionArray/r
00000000 Index dd ? ; XREF: fn_InitDispatchMethodArray+1F/t
00000004 padding db 4 dup(?)
00000008 FnPtr dq ?
00000010 DispatcherStruct ends
Este proceso se repite varias veces en esta función, 25 veces exactamente. Es por eso que lo llamamos matriz , se almacenan 25 veces la misma estructura (con diferentes valores, por supuesto) dentro de una matriz.
El valor 25 también se está almacenando en una variable que decidí renombrar de dword_14000A240 a FunctionsCount :
C++:
.text:000000014000186A mov cs:FunctionsCount , 19h
Más adelante veremos cómo se usa esta variable en el Dispatcher, pero podemos intentar adivinarla. En función de esta función, podemos deducir que el controlador tiene una lista de todos los métodos disponibles que se pueden invocar, y si se proporciona algún tipo de valor de índice, podría ser posible invocarlos.

El resultado final sería algo como esto . Tenga en cuenta que algunas funciones han cambiado de nombre desde que comencé a invertirlas antes. Veremos algunos interesantes en las siguientes publicaciones.
fn_ObtainKernelFunctions (0x140002A18)
La siguiente función es simple. Para continuar con la inicialización, el controlador necesita la dirección de algunas rutinas particulares:

Al hacer esto, pueden asegurarse de que esas funciones estén disponibles en la versión en ejecución de Windows y obtener un puntero a ellas. Solo necesitan almacenarlos en una variable y luego usarlos para llamar a cualquiera de esas rutinas convirtiéndolas en la definición de función adecuada. Puede detectar esto fácilmente también en la función de ensamblaje:
C++:
.text:0000000140002A1C lea rdx, SourceString ; "ObGetFilterVersion"
.text:0000000140002A23 lea rcx, [rsp+38h+DestinationString] ; DestinationString
.text:0000000140002A28 call cs:RtlInitUnicodeString
.text:0000000140002A2E lea rcx, [rsp+38h+DestinationString] ; SystemRoutineName
.text:0000000140002A33 call cs:MmGetSystemRoutineAddress
.text:0000000140002A39 lea rdx, aObregistercall ; "ObRegisterCallbacks"
.text:0000000140002A40 mov cs:qword_14000A288, rax
.text:0000000140002A47 lea rcx, [rsp+38h+DestinationString] ; DestinationString
.text:0000000140002A4C call cs:RtlInitUnicodeString
.text:0000000140002A52 lea rcx, [rsp+38h+DestinationString] ; SystemRoutineName
.text:0000000140002A57 call cs:MmGetSystemRoutineAddress
No hay mucha diferencia entre el código original y el resultado final. El código era algo obvio para esta función.
Próximos pasos
- Analizar la función de envío (fn_DriverIOCTLDispatcher)
- Análisis del registro de notificaciones y rutinas de devolución de llamada (fn_InitRegistrationNotifyAndCallbackRoutines y fn_RegisterCreateProcessNotifyRoutine)
Niemand
Enlace original

Reversing XignCode3 Driver - Part 2 - Analyzing init functions - Niemand - Cyber Security
If you haven’t read the previous post you can find it here. Today we will continue analyzing some functions that DriverEntry is using. On part 1 we identified the Dispatcher function of the driver as …

Niemand (@niemand_sec) | Twitter
The latest Tweets from Niemand (@niemand_sec). Security Consultant at @immunityinc. I'm a system engineer, who loves security. Opinions are my own. Argentina