De quem é essa IRP? (Process ID)
Existem casos onde é necessário saber qual processo lançou determinada IRP. Isso é muito comum em Firewalls ou em outros programas de segurança, que interceptam operações de I/O para verificar em suas bases de dados se determinado processo tem ou não acesso a um determinado recurso ou serviço. Mas como posso saber qual é o processo dono daquela IRP?
Bom, eu começo pensando que é muito fácil fazer isso. Como sabemos, o IoManager nos entrega as IRPs no contexto do processo que fez a requisição. Assim, conhecendo a API PsGetCurrentProcessId, podemos obter o ID do processo que lançou a IRP. Veja como é simples:
/**** *** OnDispatchProc ** ** Ô nominho genérico sem vergonha... */ NTSTATUS OnDispatchProc(PDEVICE_OBJECT pDeviceObject, PIRP pIrp) { //-f--> Estou pensando que é fácil, não copiem isso. HANDLE hProcessID; //-f--> Obtém o ID do processo corrente. hProcessID = PsGetCurrentProcessId(); ... }
Viu como é simples pensar que está tudo certo e estar redondamente enganado?
De fato, o IoManager entrega as IRPs no contexto do processo que está fazendo o I/O. Entretanto, o que aconteceria se um filtro de terceiro se atachar ao seu driver? Sim, isso é possível, e uma vez que tais drivers receberem estas IRPs, não é garantido que eles as repassarão para o nosso driver ainda no mesmo contexto. Vamos supor que escrevemos o driver de um dispositivo:
- Uma aplicação solicita a escrita no dispositivo.
- IoManager cria e envia a IRP para o nosso driver.
- Um filtro atachado ao nosso device recebe a IRP.
- O filtro realiza uma consulta assíncrona, possivelmente utilizando ExQueueWorkItem.
- Enquanto a consulta não termina, o filtro marca a IRP como pendente e retorna STATUS_PENDING.
- A operação assíncrona, neste caso, é realizada por uma thread de sistema, e ao final da consulta, o filtro encaminha a IRP para o driver abaixo dele, que no caso é o nosso driver.
- Nosso driver então recebe a IRP no contexto de sistema e não no contexto do processo que originou a IRP.
Quando o filtro mantém a IRP pendente e retorna da função de Dispatch, a thread que originou a IRP segue em frente e vai realizar outras tarefas. A thread original pode ainda retornar ao processo que iniciou toda esta operação, caso estejam sendo utilizadas as estruturas OVERLAPPED nas chamadas ao driver.
A IRP agora será executada no contexto do processo que chamar IoCallDriver passando como parâmetro esta IRP que ficou pendente. Em nosso exemplo, o processo que vai fazer isso é o System. Utilizando o código acima, obteremos o PID do processo System no lugar do PID do processo que iniciou a IRP.
Para corretamente obter a informação que estamos procurando, teremos que dar uma volta um pouco maior. Toda IRP, quando é criada, entra na lista de IRPs pendentes da thread que a criou. Para obter esta thread, utilizamos o campo pIrp->Tail.Overlay.Thread. Este campo possui um ponteiro para a estrutura ETHREAD da thread que criou esta IRP. Para chegar ao processo a partir da thread, utilizamos a API IoThreadToProcess. Veja o trecho abaixo.
/**** *** OnDispatchProc ** ** Ô nominho genérico sem vergonha... */ NTSTATUS OnDispatchProc(PDEVICE_OBJECT pDeviceObject, PIRP pIrp) { PEPROCESS pEProcess; PETHREAD pEThread; //-f--> Aqui obtemos o ponteiro da thread que criou esta // IRP. pEThread = pIrp->Tail.Overlay.Thread; //-f--> Agora obtemos o processo ao qual esta thread // percente. pEProcess = IoThreadToProcess(pEThread); ... }
Pronto, vejam que maravilha. Agora você tem em suas mãos a estrutura EPROCESS, que segundo a documentação da Microsoft, é uma estrutura opaca utilizada internamente pelo sistema operacional.
"The EPROCESS structure is an opaque data structure used internally by the operating system."
E o que eu faço com isso agora?
Embora eu tenha certeza que você já pensou em algo para eu fazer com esta estrutura, eu tenho uma sugestão bem melhor, e por que não dizer, bem mais apropriada. Mesmo porque podem ter crianças lendo isso. Apesar do EPROCESS não significar nada, ele ainda pode nos trazer alguma informação útil. Podemos obter o handle do processo identificado por essa estrutura e assim obter outras informações deste processo, tal como seu PID. Veja o exemplo abaixo.
//-f--> ZwQueryInformationProcess from // Windows NT/2000 Native API Reference // ISBN-10: 1578701996 // ISBN-13: 978-1578701995 NTSTATUS NTAPI ZwQueryInformationProcess ( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL ); /**** *** MyGetProcessID ** ** Obtém o ID de um processo a partir do seu EPROCESS ** Por favor, usem sua criatividade e dêem um nome melhor ** para esta função. */ NTSTATUS MyGetProcessID(IN PEPROCESS pEProcess, OUT PHANDLE phProcessId) { NTSTATUS nts = STATUS_SUCCESS; HANDLE hProcess = NULL; PROCESS_BASIC_INFORMATION ProcessInfo; ULONG ulSize; //-f--> Funções Zw são normalmente chamadas // do User Mode, assim para chamá-las // do Kernel, precisaremos no mínimo // estar rodando em PASSIVE_LEVEL. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); __try { //-f--> Inicializa o parâmetro de saída *phProcessId = 0; //-f--> Obtemos um handle para o processo // identificado pela estrutura EPROCESS nts = ObOpenObjectByPointer(pEProcess, OBJ_KERNEL_HANDLE, NULL, 0, NULL, KernelMode, &hProcess); if (!NT_SUCCESS(nts)) { ASSERT(FALSE); ExRaiseStatus(nts); } //-f--> Para utilizar esta API não documentada, basta // declarar seu protótipo como é feito no início // deste exemplo. nts = ZwQueryInformationProcess(hProcess, ProcessBasicInformation, &ProcessInfo, sizeof(ProcessInfo), &ulSize); if (NT_SUCCESS(nts)) { ASSERT(FALSE); ExRaiseStatus(nts); } //-f--> Todos vivos até aqui, agora basta alimentar o // parâmetro de saída com o que nos interessa *phProcessId = (HANDLE)ProcessInfo.UniqueProcessId; } __except(EXCEPTION_EXECUTE_HANDLER) { //-f--> Ops... Deu Mer(pii) nts = GetExceptionCode(); } //-f--> Libera o handle do processo que obtivemos. // Desta forma, seu gerente não fica te olhando // torto quando, apesar dos processos terminarem, // ainda houver acúmulo de estruturas em RAM. if (hProcess) ZwClose(hProcess); //-f--> E todos viveram felizes para sempre. // (inclusive o seu gerente) return nts; }
É possível obter inúmeras informações a partir do handle do processo. Existem até meios de obter o Path completo do processo a partir do seu handle, mas vamos deixar essa brincadeira para um próximo post.
Até mais... :-)



0 Comments:
Post a Comment
<< Home