Per creare o aprire un Callback Object già esistente si usa la funzione ExCreateCallback che è molto simile a ZwCreateFile e IoCreateFile viste in una precedente lezione (si veda [2]). Si noti che una volta che il Callback Object (nel caso di creazione) o il riferimento ad esso (nel caso di apertura) non serve più è necessario usare ObDereferenceObject per eliminarlo o decrementare il contatore dei riferimenti.
NTSTATUS ExCreateCallback( PCALLBACK_OBJECT *CallbackObject, POBJECT_ATTRIBUTES ObjectAttributes, BOOLEAN Create, BOOLEAN AllowMultipleCallbacks );
CallbackObject è il puntatore che riceverà l'indirizzo del Callback Object da creare o aprire.
ObjectAttributes è un puntatore ad una istanza di tipo OBJECT_ATTRIBUTES che è una struttura che conterrà gli attributi del Callback Object (essenzialmente nome e metodo di comparazione di questi ultimi).
Create è un booleano che indica se eventualmente il Callback Object deve essere creato nel caso non fosse possibile aprirlo.
AllowMultipleCallbacks è un booleano che indica se possono essere aggiunte funzioni di callback multiple nel caso di creazione del Callback Object (non si applica nel caso di apertura).
Per registrare una funzione di callback presso il Callback Object appena creato o aperto si usa la funzione ExRegisterCallback mentre per rimuovere una registrazione esistente si usa ExUnregisterCallback
PVOID ExRegisterCallback( PCALLBACK_OBJECT CallbackObject, PCALLBACK_FUNCTION CallbackFunction, PVOID CallbackContext );
void ExUnregisterCallback( PVOID CallbackRegistration );
CallbackObject è il puntatore al Callback Object in cui registrare la funzione di callback.
CallbackContext è un puntatore ad una istanza di una struttura dati definita dal chiamante da passare alla funzione di callback (non rilevante al momento).
CallbackFunction è il puntatore alla funzione di callback da registrare e che verrà invocata dal sistema (per conto del driver che ha creato il Callback Object) quando si verifica un dato evento. E' importante notare che la funzione di callback verrà invocata dal sistema operativo allo stesso IRQL in cui era il driver quando ha creato il Callback Object. La funzione di callback deve essere conforme alla seguente definizione di PCALLBACK_FUNCTION
VOID (*PCALLBACK_FUNCTION) ( IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2 );
CallbackContext è il puntatore alla istanza di struttura dati vista in precedenza (non rilevante al momento).
Argument1 è un puntatore ad un argomento definito dal Callback Object (non rilevante al momento).
Argument2 è un puntatore ad un argomento definito dal Callback Object (non rilevante al momento).
ExRegisterCallback ritorna un puntatore che identifica la funzione di callback appena registrata e che è necessario passare a ExUnregisterCallback nel parametro CallbackRegistration per annullare la registrazione quando non si vogliono più ricevere notifiche.
Il codice di esempio è tratto da [1] ed è relativamente banale una volta capito come funzionano i Callback Object e le funzioni collegate appena descritte.
#include <ntddk.h> // Esportata da Ntkrnl.exe ed esposta da Ntkrnl.lib (ma non negli header ufficiali) NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process); // Puntatore usato per rimuovere la registrazione della funzione di callback PVOID g_CbRegistration; NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath) { UNREFERENCED_PARAMETER(pRegPath); DbgPrint("Enter DriverEntry\n"); NTSTATUS status = STATUS_SUCCESS; pDriverObject->DriverUnload = DriverUnload; RegisterSetSystemTimeNotify(); DbgPrint("Leave DriverEntry\n"); return status; }
VOID DriverUnload(PDRIVER_OBJECT pDriverObject) { UNREFERENCED_PARAMETER(pDriverObject); UnRegisterSetSystemTimeNotify(); DbgPrint("[MyDriver]Unloaded!\n"); }
NTSTATUS RegisterSetSystemTimeNotify() { PCALLBACK_OBJECT pCallBackObj; OBJECT_ATTRIBUTES oa; UNICODE_STRING callbackname; NTSTATUS status; RtlInitUnicodeString(&callbackname, L"\\Callback\\SetSystemTime"); InitializeObjectAttributes(&oa, &callbackname, OBJ_CASE_INSENSITIVE, 0, 0); status = ExCreateCallback(&pCallBackObj, &oa, TRUE, FALSE); if (!NT_SUCCESS(status)) return status; g_CbRegistration = ExRegisterCallback(pCallBackObj, SetSystemTimeNotify, NULL); if (g_CbRegistration == NULL) return STATUS_UNSUCCESSFUL; ObDereferenceObject(pCallBackObj); return STATUS_SUCCESS; }
NTSTATUS UnRegisterSetSystemTimeNotify() { if (g_CbRegistration == NULL) return STATUS_UNSUCCESSFUL; ExUnregisterCallback(g_CbRegistration); return STATUS_SUCCESS; }
// Funzione di callback VOID SetSystemTimeNotify ( IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2 ) { UNREFERENCED_PARAMETER(CallbackContext); UNREFERENCED_PARAMETER(Argument1); UNREFERENCED_PARAMETER(Argument2); // Attenzione: si sta usando un Callback Object predefinito quindi questa funzione // di callback sarà invocata ad IRQL = 2 con tutto ciò che questo comporta (si veda [3]). DbgPrint("[SetSystemTimeNotify]IRQL: %ld\n", KeGetCurrentIrql()); }
Riferimenti:
[1] https://github.com/andylau004/LookDrvCode/tree/master/WIN64驱动编程基础教程/代码/%5B4-8%5DTimeChangeCallback
[2] 11 - Kernel API: File
[3] 01 - Concetti preliminari
Nessun commento:
Posta un commento