giovedì 3 ottobre 2019

23 - I/O Manager, ci penso io!

Nella scorsa lezione si è visto che è possibile intercettare richieste fatte ad un altro device per soddisfarle o passarle ad un altro device ma, in alcuni casi, si può andare ben oltre. Ad esempio, è possibile creare le richieste (nel senso di costruire l'IRP relativo), inviarle ad altri device e smaltirle una volta soddisfatte, riducendo così al minimo il supporto fornito dall'I/O Manager. In un'altra lezione, dove si è discusso di quella parte della Kernel API specifica per i file (si veda [2]), si sono scritte alcune funzioni che, alla fine, non facevano altro che invocare alcuni metodi dell'API per creare, leggere o scrivere un file (ad esempio ZwCreateFile, ZwReadFile, ZwWriteFile, ecc.). Quello che fanno queste funzioni, in sostanza, è inviare una richiesta al device del disco (in realtà al device del volume montato su tale disco) con l'aiuto dell'I/O Manager. Ma se si volesse evitare di invocare queste funzioni per raggiungere lo stesso risultato si perderebbe inevitabilmente anche il supporto dell'I/O Manager. Poco male perché, in effetti, è possibile costruirsi l'IRP della richiesta ed inviarla al disco direttamente: tutto quello che serve è solo l'indirizzo del device object a cui vogliamo inviare la richiesta, il resto lo farà IoCallDriver, come visto nella lezione precedente.
Il codice di esempio di questa lezione è tratto da [1] e, data la mole rilevante, richiede un approccio diverso da quello usato finora. Benché, nella maggior parte dei casi, si tratti di roba già vista in alcune parti di codice emergono dettagli ad un livello estremamente basso. Spiegare quindi le istruzioni riga per riga, con commenti o con lo spiegone finale dopo una funzione, è qualcosa di improponibile perché risulterebbe semplicemente illeggibile o pesante da digerire. Per questo motivo si è preferito inserire solo i commenti necessari, fornendo link a riferimenti dove poter leggere tutte le spiegazioni del caso. Purtroppo più si va in profondità e più il numero di risorse diminuisce, la documentazione ufficiale diventa più criptica (o praticamente assente) e l'arte di andarsi a cercare le informazioni necessarie diventa indispensabile. Per questo motivo consiglio di non saltare la presente lezione e proseguire nella lettura, leggendo anche tutti i riferimenti inseriti nei commenti al codice. Questo perché contengono un gran numero di informazioni che sicuramente torneranno utili in futuro. Ad ogni modo, tutto quello che fa l'esempio di codice di questa lezione è eseguire una serie di operazioni sui file. In particolare, si crea un file chiamato test.txt in C:\, si scrive e si legge su tale file, si calcola la sua dimensione ed infine si elencano i file presenti nella directory C:\




IRPFILEDRIVER.C

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObjectPUNICODE_STRING pRegPath)
{
 UNREFERENCED_PARAMETER(pRegPath);
 
 DbgPrint("Enter DriverEntry\n");
 NTSTATUS status = STATUS_SUCCESS;
 
 pDriverObject->DriverUnload = DriverUnload;
 
 UNICODE_STRING ustrCreateFile;
 RtlInitUnicodeString(&ustrCreateFile, L"C:\\test.txt");
 MyCreateFile(ustrCreateFile);
 DbgPrint("Create File OK.\n");
 
 UNICODE_STRING ustrWriteFile;
 LARGE_INTEGER liWriteOffset = { 0 };
 UCHAR szWriteData[256] = "Lorem ipsum";
 ULONG ulWriteDataLength = (ULONG)(1 + strlen((PCCH)szWriteData));
 RtlInitUnicodeString(&ustrWriteFile, L"C:\\test.txt");
 MyWriteFile(ustrWriteFile, liWriteOffset, szWriteData, &ulWriteDataLength);
 DbgPrint("MyWriteFile OK.\n");
 
 UNICODE_STRING ustrReadFile;
 LARGE_INTEGER liReadOffset = { 0 };
 UCHAR szReadData[256] = { 0 };
 ULONG ulReadDataLength = 256;
 RtlInitUnicodeString(&ustrReadFile, L"C:\\test.txt");
 MyReadFile(ustrReadFile, liReadOffset, szReadData, &ulReadDataLength);
 DbgPrint("MyReadFile:[%s]\n", szReadData);
 
 UNICODE_STRING ustrFileSize;
 RtlInitUnicodeString(&ustrFileSize, L"C:\\test.txt");
 ULONG64 ullFileSize = MyGetFileSize(ustrFileSize);
 DbgPrint("FileSize = %I64d Bytes\n", ullFileSize);
 
 UNICODE_STRING ustrQueryFile;
 RtlInitUnicodeString(&ustrQueryFile, L"C:\\");
 MyQueryFileAndFileFolder(ustrQueryFile);
 DbgPrint("Query File OK.\n");
 
 DbgPrint("Leave DriverEntry\n");
 return status;
}



FILEMANAGER.C

BOOLEAN MyCreateFile(UNICODE_STRING ustrFilePath)
{
 PFILE_OBJECT hFile = NULL;
 IO_STATUS_BLOCK iosb = { 0 };
 NTSTATUS status = STATUS_SUCCESS;
 
 status = IrpCreateFile(
  &hFile,
  GENERIC_READ,
  &ustrFilePath,
  &iosb,
  NULL,
  FILE_ATTRIBUTE_NORMAL,
  0,
  FILE_OPEN_IF,
  FILE_SYNCHRONOUS_IO_NONALERT,
  NULL,
  0);
 if (!NT_SUCCESS(status))
 {
  ShowError("IrpCreateFile", status);
  return FALSE;
 }
 
 ObDereferenceObject(hFile);
 return TRUE;
}

BOOLEAN MyWriteFile(
 UNICODE_STRING ustrFileName,
 LARGE_INTEGER liOffset,
 PUCHAR pWriteData,
 PULONG pulWriteDataSize)
{
 PFILE_OBJECT hFile = NULL;
 IO_STATUS_BLOCK iosb = { 0 };
 NTSTATUS status = STATUS_SUCCESS;
 
 status = IrpCreateFile(&hFile, GENERIC_WRITE, &ustrFileName, &iosb, NULL,
  FILE_ATTRIBUTE_NORMALFILE_SHARE_READ | FILE_SHARE_WRITEFILE_OPEN_IF,
  FILE_NO_INTERMEDIATE_BUFFERING | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERTNULL, 0);
 if (!NT_SUCCESS(status))
 {
  ShowError("IrpCreateFile", status);
  return FALSE;
 }
 
 RtlZeroMemory(&iosb, sizeof(iosb));
 status = IrpWriteFile(hFile, &iosb,
  pWriteData, *pulWriteDataSize, &liOffset);
 if (!NT_SUCCESS(status))
 {
  *pulWriteDataSize = (ULONG)iosb.Information;
  ObDereferenceObject(hFile);
  ShowError("IrpWriteFile", status);
  return FALSE;
 }
 
 *pulWriteDataSize = (ULONG)iosb.Information;
 ObDereferenceObject(hFile);
 
 return TRUE;
}

BOOLEAN MyReadFile(
 UNICODE_STRING ustrFileName,
 LARGE_INTEGER liOffset,
 PUCHAR pReadData,
 PULONG pulReadDataSize)
{
 PFILE_OBJECT hFile = NULL;
 IO_STATUS_BLOCK iosb = { 0 };
 NTSTATUS status = STATUS_SUCCESS;
 
 status = IrpCreateFile(&hFile, GENERIC_READ, &ustrFileName, &iosb, NULL,
  FILE_ATTRIBUTE_NORMALFILE_SHARE_READ | FILE_SHARE_WRITEFILE_OPEN,
  FILE_NO_INTERMEDIATE_BUFFERING | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  NULL, 0);
 if (!NT_SUCCESS(status))
 {
  ShowError("IrpCreateFile", status);
  return FALSE;
 }
 
 RtlZeroMemory(&iosb, sizeof(iosb));
 status = IrpReadFile(hFile, &iosb,
  pReadData, *pulReadDataSize, &liOffset);
 if (!NT_SUCCESS(status))
 {
  *pulReadDataSize = (ULONG)iosb.Information;
  ObDereferenceObject(hFile);
  ShowError("IrpReadFile", status);
  return FALSE;
 }
 
 *pulReadDataSize = (ULONG)iosb.Information;
 ObDereferenceObject(hFile);
 
 return TRUE;
}

ULONG64 MyGetFileSize(UNICODE_STRING ustrFileName)
{
 PFILE_OBJECT hFile = NULL;
 IO_STATUS_BLOCK iosb = { 0 };
 NTSTATUS status = STATUS_SUCCESS;
 FILE_STANDARD_INFORMATION fsi = { 0 };
 
 status = IrpCreateFile(&hFile, GENERIC_READ, &ustrFileName, &iosb, NULL, 0,
  FILE_SHARE_READFILE_OPENFILE_SYNCHRONOUS_IO_NONALERTNULL, 0);
 if (!NT_SUCCESS(status))
 {
  ShowError("IrpCreateFile", status);
  return 0;
 }
 
 status = IrpQueryInformationFile(
    hFile, 
    &iosb, 
    &fsi, 
    sizeof(FILE_STANDARD_INFORMATION), 
    FileStandardInformation);
 if (!NT_SUCCESS(status))
 {
  ObDereferenceObject(hFile);
  ShowError("IrpQueryInformationFile", status);
  return 0;
 }
 
 ObDereferenceObject(hFile);
 return fsi.EndOfFile.QuadPart;
}

BOOLEAN MyQueryFileAndFileFolder(UNICODE_STRING ustrPath)
{
 PFILE_OBJECT hFile = NULL;
 IO_STATUS_BLOCK iosb = { 0 };
 NTSTATUS status = STATUS_SUCCESS;
 
 // http://www.paminerva.com/2019/08/11-kernel-api-file.html
 status = IrpCreateFile(&hFile, FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_ANY_ACCESS,
  &ustrPath, &iosb, NULLFILE_ATTRIBUTE_NORMALFILE_SHARE_READ | FILE_SHARE_WRITE,
  FILE_OPENFILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
  NULL, 0);
 if (!NT_SUCCESS(status))
 {
  ShowError("IrpCreateFile", status);
  return FALSE;
 }
 
 // http://www.paminerva.com/2019/08/11-kernel-api-file.html
 ULONG ulLength = (2 * 4096 + sizeof(FILE_BOTH_DIR_INFORMATION)) * 0x2000;
 PFILE_BOTH_DIR_INFORMATION pDir = ExAllocatePool(PagedPool, ulLength);
 
 // http://www.paminerva.com/2019/08/11-kernel-api-file.html
 PFILE_BOTH_DIR_INFORMATION pBeginAddr = pDir;
 
 status = IrpQueryDirectoryFile(hFile, &iosb, pDir, ulLength,
  FileBothDirectoryInformationNULL);
 if (!NT_SUCCESS(status))
 {
  ExFreePool(pDir);
  ObDereferenceObject(hFile);
  ShowError("IrpQueryDirectoryFile", status);
  return FALSE;
 }
 
 UNICODE_STRING ustrTemp;
 UNICODE_STRING ustrOne;
 UNICODE_STRING ustrTwo;
 RtlInitUnicodeString(&ustrOne, L".");
 RtlInitUnicodeString(&ustrTwo, L"..");
 WCHAR wcFileName[1024] = { 0 };
 
 while (TRUE)
 {
  // http://www.paminerva.com/2019/08/11-kernel-api-file.html
  RtlZeroMemory(wcFileName, 1024);
  RtlCopyMemory(wcFileName, pDir->FileName, pDir->FileNameLength);
  RtlInitUnicodeString(&ustrTemp, wcFileName);
 
  // http://www.paminerva.com/2019/08/11-kernel-api-file.html
  if ((0 != RtlCompareUnicodeString(&ustrTemp, &ustrOne, TRUE)) &&
   (0 != RtlCompareUnicodeString(&ustrTemp, &ustrTwo, TRUE)))
  {
   if (pDir->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
   {
    DbgPrint("[DIRECTORY]\t%wZ\n", &ustrTemp);
   }
   else
   {
    DbgPrint("[FILE]\t\t%wZ\n", &ustrTemp);
   }
  }
 
  // http://www.paminerva.com/2019/08/11-kernel-api-file.html
  if (pDir->NextEntryOffset == 0)
  {
   DbgPrint("\n[QUERY OVER]\n\n");
   break;
  }
 
  // http://www.paminerva.com/2019/08/11-kernel-api-file.html
  pDir = (PFILE_BOTH_DIR_INFORMATION)((PUCHAR)pDir + pDir->NextEntryOffset);
 }
 
 ExFreePool(pBeginAddr);
 ObDereferenceObject(hFile);
 return TRUE;
}



IRPFILE.H

#include <ntddk.h>
 
// http://www.codewarrior.cn/ntdoc/wrk/se/SeCreateAccessState.htm
NTSTATUS SeCreateAccessState(
 PACCESS_STATE AccessState,
 PVOID AuxData,
 ACCESS_MASK DesiredAccess,
 PGENERIC_MAPPING GenericMapping
);
 
// http://www.codewarrior.cn/ntdoc/wrk/ob/ObCreateObject.htm
NTSTATUS ObCreateObject(
 __in KPROCESSOR_MODE ProbeMode,
 __in POBJECT_TYPE ObjectType,
 __in POBJECT_ATTRIBUTES ObjectAttributes,
 __in KPROCESSOR_MODE OwnershipMode,
 __inout_opt PVOID ParseContext,
 __in ULONG ObjectBodySize,
 __in ULONG PagedPoolCharge,
 __in ULONG NonPagedPoolCharge,
 __out PVOID *Object
);
 
/*
kd> dt nt!_AUX_ACCESS_DATA
   +0x000 PrivilegesUsed   : Ptr64 _PRIVILEGE_SET
   +0x008 GenericMapping   : _GENERIC_MAPPING
   +0x018 AccessesToAudit  : Uint4B
   +0x01c MaximumAuditMask : Uint4B
   +0x020 TransactionId    : _GUID
   +0x030 NewSecurityDescriptor : Ptr64 Void
   +0x038 ExistingSecurityDescriptor : Ptr64 Void
   +0x040 ParentSecurityDescriptor : Ptr64 Void
   +0x048 DeRefSecurityDescriptor : Ptr64     void
   +0x050 SDLock           : Ptr64 Void
   +0x058 AccessReasons    : _ACCESS_REASONS
   +0x0d8 GenerateStagingEvents : UChar
 
 
kd> ?? sizeof(nt!_AUX_ACCESS_DATA)
unsigned int64 0xe0
*/
typedef struct _AUX_ACCESS_DATA {
 PPRIVILEGE_SET PrivilegesUsed;
 GENERIC_MAPPING GenericMapping;
 ACCESS_MASK AccessesToAudit;
 ACCESS_MASK MaximumAuditMask;
 
 // alla fine interessano solo i campi sopra e non è necessaria la
 // dimesione esatta della struttura per quello che serve in questo
 // esempio.
 ULONG Unknown[256];
} AUX_ACCESS_DATA, *PAUX_ACCESS_DATA;



IRPFILE.C

// Funzione di completamento.
NTSTATUS MyCompleteRoutine(
 IN PDEVICE_OBJECT DeviceObject,
 IN PIRP pIrp,
 IN PVOID Context)
{
 UNREFERENCED_PARAMETER(DeviceObject);
 UNREFERENCED_PARAMETER(Context);
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/how-completion-processing-is-performed
 // anche se in questo caso è inutile dato che non c'è client usermode a cui si passa l'IRP 
 *pIrp->UserIosb = pIrp->IoStatus;
 
 // Segnala evento così funzione IrpXXXFile in attesa può proseguire.
 if (pIrp->UserEvent)
 {
  KeSetEvent(pIrp->UserEvent, IO_NO_INCREMENTFALSE);
 }
 
 // Rilascia eventuali risorse
 if (pIrp->MdlAddress)
 {
  IoFreeMdl(pIrp->MdlAddress);
  pIrp->MdlAddress = NULL;
 }
 
 // Rilascia l'IRP dato che l'abbiamo allocato in kernelmode e non deve
 // essere passato o consultato da client in usermode.
 IoFreeIrp(pIrp);
 pIrp = NULL;
 
 // IrpXXXFile è in attesa quindi la richiesta scaturita dalla
 // sua chiamata non può ritenersi conclusa.
 return STATUS_MORE_PROCESSING_REQUIRED;
}

// Crea o apre un a file in modo simile a ZwCreateFile
NTSTATUS IrpCreateFile(
 OUT PFILE_OBJECT *ppFileObject,
 IN ACCESS_MASK DesiredAccess,
 IN PUNICODE_STRING pustrFilePath,
 OUT PIO_STATUS_BLOCK IoStatusBlock,
 IN PLARGE_INTEGER AllocationSize OPTIONAL,
 IN ULONG FileAttributes,
 IN ULONG ShareAccess,
 IN ULONG CreateDisposition,
 IN ULONG CreateOptions,
 IN PVOID EaBuffer OPTIONAL,
 IN ULONG EaLength)
{
 UNREFERENCED_PARAMETER(AllocationSize);
 
 NTSTATUS status = STATUS_SUCCESS;
 ULONG ulFileNameMaxSize = 512;
 WCHAR wszName[100] = { 0 };
 UNICODE_STRING ustrRootPath;
 OBJECT_ATTRIBUTES objectAttributes = { 0 };
 HANDLE hRootFile = NULL;
 PFILE_OBJECT pRootFileObject = NULL, pFileObject = NULL;
 PDEVICE_OBJECT RootDeviceObject = NULL, RootRealDevice = NULL;
 PIRP pIrp = NULL;
 KEVENT kEvent = { 0 };
 ACCESS_STATE accessData = { 0 };
 AUX_ACCESS_DATA auxAccessData = { 0 };
 IO_SECURITY_CONTEXT ioSecurityContext = { 0 };
 PIO_STACK_LOCATION pIoStackLocation = NULL;
 
 wcscpy(wszName, L"\\??\\A:\\");
 wszName[4] = pustrFilePath->Buffer[0]; // sostituisce A con lettera del volume corretto
 RtlInitUnicodeString(&ustrRootPath, wszName);
 DbgPrint("RootPath:%wZ\n", &ustrRootPath);
 InitializeObjectAttributes(&objectAttributes, &ustrRootPath, OBJ_KERNEL_HANDLENULLNULL);
 
 // Recupera handle al file object relativo al volume montato sul disco.
 status = IoCreateFile(&hRootFile, GENERIC_READ | SYNCHRONIZE,
  &objectAttributes, IoStatusBlockNULLFILE_ATTRIBUTE_NORMAL,
  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  FILE_OPENFILE_SYNCHRONOUS_IO_NONALERTNULL, 0, CreateFileTypeNone,
  NULLIO_NO_PARAMETER_CHECKING);
 if (!NT_SUCCESS(status))
 {
  DbgPrint("IoCreateFile Error[0x%X]", status);
  return status;
 }
 
 // Recupera il file object collegato al volume montato sul disco
 status = ObReferenceObjectByHandle(
  hRootFile,
  FILE_READ_ACCESS,
  *IoFileObjectType,
  KernelMode,
  &pRootFileObject,
  NULL);
 if (!NT_SUCCESS(status))
 {
  ZwClose(hRootFile);
  DbgPrint("ObReferenceObjectByHandle Error[0x%X]\n", status);
  return status;
 }
 
 // Volume Parameter Block (VPB)
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_file_object
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_vpb
 // DeviceObject è il device object che rappresenta il volume montato.
 // RealDevice è il device object che rappresenta il disco fisico o virtuale che contiene 
 // il volume montato.
 RootDeviceObject = pRootFileObject->Vpb->DeviceObject;
 RootRealDevice = pRootFileObject->Vpb->RealDevice;
 
 // Non servono più dato che ora possiamo agire direttamente sui device object recuperati.
 ObDereferenceObject(pRootFileObject);
 ZwClose(hRootFile);
 
 // Crea un IRP
 // primo param.: numero di stack location che seguono l'IRP
 // secondo param.: TRUE se la memoria allocata deve essere "addebitata" allo spazio del 
 // processo corrente (solitamente FALSE).
 pIrp = IoAllocateIrp(RootDeviceObject->StackSize, FALSE);
 if (pIrp == NULL)
 {
  ObDereferenceObject(pFileObject);
  DbgPrint("IoAllocateIrp Error!\n");
  return STATUS_UNSUCCESSFUL;
 }
 
 // Inizializza evento su cui IrpCreateFile resterà in attesa finchè la richiesta a 
 // RootDeviceObject sarà stata completata.
 // Necessario perchè inviamo una richiesta a RootDeviceObject e dobbiamo essere informati 
 // quando questa verrà completata per proseguire e impostare il parametro di output con 
 // il puntatore al file object del file da creare o aprire.
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-keinitializeevent
 KeInitializeEvent(&kEvent, SynchronizationEventFALSE);
 
 // Crea una istanza vuota di file object.
 // L'I/O Manager crea un file object che rappresenta il file a prescindere dal fatto che
 // questo sia da creare o semplicemente da aprire.
 // https://docs.microsoft.com/it-it/windows-hardware/drivers/ifs/irp-mj-create
 InitializeObjectAttributes(&objectAttributes, NULLOBJ_CASE_INSENSITIVENULLNULL);
 status = ObCreateObject(KernelMode, *IoFileObjectType, &objectAttributes,
  KernelModeNULLsizeof(FILE_OBJECT), 0, 0, &pFileObject);
 if (!NT_SUCCESS(status))
 {
  DbgPrint("ObCreateObject Error[0x%X]\n", status);
  return status;
 }
 
 // Imposta istanza appena creata di file object
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_file_object
 RtlZeroMemory(pFileObject, sizeof(FILE_OBJECT));
 pFileObject->Type = IO_TYPE_FILE;
 pFileObject->Size = sizeof(FILE_OBJECT);
 pFileObject->DeviceObject = RootRealDevice; // Device reale a cui appartiene il file
 pFileObject->Flags = FO_SYNCHRONOUS_IO;
 pFileObject->FileName.Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, ulFileNameMaxSize);
 pFileObject->FileName.MaximumLength = (USHORT)ulFileNameMaxSize;
 pFileObject->FileName.Length = pustrFilePath->Length - 4; // salta C: (vedi sotto)
 RtlZeroMemory(pFileObject->FileName.Buffer, ulFileNameMaxSize);
 RtlCopyMemory(pFileObject->FileName.Buffer, &pustrFilePath->Buffer[2],
  pFileObject->FileName.Length); // salta C: (o qualunque sia la lettera del volume)
 DbgPrint("pFileObject->FileName:%wZ\n", &pFileObject->FileName);
 KeInitializeEvent(&pFileObject->Lock, SynchronizationEventFALSE);
 KeInitializeEvent(&pFileObject->Event, NotificationEventFALSE);
 
 // SeCreateAccessState crea uno stato di accesso.
 // La struttura ACCESS_STATE descrive lo stato di un accesso in corso.
 // GENERIC_MAPPING è una struttura che descrive il mapping tra generici e specifici 
 // permessi di accesso.
 // IoGetFileObjectGenericMapping ritorna informazioni sul mapping tra permessi di accesso 
 // generici e l'insieme dei permessi di accesso specifici per i file.
 // http://www.codewarrior.cn/ntdoc/wrk/se/SeCreateAccessState.htm
 RtlZeroMemory(&auxAccessData, sizeof(auxAccessData));
 status = SeCreateAccessState(
  &accessData,
  &auxAccessData,
  DesiredAccess,
  IoGetFileObjectGenericMapping());
 if (!NT_SUCCESS(status))
 {
  IoFreeIrp(pIrp);
  ObDereferenceObject(pFileObject);
  DbgPrint("SeCreateAccessState Error[0x%X]\n", status);
  return status;
 }
 
 // Imposta istanza di tipo IO_SECURITY_CONTEXT
 // IO_SECURITY_CONTEXT rappresenta il contesto di sicurezza di una richiesta IRP_MJ_CREATE.
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_io_security_context
 ioSecurityContext.SecurityQos = NULL;
 ioSecurityContext.AccessState = &accessData;
 ioSecurityContext.DesiredAccess = DesiredAccess;
 ioSecurityContext.FullCreateOptions = 0;
 
 // Imposta IRP
 // https://docs.microsoft.com/it-it/windows-hardware/drivers/ifs/irp-mj-create
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/how-completion-processing-is-performed
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_irp
 // Windows NT File System Internals (libro datato ma ancora utilissimo in certi contesti)
 RtlZeroMemory(IoStatusBlocksizeof(IO_STATUS_BLOCK));
 pIrp->MdlAddress = NULL;
 pIrp->AssociatedIrp.SystemBuffer = EaBuffer// NULL
 pIrp->Flags = IRP_CREATE_OPERATION | IRP_SYNCHRONOUS_API;
 pIrp->RequestorMode = KernelMode;
 pIrp->UserIosb = IoStatusBlock;
 pIrp->UserEvent = &kEvent;
 pIrp->PendingReturned = FALSE;
 pIrp->Cancel = FALSE;
 pIrp->CancelRoutine = NULL;
 pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
 pIrp->Tail.Overlay.AuxiliaryBuffer = NULL;
 pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
 
 // Prima di chiamare IoCallDriver bisogna impostare prossimo stack location
 // che sarà quello che vedrà il driver del prossimo device sullo stack.
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_io_stack_location
 pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
 pIoStackLocation->MajorFunction = IRP_MJ_CREATE;
 pIoStackLocation->DeviceObject = RootDeviceObject;
 pIoStackLocation->FileObject = pFileObject;
 
 // https://docs.microsoft.com/it-it/windows-hardware/drivers/ifs/irp-mj-create
 pIoStackLocation->Parameters.Create.SecurityContext = &ioSecurityContext;
 pIoStackLocation->Parameters.Create.Options = (CreateDisposition << 24) | CreateOptions;
 pIoStackLocation->Parameters.Create.FileAttributes = (USHORT)FileAttributes;
 pIoStackLocation->Parameters.Create.ShareAccess = (USHORT)ShareAccess;
 pIoStackLocation->Parameters.Create.EaLength = EaLength;
 
 // Dobbiamo impostare un metodo che viene chiamato al completamento della richiesta a
 // RootDeviceObject perchè dobbiamo segnalare l'evento e liberare le risorse allocate.
 IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULLTRUETRUETRUE);
 
 // Inoltra rihiesta a device più in basso sullo stack
 status = IoCallDriver(RootDeviceObject, pIrp);
 
 // Attende il completamento della richiesta
 if (status == STATUS_PENDING)
 {
  KeWaitForSingleObject(&kEvent, ExecutiveKernelModeTRUENULL);
 }
 
 // Determina il risultato del completamento della richiesta
 status = IoStatusBlock->Status;
 if (!NT_SUCCESS(status))
 {
  ObDereferenceObject(pFileObject);
  DbgPrint("IRP FAILED!\n");
  return status;
 }
 
 // Incrementa numero di riferimenti al file object
 InterlockedIncrement((PLONG)&pFileObject->DeviceObject->ReferenceCount);
 if (pFileObject->Vpb)
 {
  InterlockedIncrement((PLONG)&pFileObject->Vpb->ReferenceCount);
 }
 
 // Ritorna puntatore al file object impostando il parametro di output.
 *ppFileObject = pFileObject;
 
 return status;
}

// Scrive un a file in modo simile a ZwWriteFile
NTSTATUS IrpWriteFile(
 IN PFILE_OBJECT pFileObject,
 OUT PIO_STATUS_BLOCK IoStatusBlock,
 IN PVOID Buffer,
 IN ULONG Length,
 IN PLARGE_INTEGER ByteOffset OPTIONAL
)
{
 NTSTATUS status = STATUS_SUCCESS;
 PIRP pIrp = NULL;
 KEVENT kEvent = { 0 };
 PIO_STACK_LOCATION pIoStackLocation = NULL;
 PDEVICE_OBJECT pDevObj = NULL;
 
 if ((pFileObject == NULL) ||
  (pFileObject->Vpb == NULL) ||
  (pFileObject->Vpb->DeviceObject == NULL))
 {
  return STATUS_UNSUCCESSFUL;
 }
 
 // Salva offset corrente del file da cui iniziare a scrivere.
 if (ByteOffset == NULL)
 {
  // Se l'operazione sul file non può avvenire in modo sincronizzato lascia perdere.
  if ((pFileObject->Flags & FO_SYNCHRONOUS_IO) == 0)
  {
   return STATUS_INVALID_PARAMETER;
  }
 
  ByteOffset = &pFileObject->CurrentByteOffset;
 }
 
 pDevObj = pFileObject->Vpb->DeviceObject;
 
 pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
 if (pIrp == NULL)
 {
  return STATUS_UNSUCCESSFUL;
 }
 
 KeInitializeEvent(&kEvent, SynchronizationEventFALSE);
 
 // File System usa Direct I/O.
 // Crea MDL per memoria puntata da Buffer.
 pIrp->MdlAddress = MmCreateMdl(NULLBufferLength);
 if (pIrp->MdlAddress == NULL)
 {
  IoFreeIrp(pIrp);
  return STATUS_INSUFFICIENT_RESOURCES;
 }
 
 // Non è necessario invocare MmProbeAndLockPages perchè nel parametro Buffer è stata
 // passata una variabile locale definita in kernel mode (kernel stack è non-paged) 
 // quindi superfluo effettuare controlli di validità ed il lock di tale memoria.
 // Se si è in questo caso MmBuildMdlForNonPagedPool aggiorna la struttura MDL affinché
 // descriva in modo corretto le relative pagine fisiche.
 // Per una spiegazione più tecnica si vedano:
 // http://www.codewarrior.cn/ntdoc/wrk/mm/MmBuildMdlForNonPagedPool.htm
 // Windows Internals (qualsiasi edizione, cercare page frame number)
 // Windows NT File System Internals (cercare page frame number)
 // // https://www.osronline.com/article.cfm%5Eid=423.htm
 MmBuildMdlForNonPagedPool(pIrp->MdlAddress);
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_irp
 pIrp->UserEvent = &kEvent;
 pIrp->UserIosb = IoStatusBlock;
 pIrp->Flags = IRP_WRITE_OPERATION;
 pIrp->RequestorMode = KernelMode;
 pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
 pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_io_stack_location
 pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
 pIoStackLocation->MajorFunction = IRP_MJ_WRITE;
 pIoStackLocation->MinorFunction = IRP_MN_NORMAL;
 pIoStackLocation->DeviceObject = pDevObj;
 pIoStackLocation->FileObject = pFileObject;
 
 // https://docs.microsoft.com/it-it/windows-hardware/drivers/ifs/irp-mj-write
 pIoStackLocation->Parameters.Write.Length = Length;
 pIoStackLocation->Parameters.Write.ByteOffset = *ByteOffset;
 
 IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULLTRUETRUETRUE);
 
 status = IoCallDriver(pDevObj, pIrp);
 
 if (status == STATUS_PENDING)
 {
  KeWaitForSingleObject(&kEvent, ExecutiveKernelModeFALSENULL);
 }
 
 status = IoStatusBlock->Status;
 if (!NT_SUCCESS(status))
 {
  return status;
 }
 return status;
}

// Legge un a file in modo simile a ZwReadFile
NTSTATUS IrpReadFile(
 IN PFILE_OBJECT pFileObject,
 OUT PIO_STATUS_BLOCK IoStatusBlock,
 OUT PVOID Buffer,
 IN ULONG Length,
 IN PLARGE_INTEGER ByteOffset OPTIONAL
)
{
 NTSTATUS status = STATUS_SUCCESS;
 PIRP pIrp = NULL;
 KEVENT kEvent = { 0 };
 PIO_STACK_LOCATION pIoStackLocation = NULL;
 PDEVICE_OBJECT pDevObj = NULL;
 
 if ((pFileObject == NULL) ||
  (pFileObject->Vpb == NULL) ||
  (pFileObject->Vpb->DeviceObject == NULL))
 {
  return STATUS_UNSUCCESSFUL;
 }
 
 if (NULL == ByteOffset)
 {
  if ((pFileObject->Flags & FO_SYNCHRONOUS_IO) == 0)
  {
   return STATUS_INVALID_PARAMETER;
  }
  ByteOffset = &pFileObject->CurrentByteOffset;
 }
 
 pDevObj = pFileObject->Vpb->DeviceObject;
 
 pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
 if (pIrp == NULL)
 {
  return STATUS_UNSUCCESSFUL;
 }
 
 KeInitializeEvent(&kEvent, SynchronizationEventFALSE);
 
 RtlZeroMemory(BufferLength);
 pIrp->MdlAddress = MmCreateMdl(NULLBufferLength);
 if (pIrp->MdlAddress == NULL)
 {
  IoFreeIrp(pIrp);
  return STATUS_INSUFFICIENT_RESOURCES;
 }
 MmBuildMdlForNonPagedPool(pIrp->MdlAddress);
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_irp
 pIrp->UserEvent = &kEvent;
 pIrp->UserIosb = IoStatusBlock;
 pIrp->Flags = IRP_READ_OPERATION;
 pIrp->RequestorMode = KernelMode;
 pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
 pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_io_stack_location
 pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
 pIoStackLocation->MajorFunction = IRP_MJ_READ;
 pIoStackLocation->MinorFunction = IRP_MN_NORMAL;
 pIoStackLocation->DeviceObject = pDevObj;
 pIoStackLocation->FileObject = pFileObject;
 
 // https://docs.microsoft.com/it-it/windows-hardware/drivers/ifs/irp-mj-read
 pIoStackLocation->Parameters.Read.Length = Length;
 pIoStackLocation->Parameters.Read.ByteOffset = *ByteOffset;
 
 IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULLTRUETRUETRUE);
 
 status = IoCallDriver(pDevObj, pIrp);
 
 if (status == STATUS_PENDING)
 {
  KeWaitForSingleObject(&kEvent, ExecutiveKernelModeFALSENULL);
 }
 
 status = IoStatusBlock->Status;
 if (!NT_SUCCESS(status))
 {
  return status;
 }
 return status;
}

// Recupera info su file in modo simile a ZwQueryInformationFile
NTSTATUS IrpQueryInformationFile(
 IN PFILE_OBJECT pFileObject,
 OUT PIO_STATUS_BLOCK IoStatusBlock,
 OUT PVOID FileInformation,
 IN ULONG Length,
 IN FILE_INFORMATION_CLASS FileInformationClass
)
{
 NTSTATUS status = STATUS_SUCCESS;
 PIRP pIrp = NULL;
 KEVENT kEvent = { 0 };
 PIO_STACK_LOCATION pIoStackLocation = NULL;
 PDEVICE_OBJECT pDevObj = NULL;
 
 if ((pFileObject == NULL) ||
  (pFileObject->Vpb == NULL) ||
  (pFileObject->Vpb->DeviceObject == NULL))
 {
  return STATUS_UNSUCCESSFUL;
 }
 
 pDevObj = pFileObject->Vpb->DeviceObject;
 
 pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
 if (pIrp == NULL)
 {
  return STATUS_UNSUCCESSFUL;
 }
 
 KeInitializeEvent(&kEvent, SynchronizationEventFALSE);
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_irp
 // https://docs.microsoft.com/it-it/windows-hardware/drivers/ifs/irp-mj-query-information
 RtlZeroMemory(FileInformationLength);
 pIrp->UserEvent = &kEvent;
 pIrp->UserIosb = IoStatusBlock;
 pIrp->AssociatedIrp.SystemBuffer = FileInformation// buffer di output che conterrà dati raccolti
 pIrp->RequestorMode = KernelMode;
 pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
 pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
 
 // https://docs.microsoft.com/it-it/windows-hardware/drivers/ifs/irp-mj-query-information
 pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
 pIoStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION;
 pIoStackLocation->DeviceObject = pDevObj;
 pIoStackLocation->FileObject = pFileObject;
 pIoStackLocation->Parameters.QueryFile.Length = Length;
 pIoStackLocation->Parameters.QueryFile.FileInformationClass = FileInformationClass;
 
 IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULLTRUETRUETRUE);
 
 status = IoCallDriver(pDevObj, pIrp);
 
 if (status == STATUS_PENDING)
 {
  KeWaitForSingleObject(&kEvent, ExecutiveKernelModeFALSENULL);
 }
 
 status = IoStatusBlock->Status;
 if (!NT_SUCCESS(status))
 {
  return status;
 }
 return status;
}

// Recupera info sui file in una directory modo simile a ZwQueryDirectoryFile
NTSTATUS IrpQueryDirectoryFile(
 IN PFILE_OBJECT pFileObject,
 OUT PIO_STATUS_BLOCK IoStatusBlock,
 OUT PVOID FileInformation,
 IN ULONG Length,
 IN FILE_INFORMATION_CLASS FileInformationClass,
 IN PUNICODE_STRING FileName OPTIONAL)
{
 NTSTATUS status = STATUS_SUCCESS;
 PIRP pIrp = NULL;
 KEVENT kEvent = { 0 };
 PIO_STACK_LOCATION pIoStackLocation = NULL;
 PDEVICE_OBJECT pDevObj = NULL;
 
 if ((pFileObject == NULL) ||
  (pFileObject->Vpb == NULL) ||
  (pFileObject->Vpb->DeviceObject == NULL))
 {
  return STATUS_UNSUCCESSFUL;
 }
 
 pDevObj = pFileObject->Vpb->DeviceObject;
 
 pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
 if (pIrp == NULL)
 {
  return STATUS_UNSUCCESSFUL;
 }
 
 KeInitializeEvent(&kEvent, SynchronizationEventFALSE);
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_irp
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/irp-mj-directory-control
 RtlZeroMemory(FileInformationLength);
 pIrp->UserEvent = &kEvent;
 pIrp->UserIosb = IoStatusBlock;
 pIrp->UserBuffer = FileInformation// buffer di output che conterrà dati raccolti
 pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
 pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/how-completion-processing-is-performed
 // In questo caso l'IRP l'abbiamo creato noi in kernelmode quindi non c'è nessun 
 // client usermode che avrebbe potuto richiedere l'invocazione di un metodo di completamento.
 pIrp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
 
 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/irp-mj-directory-control
 pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
 pIoStackLocation->MajorFunction = IRP_MJ_DIRECTORY_CONTROL;
 pIoStackLocation->MinorFunction = IRP_MN_QUERY_DIRECTORY;
 pIoStackLocation->FileObject = pFileObject;
 pIoStackLocation->Flags = SL_RESTART_SCAN;
 pIoStackLocation->Parameters.QueryDirectory.Length = Length;
 pIoStackLocation->Parameters.QueryDirectory.FileName = FileName// NULL
 pIoStackLocation->Parameters.QueryDirectory.FileInformationClass = FileInformationClass;
 
 IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULLTRUETRUETRUE);
 
 status = IoCallDriver(pDevObj, pIrp);
 
 if (status == STATUS_PENDING)
 {
  KeWaitForSingleObject(&kEvent, ExecutiveKernelModeFALSENULL);
 }
 
 status = IoStatusBlock->Status;
 if (!NT_SUCCESS(status))
 {
  return status;
 }
 return status;
}




Codice sorgente:
IRPFileDriver.zip



Riferimenti:
[1] https://github.com/dearfuture/WindowsHack/tree/master/内核层/2/文件管理之IRP/IrpFile_Test/IrpFile_Test
[2] 11 - Kernel API: File

Nessun commento:

Posta un commento