NTSYSAPI NTSTATUS ZwCreateKey( PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG TitleIndex, PUNICODE_STRING Class, ULONG CreateOptions, PULONG Disposition );
NTSYSAPI NTSTATUS ZwOpenKey( PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes );
KeyHandle è il puntatore che riceverà l'indirizzo dell'handle alla chiave di registro
DesiredAccess è un valore (o una combinazione di valori) che indica cosa si vuole fare della chiave tramite il relativo handle. Ad esempio, si può voler leggere o scrivere (o entrambe le cose) un valore della chiave.
ObjectAttributes è un puntatore ad una struttura di tipo OBJECT_ATTRIBUTES che deve essere inizializzata con vari attributi da associare all'handle. Tra i più importanti ci sono:
- il nome (spesso completamente qualificato) dell'oggetto kernel (chiave di registro) di cui si vuole ottenere l'handle.
- l'indicazione del fatto che l'handle sarà usato solo in kernel mode o meno.
- l'indicazione del fatto che la comparazione tra il nome indicato per l'oggetto kernel ed altri nomi di oggetti esistesti dello stesso tipo avvenga senza distinzione tra maiuscole o minuscole.
Per inizializzare tale struttura si può usare la macro InitializeObjectAttributes (si veda [2]).
TitleIndex e Class non sono rilevanti: impostarli a zero e NULL, rispettivamente.
CreateDisposition è un valore (o una combinazione di valori) che specifica le opzioni da applicare quando si crea o apre una chiave di registro. In particolare, si può indicare se la chiave deve essere cancellata (REG_OPTION_VOLATILE) o preservata (REG_OPTION_NON_VOLATILE) quando il sistema è riavviato
Disposition è un puntatore ad una variabile che riceve un valore che indica se una nuova chiave è stata creata (REG_CREATED_NEW_KEY) od una esistente è stata aperta (REG_OPENED_EXISTING_KEY).
Tutti gli esempi di codice presenti in questa lezione sono tratti da [1].
BOOLEAN MyCreateRegistryKey(UNICODE_STRING ustrRegistry) { HANDLE hRegister = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; ULONG ulResult = 0; NTSTATUS status = STATUS_SUCCESS; InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwCreateKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &ulResult); if (!NT_SUCCESS(status)) { ShowError("ZwCreateKey", status); return FALSE; } if (ulResult == REG_CREATED_NEW_KEY) { DbgPrint("The register item is createed!\n"); } else if (ulResult == REG_OPENED_EXISTING_KEY) { DbgPrint("The register item has been created, and now is opened!\n"); } ZwClose(hRegister); return TRUE; }
BOOLEAN MyOpenRegistryKey(UNICODE_STRING ustrRegistry) { OBJECT_ATTRIBUTES objectAttributes = { 0 }; HANDLE hRegister = NULL; NTSTATUS status = STATUS_SUCCESS; InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes); if (!NT_SUCCESS(status)) { ShowError("ZwOpenKey", status); return FALSE; } DbgPrint("Open register successfully!\n"); ZwClose(hRegister); return TRUE; }
Un'altra funzione utile è ZwDeleteKey.
NTSYSAPI NTSTATUS ZwDeleteKey( HANDLE KeyHandle );
KeyHandle è l'handle alla chiave che si vuole eliminare.
BOOLEAN MyDeleteRegistryKey(UNICODE_STRING ustrRegistry) { HANDLE hRegister = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; NTSTATUS status = STATUS_SUCCESS; InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes); if (!NT_SUCCESS(status)) { ShowError("ZwOpenKey", status); return FALSE; } status = ZwDeleteKey(hRegister); if (!NT_SUCCESS(status)) { ZwClose(hRegister); ShowError("ZwDeleteKey", status); return FALSE; } ZwClose(hRegister); return TRUE; }
Se si vuole creare un valore per una chiava o si vuole impostare un suo valore già esistente si può usare la funzione ZwSetValueKey.
NTSYSAPI NTSTATUS ZwSetValueKey( HANDLE KeyHandle, PUNICODE_STRING ValueName, ULONG TitleIndex, ULONG Type, PVOID Data, ULONG DataSize );
KeyHandle è l'handle alla chiave di registro in cui si vuole creare o impostare il valore. In genere tale handle è ottenuto tramite ZwCreateKey o ZwOpenKey.
ValueName è un puntatore ad una struttura UNICODE_STRING che contiene il nome del valore che conterrà i dati che si vogliono scrivere. Se si fornisce il nome di un valore già esistente i dati che esso contiene verranno sovrascritti.
TitleIndex non è rilevante: impostarlo a zero.
Type è un valore che indica il tipo di dati da scrivere. Tra i valori più usati ci sono REG_BINARY (binario), REG_DWORD (numerico) e REG_SZ (stringa).
Data è un puntatore che contiene l'indirizzo del buffer con i dati da scrivere.
DataSize è un valore che indica la dimensione in byte del buffer specificato nel parametro Data.
BOOLEAN MySetRegistryKeyValue( UNICODE_STRING ustrRegistry, UNICODE_STRING ustrKeyValueName, ULONG ulKeyValueType, PVOID pKeyValueData, ULONG ulKeyValueDataSize) { HANDLE hRegister = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; NTSTATUS status = STATUS_SUCCESS; InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes); if (!NT_SUCCESS(status)) { ShowError("ZwOpenKey", status); return FALSE; } status = ZwSetValueKey(hRegister, &ustrKeyValueName, 0, ulKeyValueType, pKeyValueData, ulKeyValueDataSize); if (!NT_SUCCESS(status)) { ZwClose(hRegister); ShowError("ZwSetValueKey", status); return FALSE; } ZwClose(hRegister); return TRUE; }
Se invece si vuole leggere il valore di una chiava si può usare la funzione ZwQueryValueKey.
NTSYSAPI NTSTATUS ZwQueryValueKey( HANDLE KeyHandle, PUNICODE_STRING ValueName, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength );
KeyHandle è l'handle alla chiave di registro dove si trova il valore da leggere. In genere tale handle è ottenuto tramite ZwCreateKey o ZwOpenKey.
ValueName è un puntatore ad una struttura UNICODE_STRING che contiene il nome del valore che si vuole leggere.
KeyValueInformationClass è un valore che indica il tipo di informazioni che si vogliono leggere. Tale valore di solito è KeyValuePartialInformation quando si è interessati esclusivamente ai dati di un valore.
KeyValueInformation è un puntatore che contiene l'indirizzo del buffer che conterrà una istanza del tipo specificato nel parametro KeyValueInformationClass. Nel caso in esame questa sarà una istanza della struttura KEY_VALUE_PARTIAL_INFORMATION
Length è un valore che indica la dimensione in byte del buffer specificato nel parametro KeyValueInformation.
ResultLength è un puntatore ad una variabile. Se ZwQueryValueKey restituisce STATUS_SUCCESS tale variabile conterrà il numero di byte scritti nel buffer specificato nel membro KeyValueInformation. Se invece restituisce STATUS_BUFFER_OVERFLOW o STATUS_BUFFER_TOO_SMALL tale variabile conterrà la dimensione in byte necessari a contenere le informazioni richieste per il valore della chiave.
BOOLEAN MyQueryRegistryKeyValue(UNICODE_STRING ustrRegistry, UNICODE_STRING ustrKeyValueName) { HANDLE hRegister = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; NTSTATUS status = STATUS_SUCCESS; ULONG ulBufferSize = 0; PKEY_VALUE_PARTIAL_INFORMATION pKeyValuePartialInfo = NULL; InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes); if (!NT_SUCCESS(status)) { ShowError("ZwOpenKey", status); return FALSE; } // Passando zero come dimensione del buffer obbliga ZwQueryValueKey a restituire STATUS_BUFFER_TOO_SMALL // e passare la dimensione necessaria a contenere le informazioni richieste in ulBufferSize. // Passa NULL come puntatore al buffer perchè non necessario in // questa fase: controllo su dimensioni avviene prima quindi il puntatore non viene mai usato. status = ZwQueryValueKey(hRegister, &ustrKeyValueName, KeyValuePartialInformation, NULL, 0, &ulBufferSize); if (ulBufferSize == 0) { ZwClose(hRegister); ShowError("ZwQueryValueKey", status); return FALSE; } // Usa ulBufferSize per allocare un buffer della dimesione appropriata. pKeyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(NonPagedPool, ulBufferSize); status = ZwQueryValueKey( hRegister, &ustrKeyValueName, KeyValuePartialInformation, pKeyValuePartialInfo, ulBufferSize, &ulBufferSize); if (!NT_SUCCESS(status)) { ExFreePool(pKeyValuePartialInfo); ZwClose(hRegister); ShowError("ZwQueryValueKey", status); return FALSE; } /* typedef struct _KEY_VALUE_PARTIAL_INFORMATION { ULONG TitleIndex; ULONG Type; ULONG DataLength; UCHAR Data[1]; } KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION; */ DbgPrint("KeyValueName=%wZ, KeyValueType=%d, KeyValueData=%S\n", &ustrKeyValueName, pKeyValuePartialInfo->Type, pKeyValuePartialInfo->Data); ExFreePool(pKeyValuePartialInfo); ZwClose(hRegister); return TRUE; }
Per eliminare il valore di una chiave si può usare ZwDeleteValueKey.
NTSYSAPI NTSTATUS ZwDeleteValueKey( HANDLE KeyHandle, PUNICODE_STRING ValueName );
KeyHandle è l'handle alla chiave di registro dove si trova il valore da eliminare. In genere tale handle è ottenuto tramite ZwCreateKey o ZwOpenKey.
ValueName è un puntatore ad una struttura UNICODE_STRING che contiene il nome del valore che si vuole eliminare.
BOOLEAN MyDeleteRegistryKeyValue(UNICODE_STRING ustrRegistry, UNICODE_STRING ustrKeyValueName) { HANDLE hRegister = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; NTSTATUS status = STATUS_SUCCESS; InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes); if (!NT_SUCCESS(status)) { ShowError("ZwOpenKey", status); return FALSE; } status = ZwDeleteValueKey(hRegister, &ustrKeyValueName); if (!NT_SUCCESS(status)) { ZwClose(hRegister); ShowError("ZwDeleteValueKey", status); return FALSE; } ZwClose(hRegister); return TRUE; }
Esistono altre funzioni che permettono di interagire con il registro di sistema ma queste sono le principali. Infine, segue il codice di DriverEntry per testare le funzioni scritte finora.
VOID DriverUnload(PDRIVER_OBJECT pDriverObject) { } NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp) { NTSTATUS status = STATUS_SUCCESS; pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return status; } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath) { DbgPrint("Enter DriverEntry\n"); NTSTATUS status = STATUS_SUCCESS; pDriverObject->DriverUnload = DriverUnload; for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { pDriverObject->MajorFunction[i] = DriverDefaultHandle; } UNICODE_STRING ustrRegistry; RtlInitUnicodeString(&ustrRegistry, L"\\Registry\\Machine\\Software\\TestReg"); MyCreateRegistryKey(ustrRegistry); MyOpenRegistryKey(ustrRegistry); UNICODE_STRING ustrKeyValueName; WCHAR wstrKeyValueData[] = L"Stringa di prova"; RtlInitUnicodeString(&ustrKeyValueName, L"Test"); MySetRegistryKeyValue(ustrRegistry, ustrKeyValueName, REG_SZ, wstrKeyValueData, sizeof(wstrKeyValueData)); MyQueryRegistryKeyValue(ustrRegistry, ustrKeyValueName); MyDeleteRegistryKeyValue(ustrRegistry, ustrKeyValueName); MyDeleteRegistryKey(ustrRegistry); DbgPrint("Leave DriverEntry\n"); return status; }
Riferimenti:
[1] https://github.com/DemonGan/Windows-Hack-Programming
[2] https://docs.microsoft.com/en-us/windows/win32/api/ntdef/nf-ntdef-initializeobjectattributes
Nessun commento:
Posta un commento