Sure.
Global variable:
My structure for linked list:
Code: Select allstruct _FILE_FOLDER_INFO {
WCHAR path[MAX_PATH];
struct _FILE_FOLDER_INFO* next;
};
typedef struct _FILE_FOLDER_INFO FILE_FOLDER_INFO, *PFILE_FOLDER_INFO;
IOCTL 1:
Code: Select allINT32 linkListSize = 0;
szBuffer = (WCHAR*)Irp->AssociatedIrp.SystemBuffer;
if (szBuffer)
{
if (NT_SUCCESS(EnumFiles(szBuffer, &linkListSize)))
Irp->IoStatus.Information = linkListSize;
else
Irp->IoStatus.Information = 0;
}
IOCTL 2:
Code: Select allif (Irp->AssociatedIrp.SystemBuffer)
{
INT32 bufferSize = (INT32)loc->Parameters.DeviceIoControl.OutputBufferLength;
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, dataToSend, bufferSize);
Irp->IoStatus.Information = bufferSize;
ExFreePoolWithTag(dataToSend, 'tag3');
}
Enumerate files:
Code: Select allNTSTATUS EnumFiles(WCHAR* directory, INT32* linkListSize)
{
NTSTATUS status;
BOOLEAN bIsStarted = TRUE;
PFILE_BOTH_DIR_INFORMATION pBuffer;
ULONG dwSize;
HANDLE hObjectHandle;
PFILE_OBJECT file;
OBJECT_ATTRIBUTES ObjAttributes;
UNICODE_STRING uAllocatedBufferName;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_FOLDER_INFO curr;
PFILE_FOLDER_INFO head = NULL;
PFILE_FOLDER_INFO free;
INT32 linkListSizeI = 0;
//*linkListSize = 0;
dwSize = (sizeof(FILE_BOTH_DIR_INFORMATION) + MAX_PATH * sizeof(WCHAR));
RtlInitUnicodeString(&uAllocatedBufferName, directory);
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
return STATUS_INVALID_DEVICE_STATE;
InitializeObjectAttributes(&ObjAttributes,
&uAllocatedBufferName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = IoCreateFile(&hObjectHandle,
GENERIC_READ | SYNCHRONIZE,
&ObjAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0,
0,
NULL,
IO_NO_PARAMETER_CHECKING);
if (!NT_SUCCESS(status))
return status;
status = ObReferenceObjectByHandle(hObjectHandle, FILE_ALL_ACCESS, 0, KernelMode, &file, NULL);
if (!NT_SUCCESS(status))
{
ZwClose(hObjectHandle);
return status;
}
pBuffer = ExAllocatePoolWithTag(PagedPool, dwSize, 'tag1');
if (pBuffer)
{
while (TRUE)
{
status = QueryDirectoryFile(file,
&IoStatusBlock,
pBuffer,
dwSize,
FileBothDirectoryInformation,
NULL,
bIsStarted);
switch (status)
{
case STATUS_SUCCESS:
{
if (bIsStarted)
bIsStarted = FALSE;
curr = ExAllocatePoolWithTag(PagedPool, sizeof(FILE_FOLDER_INFO), 'tag2');
RtlZeroMemory(curr, sizeof(FILE_FOLDER_INFO));
curr->path[0] = L'*';
if ((BOOLEAN)((pBuffer->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) //if (IsFileDirectory(pBuffer->FileAttributes))
curr->path[1] = L'd'; //directory
else
curr->path[1] = L'f'; //file
RtlStringCchCopyW(curr->path + 2, MAX_PATH, pBuffer->FileName);
linkListSizeI += pBuffer->FileNameLength + 4; //1 for d or f, and 1 for *, then double since wchar
curr->next = head;
head = curr;
break;
}
case STATUS_NO_MORE_FILES:
{
//if linkListSize is increased by 2 under STATUS_NO_MORE_FILES, the BSOD does not occur while trying to free memory
dataToSend = ExAllocatePoolWithTag(PagedPool, linkListSizeI + 2, 'tag3');
RtlZeroMemory(dataToSend, linkListSizeI + 2);
//dataToSend[0] = L'\0';
curr = head;
while (curr)
{
RtlStringCchCatW(dataToSend, linkListSizeI + 2, curr->path);
free = curr;
curr = curr->next;
ExFreePoolWithTag(free, 'tag2');
}
*linkListSize = linkListSizeI;
ExFreePoolWithTag(pBuffer, 'tag1');
ZwClose(hObjectHandle);
ObDereferenceObject(file);
return STATUS_SUCCESS;
}
default:
{
ExFreePoolWithTag(pBuffer, 'tag1');
ZwClose(hObjectHandle);
ObDereferenceObject(file);
return status;
}
}
}
}
ExFreePoolWithTag(pBuffer, 'tag1');
ZwClose(hObjectHandle);
ObDereferenceObject(file);
return status;
}
The BSOD occurs on IOCTL 2 when tag3 memory is freed. Under my EnumFiles function, if I increase size of linkedListSize by 2, it no longer BSODs when freeing the memory.
Edit: I forgot to mention, if I do not add an additional 2 bytes to linkedListSize, it only BSODs sometimes, but not always. I thought I had traced the problem to empty directories, but that didn't seem to be it either.