BSOD con Kernel mutex

Iniciado por Distorsion, 24 Febrero 2011, 22:31 PM

0 Miembros y 1 Visitante están viendo este tema.

Distorsion

Buenas,


tengo echo un hook a un major de un driver, en concreto al tcpip.sys, para controlar los programas que se conectan a internet.

Básicamente esta es la función que sustituye el major del driver:

Obtengo el PID
si esta en la lista de permitidos devuelvo major original

sino:
mutex up
   evento para que el programa de usuario lea el PID
   espero con un evento si el usuario permite o deniega el acceso a internet a la aplicación
  cuando la aplicación modo user relesea el evento:
  permito o deniego el acceso a la aplicación
release mutex





Pongo los mutex para que los demas threats que quieren conectarse esperen su turno para que el usuario decida, ademas la respuesta del usuario se escribe en el driver como un device, así tampoco se pisan las respuestas.

Vale el problema es que me peta aleatoriamente con un BSOD aunque ponga try and catch.


ATTEMPTED_SWITCH_FROM_DPC (b8)
A wait operation, attach process, or yield was attempted from a DPC routine.
This is an illegal operation and the stack track will lead to the offending
code and original DPC routine.
Arguments:
Arg1: 87cf6d48, Original thread which is the cause of the failure
Arg2: 83177280, New thread
Arg3: 93cb7fd0, Stack address of the original thread
Arg4: 00000000

Debugging Details:
------------------


FAULTING_THREAD:  87cf6d48

DEFAULT_BUCKET_ID:  VISTA_DRIVER_FAULT

BUGCHECK_STR:  0xB8

PROCESS_NAME:  System

CURRENT_IRQL:  2

LAST_CONTROL_TRANSFER:  from 830b2c25 to 830abaa6

STACK_TEXT: 
8078abc0 830b2c25 87cf6d48 00000000 8316dd20 nt!KiSwapContext+0x26
8078abf8 830b1523 87cf6e08 87cf6d48 8611b260 nt!KiSwapThread+0x266
8078ac20 830ab40f 87cf6d48 87cf6e08 00000000 nt!KiCommitThreadWait+0x1df
8078ac98 a53de3d6 8611b260 00000000 00000000 nt!KeWaitForSingleObject+0x393
8078ace4 830804ac 86eac360 85fff990 c68ea158 tcpHook!HookedDeviceControl+0xcc [c:\driver\limpiofull\codigo\driver\filtro.h @ 73]
8078acfc 91e7c199 91e98ec4 888d8410 88b03bf4 nt!IofCallDriver+0x63
8078ad18 91e78897 00000000 88b03bf4 00000032 netbt!TdiSendDatagram+0x14e
8078ad5c 91e78c74 88b03af8 c0a801ff 91e7a376 netbt!UdpSendDatagram+0x18c
8078ada4 91e797fc 88b03af8 85fe3298 02ea4498 netbt!SendNameServiceRequest+0x2a1
8078ade8 91e78065 02ea4498 00000000 88d98628 netbt!MSnodeCompletion+0x227
8078ae08 830ae16d 85c63d80 85c63d38 caf59f40 netbt!TimerExpiry+0x60
8078ae4c 830ae111 8316dd20 8078af78 00000002 nt!KiProcessTimerDpcTable+0x50
8078af38 830adfce 8316dd20 8078af78 00000000 nt!KiProcessExpiredTimerList+0x101
8078afac 830ac34e 0000cfc6 93cb7ac4 00000000 nt!KiTimerExpiration+0x25c
8078aff4 830abb1c 93cb7a74 00000000 00000000 nt!KiRetireDpcList+0xcb
8078aff8 93cb7a74 00000000 00000000 00000000 nt!KiDispatchInterrupt+0x2c
WARNING: Frame IP not in any known module. Following frames may be wrong.
830abb1c 00000000 0000001a 00d6850f bb830000 0x93cb7a74


STACK_COMMAND:  .thread 0xffffffff87cf6d48 ; kb

FOLLOWUP_IP:
tcpHook!HookedDeviceControl+cc [c:\driver\limpiofull\codigo\driver\filtro.h @ 73]
a53de3d6 57              push    edi

FAULTING_SOURCE_CODE: 
    69:    
    70:       KeSetEvent(syncEvent, 0, FALSE);
    71:       
    72:       KeWaitForSingleObject(syncEventK,Executive,KernelMode,FALSE,NULL);
>   73:       KeReleaseMutex(&mutex,FALSE);
    74: /*      if(control=='y'){
    75:          PIDS[(int)PID] = 'y';
    76:          return OldIrpMjDeviceControl(DeviceObject, Irp);
    77:       }else{
    78:          PIDS[(int)PID] = 'n';


SYMBOL_STACK_INDEX:  4

SYMBOL_NAME:  tcpHook!HookedDeviceControl+cc

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: tcpHook

IMAGE_NAME:  tcpHook.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  4d62ceb6

FAILURE_BUCKET_ID:  0xB8_tcpHook!HookedDeviceControl+cc

BUCKET_ID:  0xB8_tcpHook!HookedDeviceControl+cc

Followup: MachineOwner



En el log sale que peta el programa System, pero System no entra en ese pedazo de código, al ser llamada la función comprueba el PID del programa que la llama, si es 4 (System) devuelve inmediatamente la función original.

Así que mi deducción es que System es quien controla los mutex de todo el sistema y peta al controlar uno de los mutex que llama otro programa al pasar por mi función, el porque? es lo que intento averiguar.... A lo mejor es que demasiados threats esperan ese mutex o nose.

Alguna sugerencia o manera alternativa?

Gracias por la ayuda!!



Eternal Idol

Callers of KeWaitForSingleObject must be running at IRQL <= DISPATCH_LEVEL. However, if Timeout = NULL or *Timeout != 0, the caller must be running at IRQL <= APC_LEVEL and in a nonarbitrary thread context.

No deberias esperar en un DPC igual ...
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Distorsion

Buaaa perdona mi garrulez pero es que no estoy muy familiarizado con esto.

Se supone que uno de los programas que llama al KeWaitForSingleObject tienen que estar en el APC_LEVEL?

Mañana le echare un vistazo y buscare información, a ver como solucionarlo o un método alternativo, si me pudieras echar un poco de luz al asunto te lo agradecería.

Muchas gracias!! ;-)

Eternal Idol

No, tu codigo esta mal:

72:       KeWaitForSingleObject(syncEventK,Executive,KernelMode,FALSE,NULL);

CURRENT_IRQL:  2 << DISPATCH_LEVEL

El ultimo parametro de esa funcion es el Timeout y la unica forma de llamarlo en DISPATCH_LEVEL es con un puntero a un LARGE_INTEGER cuyo valor sea 0. Es decir que no podes ESPERAR, solo podes comprobar si el objeto esta señalado.
En tu caso le estas pasando un puntero nulo y por eso se produce la excepcion no controlada y el BSOD.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Distorsion

#4
Buaaaa ;-)

'Arreglado' ya no peta:

LARGE_INTEGER tiempo;

tiempo.QuadPart= 0x0;

KeWaitForSingleObject(syncEventK,Executive,KernelMode, FALSE, &tiempo);


Bueno ahora no da error ni nada, pero claro, no hace lo que quiero, no me espera :rolleyes:

Entonces he pensado en detectar el nivel de IRQL en  el que corre el threat y entonces si es APC_LEVEL esperar y si es otro retornar la función original sin esperar.
Esto lo hago con:
if(KeGetCurrentIrql() == DISPATCH_LEVEL);
   return OldIrpMjDeviceControl(DeviceObject, Irp);

Sorpresa, esto me dice que SIEMPRE esta en DISPATCH_LEVEL, así que mi filtro no hace nada. No lo entiendo :( Si no petaba siempre, solo cuelgues aleatorios, suponía que de vez en cuando entraba un threat en DISPATCH_LEVEL y petaba, pero ahora resulta que siempre esta en DISPATCH_LEVEL, entonces como es que esperaba aveces si y aveces BSOD?

Alguna solución o luz sobre el asunto? :huh:

Saludos y gracias :-*


----Edit-----

A lo mejor es que así obtengo el IRQL del driver y debería sacar el IRQL del DeviceObject o Irp que me entran por parámetros?
:o

Eternal Idol

#5
No tenes los conceptos basicos claros ... es muy dificil hacer algo estable y funcional de esa manera.

El IRQL es uno solo, el de cada procesador.

http://blogs.msdn.com/b/doronh/archive/2010/02/02/what-is-irql.aspx

Un thread, con d, es un hilo, una threat, con t, es una amenaza.

La excepcion esa se genera cuando se cambia de hilo efectivamente ATTEMPTED_SWITCH_FROM_DPC. Podias tener suerte en caso de que el evento ya estuviera señalado ... o cuando no tuvieras que esperar por el ...


No se si comprendi todo el asunto y lo mas probable es que exista una forma de hacer esto CORRECTAMENTE y no con un hook ... pero una posible solucion puede ser esta:

Marca el IRP como pendiente, retorna el estado como tal en el manejador del IRP y encola un work item. En el work item llama al manejador original o completalo como denegado y listo.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Distorsion

#6
Uffff me suena todo a nuevo, pero muchas gracias por la ayuda, a ver si entre el curro y la universiad encuentro un hueco para buscar información de todo esto
;-)


Supongo que te refieres a devolver un STATUS_PENDING y luego guardar en irp en una cola para tratarlo y completar o denegar. Mirare a ver como guardar el irp para luego retomarlo :D

Sino es mucho preguntar ya, como podría obtener el nombre del ejecutable que llama la función desde la propia función?

Así si no logro preguntarlo en tiempo real al usuario pues podria hacer una lista con los programas a los cuales quiero dar acceso y no interrumpir threads.

Muchas gracias!

Eternal Idol

#7
Ahi no podes obtener el nombre, un DPC se ejecuta en un contexto cualquiera ... es seguro que esta no es la manera ideal de hacer lo que queres.

Para obtener el nombre y/o el path completo (obtene el PEB) podes usar (siempre en PASSIVE_LEVEL obviamente):
ZwQueryInformationProcess

PERO NADA FUNCIONARA USANDO EL PROCESO ACTUAL SI ESTAS EN UN CONTEXTO INDETERMINADO COMO PASA EN UN DPC. Tal vez si puedas usar esta funcion: IoGetRequestorProcess y obtener un EPROCESS util del IRP.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón