/* * Copyright (c) 1999 - 2003 * NetGroup, Politecnico di Torino (Italy) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Politecnico di Torino nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include "debug.h" #include "packet.h" #include "win_bpf.h" //------------------------------------------------------------------- NTSTATUS NPF_OpenDumpFile(POPEN_INSTANCE Open , PUNICODE_STRING fileName, BOOLEAN Append) { NTSTATUS ntStatus; IO_STATUS_BLOCK IoStatus; OBJECT_ATTRIBUTES ObjectAttributes; PWCHAR PathPrefix; USHORT PathLen; UNICODE_STRING FullFileName; ULONG FullFileNameLength; PDEVICE_OBJECT fsdDevice; IF_LOUD(DbgPrint("NPF: OpenDumpFile.\n");) if(fileName->Buffer[0] == L'\\' && fileName->Buffer[1] == L'?' && fileName->Buffer[2] == L'?' && fileName->Buffer[3] == L'\\' ){ PathLen = 0; } else{ PathPrefix = L"\\??\\"; PathLen = 8; } // Insert the correct path prefix. FullFileNameLength = PathLen + fileName->MaximumLength; FullFileName.Buffer = ExAllocatePoolWithTag(NonPagedPool, FullFileNameLength, '0DWA'); if (FullFileName.Buffer == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; return ntStatus; } FullFileName.Length = PathLen; FullFileName.MaximumLength = (USHORT)FullFileNameLength; if(PathLen) RtlMoveMemory (FullFileName.Buffer, PathPrefix, PathLen); RtlAppendUnicodeStringToString (&FullFileName, fileName); IF_LOUD(DbgPrint( "Packet: Attempting to open %wZ\n", &FullFileName);) InitializeObjectAttributes ( &ObjectAttributes, &FullFileName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // Create the dump file ntStatus = ZwCreateFile( &Open->DumpFileHandle, SYNCHRONIZE | FILE_WRITE_DATA, &ObjectAttributes, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, (Append)?FILE_OPEN_IF:FILE_SUPERSEDE, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if ( !NT_SUCCESS( ntStatus ) ) { IF_LOUD(DbgPrint("NPF: Error opening file %x\n", ntStatus);) ExFreePool(FullFileName.Buffer); Open->DumpFileHandle=NULL; ntStatus = STATUS_NO_SUCH_FILE; return ntStatus; } ExFreePool(FullFileName.Buffer); ntStatus = ObReferenceObjectByHandle(Open->DumpFileHandle, FILE_WRITE_ACCESS, *IoFileObjectType, KernelMode, &Open->DumpFileObject, 0); if ( !NT_SUCCESS( ntStatus ) ) { IF_LOUD(DbgPrint("NPF: Error creating file, status=%x\n", ntStatus);) ZwClose( Open->DumpFileHandle ); Open->DumpFileHandle=NULL; ntStatus = STATUS_NO_SUCH_FILE; return ntStatus; } fsdDevice = IoGetRelatedDeviceObject(Open->DumpFileObject); IF_LOUD(DbgPrint("NPF: Dump: write file created succesfully, status=%d \n",ntStatus);) return ntStatus; } //------------------------------------------------------------------- NTSTATUS NPF_StartDump(POPEN_INSTANCE Open) { NTSTATUS ntStatus; struct packet_file_header hdr; IO_STATUS_BLOCK IoStatus; IF_LOUD(DbgPrint("NPF: StartDump.\n");) // Init the file header hdr.magic = TCPDUMP_MAGIC; hdr.version_major = PCAP_VERSION_MAJOR; hdr.version_minor = PCAP_VERSION_MINOR; hdr.thiszone = 0; /*Currently not set*/ hdr.snaplen = 1514; hdr.sigfigs = 0; // Detect the medium type switch (Open->Medium){ case NdisMediumWan: hdr.linktype = DLT_EN10MB; break; case NdisMedium802_3: hdr.linktype = DLT_EN10MB; break; case NdisMediumFddi: hdr.linktype = DLT_FDDI; break; case NdisMedium802_5: hdr.linktype = DLT_IEEE802; break; case NdisMediumArcnet878_2: hdr.linktype = DLT_ARCNET; break; case NdisMediumAtm: hdr.linktype = DLT_ATM_RFC1483; break; default: hdr.linktype = DLT_EN10MB; } // Write the header. // We can use ZwWriteFile because we are in the context of the application ntStatus = ZwWriteFile(Open->DumpFileHandle, NULL, NULL, NULL, &IoStatus, &hdr, sizeof(hdr), NULL, NULL ); if ( !NT_SUCCESS( ntStatus ) ) { IF_LOUD(DbgPrint("NPF: Error dumping file %x\n", ntStatus);) ZwClose( Open->DumpFileHandle ); Open->DumpFileHandle=NULL; ntStatus = STATUS_NO_SUCH_FILE; return ntStatus; } Open->DumpOffset.QuadPart=24; ntStatus = PsCreateSystemThread(&Open->DumpThreadHandle, THREAD_ALL_ACCESS, (ACCESS_MASK)0L, 0, 0, NPF_DumpThread, Open); if ( !NT_SUCCESS( ntStatus ) ) { IF_LOUD(DbgPrint("NPF: Error creating dump thread, status=%x\n", ntStatus);) ZwClose( Open->DumpFileHandle ); Open->DumpFileHandle=NULL; return ntStatus; } ntStatus = ObReferenceObjectByHandle(Open->DumpThreadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, &Open->DumpThreadObject, 0); if ( !NT_SUCCESS( ntStatus ) ) { IF_LOUD(DbgPrint("NPF: Error creating dump thread, status=%x\n", ntStatus);) ObDereferenceObject(Open->DumpFileObject); ZwClose( Open->DumpFileHandle ); Open->DumpFileHandle=NULL; return ntStatus; } return ntStatus; } //------------------------------------------------------------------- // Dump Thread //------------------------------------------------------------------- VOID NPF_DumpThread(POPEN_INSTANCE Open) { // ULONG FrozenNic; IF_LOUD(DbgPrint("NPF: In the work routine. Parameter = 0x%0x\n",Open);) while(TRUE){ // Wait until some packets arrive or the timeout expires NdisWaitEvent(&Open->DumpEvent, 5000); IF_LOUD(DbgPrint("NPF: Worker Thread - event signalled\n");) if(Open->DumpLimitReached || Open->Size==0){ // BufSize=0 means that this instance was closed, or that the buffer is too // small for any capture. In both cases it is better to end the dump IF_LOUD(DbgPrint("NPF: Worker Thread - Exiting happily\n");) IF_LOUD(DbgPrint("Thread: Dumpoffset=%I64d\n",Open->DumpOffset.QuadPart);) PsTerminateSystemThread(STATUS_SUCCESS); return; } NdisResetEvent(&Open->DumpEvent); // Write the content of the buffer to the file if(NPF_SaveCurrentBuffer(Open) != STATUS_SUCCESS){ PsTerminateSystemThread(STATUS_SUCCESS); return; } } } //------------------------------------------------------------------- NTSTATUS NPF_SaveCurrentBuffer(POPEN_INSTANCE Open) { #if 0 Thead=Open->Bhead; Ttail=Open->Btail; TLastByte=Open->BLastByte; IF_LOUD(DbgPrint("NPF: NPF_SaveCurrentBuffer.\n");) // Get the address of the buffer CurrBuff=Open->Buffer; // // Fill the application buffer // if( Ttail < Thead ) { if(Open->MaxDumpBytes && (UINT)Open->DumpOffset.QuadPart /*+ GetBuffOccupation(Open)*/ > Open->MaxDumpBytes) { // Size limit reached UINT PktLen; SizeToDump = 0; // Scan the buffer to detect the exact amount of data to save while(TRUE){ PktLen = ((struct sf_pkthdr*)(CurrBuff + Thead + SizeToDump))->caplen + sizeof(struct sf_pkthdr); if((UINT)Open->DumpOffset.QuadPart + SizeToDump + PktLen > Open->MaxDumpBytes) break; SizeToDump += PktLen; } } else SizeToDump = TLastByte-Thead; lMdl=IoAllocateMdl(CurrBuff+Thead, SizeToDump, FALSE, FALSE, NULL); if (lMdl == NULL) { // No memory: stop dump IF_LOUD(DbgPrint("NPF: dump thread: Failed to allocate Mdl\n");) return STATUS_UNSUCCESSFUL; } MmBuildMdlForNonPagedPool(lMdl); // Write to disk NPF_WriteDumpFile(Open->DumpFileObject, &Open->DumpOffset, SizeToDump, lMdl, &IoStatus); IoFreeMdl(lMdl); if(!NT_SUCCESS(IoStatus.Status)){ // Error return STATUS_UNSUCCESSFUL; } if(SizeToDump != TLastByte-Thead){ // Size limit reached. Open->DumpLimitReached = TRUE; // Awake the application KeSetEvent(Open->ReadEvent,0,FALSE); return STATUS_UNSUCCESSFUL; } // Update the packet buffer Open->DumpOffset.QuadPart+=(TLastByte-Thead); Open->BLastByte=Ttail; Open->Bhead=0; } if( Ttail > Thead ){ if(Open->MaxDumpBytes && (UINT)Open->DumpOffset.QuadPart /* +GetBuffOccupation(Open)*/ > Open->MaxDumpBytes) { // Size limit reached UINT PktLen; SizeToDump = 0; // Scan the buffer to detect the exact amount of data to save while(Thead + SizeToDump < Ttail){ PktLen = ((struct sf_pkthdr*)(CurrBuff + Thead + SizeToDump))->caplen + sizeof(struct sf_pkthdr); if((UINT)Open->DumpOffset.QuadPart + SizeToDump + PktLen > Open->MaxDumpBytes) break; SizeToDump += PktLen; } } else SizeToDump = Ttail-Thead; lMdl=IoAllocateMdl(CurrBuff+Thead, SizeToDump, FALSE, FALSE, NULL); if (lMdl == NULL) { // No memory: stop dump IF_LOUD(DbgPrint("NPF: dump thread: Failed to allocate Mdl\n");) return STATUS_UNSUCCESSFUL; } MmBuildMdlForNonPagedPool(lMdl); // Write to disk NPF_WriteDumpFile(Open->DumpFileObject, &Open->DumpOffset, SizeToDump, lMdl, &IoStatus); IoFreeMdl(lMdl); if(!NT_SUCCESS(IoStatus.Status)){ // Error return STATUS_UNSUCCESSFUL; } if(SizeToDump != Ttail-Thead){ // Size limit reached. Open->DumpLimitReached = TRUE; // Awake the application KeSetEvent(Open->ReadEvent,0,FALSE); return STATUS_UNSUCCESSFUL; } // Update the packet buffer Open->DumpOffset.QuadPart+=(Ttail-Thead); Open->Bhead=Ttail; } #endif return STATUS_SUCCESS; } //------------------------------------------------------------------- NTSTATUS NPF_CloseDumpFile(POPEN_INSTANCE Open){ #if 0 IF_LOUD(DbgPrint("NPF: NPF_CloseDumpFile.\n");) IF_LOUD(DbgPrint("Dumpoffset=%d\n",Open->DumpOffset.QuadPart);) DbgPrint("1\n"); // Consistency check if(Open->DumpFileHandle == NULL) return STATUS_UNSUCCESSFUL; DbgPrint("2\n"); ZwClose( Open->DumpFileHandle ); ObDereferenceObject(Open->DumpFileObject); /* if(Open->DumpLimitReached == TRUE) // Limit already reached: don't save the rest of the buffer. return STATUS_SUCCESS; */ DbgPrint("3\n"); NPF_OpenDumpFile(Open,&Open->DumpFileName, TRUE); // Flush the buffer to file NPF_SaveCurrentBuffer(Open); // Close The file ObDereferenceObject(Open->DumpFileObject); ZwClose( Open->DumpFileHandle ); Open->DumpFileHandle = NULL; ObDereferenceObject(Open->DumpFileObject); #endif return STATUS_SUCCESS; } //------------------------------------------------------------------- static NTSTATUS PacketDumpCompletion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context) { // Copy the status information back into the "user" IOSB *Irp->UserIosb = Irp->IoStatus; // Wake up the mainline code KeSetEvent(Irp->UserEvent, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } //------------------------------------------------------------------- VOID NPF_WriteDumpFile(PFILE_OBJECT FileObject, PLARGE_INTEGER Offset, ULONG Length, PMDL Mdl, PIO_STATUS_BLOCK IoStatusBlock) { PIRP irp; KEVENT event; PIO_STACK_LOCATION ioStackLocation; PDEVICE_OBJECT fsdDevice = IoGetRelatedDeviceObject(FileObject); // Set up the event we'll use KeInitializeEvent(&event, SynchronizationEvent, FALSE); // Allocate and build the IRP we'll be sending to the FSD irp = IoAllocateIrp(fsdDevice->StackSize, FALSE); if (!irp) { // Allocation failed, presumably due to memory allocation failure IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES; IoStatusBlock->Information = 0; return; } irp->MdlAddress = Mdl; irp->UserEvent = &event; irp->UserIosb = IoStatusBlock; irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->Tail.Overlay.OriginalFileObject= FileObject; irp->RequestorMode = KernelMode; // Indicate that this is a WRITE operation irp->Flags = IRP_WRITE_OPERATION; // Set up the next I/O stack location ioStackLocation = IoGetNextIrpStackLocation(irp); ioStackLocation->MajorFunction = IRP_MJ_WRITE; ioStackLocation->MinorFunction = 0; ioStackLocation->DeviceObject = fsdDevice; ioStackLocation->FileObject = FileObject; IoSetCompletionRoutine(irp, PacketDumpCompletion, 0, TRUE, TRUE, TRUE); ioStackLocation->Parameters.Write.Length = Length; ioStackLocation->Parameters.Write.ByteOffset = *Offset; // Send it on. Ignore the return code (void) IoCallDriver(fsdDevice, irp); // Wait for the I/O to complete. KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0); // Free the IRP now that we are done with it IoFreeIrp(irp); return; }