Changing Data in Fat Table at the Formatting Stage

In my upper disk device filter, I process read and write requests. At the stage of formatting, the data in the fat table changes in steps of 0x20,000 bytes. For example, the first bytesoffset 0x104000 next 0x124000, 0x144000, and so on. But if I try to change the data in my write request, then after changing the first 0x20,000 bytes, the next bytesoffset will be not 0x124,000, but 0x184,000, why are 0x60,000 bytes skipped?

Alexey_Yerakhavets wrote:
> In my upper disk device filter, I process read and write requests. At the stage of formatting, the data in the fat table changes in steps of 0x20,000 bytes. For example, the first bytesoffset 0x104000 next 0x124000, 0x144000, and so on. But if I try to change the data in my write request, then after changing the first 0x20,000 bytes, the next bytesoffset will be not 0x124,000, but 0x184,000, why are 0x60,000 bytes skipped?

Formatting does not necessarily write every cluster on the device. It has to initialize the FAT and the root directory, but the rest of the disk need not change.

Have you tried formatting without using the ‘quick format’ option’?

As Tim stated, the file system(s) need only write the data it needs, not
the entire disk. If you skip quick formatting, it performs many more
writes. Although, I am not sure it writes to every block. I don’t remember.

On Mon, Oct 22, 2018 at 12:13 PM Tim_Roberts
wrote:

> OSR http://osr.vanillacommunities.com/
> Tim_Roberts commented on Changing Data in Fat Table at the Formatting Stage
>
> (empty message)
>
> –
> Reply to this email directly or follow the link below to check it out:
> http://osr.vanillacommunities.com/discussion/comment/291078#Comment_291078
>
> Check it out:
> http://osr.vanillacommunities.com/discussion/comment/291078#Comment_291078
>

I did not say that with fast formatting, it overwrites the entire disk. With fast formatting, it overwrites the fat table in steps of 0x20,000 bytes. For example, the size of the FAT table is 0x200,000 bytes. The first sector of the FAT table is at a bytesoffset of 0x140C00. With fast formatting, a write request is constantly invoked with a bytesoffset of 0x140C00 and 0x20,000 bytes in length, followed by a request to read the same 0x20,000 bytes with an bytesoffset of 0x140C00. until the entire table is formatted. I checked it many times. But if I try to change the data in a buffer of 0x20,000 bytes in size, the next bytesoffset will no longer be 164,000, but for some reason 1C4,000. That is, the formatting does not begin to process all sectors of the FAT table. Maybe my attempts to change the buffer affect the speed or maybe some other reason?
Below is an example of processing a write request.
VOID
WdfFltrWrite(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t Length
) {

WDFFLTR_REQUEST        requestEntry;
PWDFFLTR_WRITE_REQUEST writeRequest;
PIRP                   wdmWriteIrp;
NTSTATUS               status;
PCHAR                  dataBuffer = NULL;
PDEVICE_CONTEXT devCont;
ULONG SectorsCount;
LARGE_INTEGER StartSector,
//
// Retrieve some parameters of the request to print 
//  to the debugger
//
if (Length != 0) {

	//
	// This is a non-zero length transfer, retrieve
	//  the data buffer.
	//
	status = WdfRequestRetrieveInputBuffer(Request,
		Length,
		(PVOID *)&dataBuffer,
		&length);

	if (!NT_SUCCESS(status)) {

		//
		// Not good. We'll still pass the request down
		//  and let the next device decide what to do with 
		//  this.
		//
		WdfFltrTrace(("RetrieveInputBuffer failed - 0x%x\n",
			status));
	}

}
devCont = WdfFltrGetDeviceContext(WdfIoQueueGetDevice(Queue));
//
// Get the WDM IRP
//
wdmWriteIrp = WdfRequestWdmGetIrp(Request);

for (int i = 0; i < 64; i++)
{
	DbgPrint(" %x", *((PUCHAR)dataBuffer + i));
	if (i % 32 == 0)
	DbgPrint("\n");
}
DbgPrint("CryptKey loaded: ByteOffset = %x; Length = %x;\n", irpStack->Parameters.Write.ByteOffset, irpStack->Parameters.Write.Length);

if (devCont->Filter.KeyLoaded && irpStack->Parameters.Write.ByteOffset.QuadPart >= 1313792) {		
	// Write.ByteOffset.QuadPart = 1313792 - first sector FAT table
		SectorsCount = irpStack->Parameters.Write.Length >> 9;
		StartSector = irpStack->Parameters.Write.ByteOffset;
	while (SectorsCount--)
	{		
		for (int i = 0; i < 512; i++)
		{
			*((PUCHAR)dataBuffer+i) ^= 0xAA;
		}
	
		dataBuffer += SECTOR_SIZE;
		StartSector.QuadPart += SECTOR_SIZE;
	}
}
            for (int i = 0; i < 64; i++)
	{
		DbgPrint(" %x", *((PUCHAR)dataBuffer + i));
		if (i % 32 == 0)
		DbgPrint("\n");
	}			
	
WdfFltrForwardRequest(WdfIoQueueGetDevice(Queue), Request);
return;

}

Can someone explain how you can change the data received during the processing of a write request and send them further down the driver stack in disk filter driver ?

Copy the buffer, change the data, create a new Request describing the now changed data, send it down the stack, and when it completes, complete the original request.

Does that help?

Peter

Hi, Peter. It did not help. The error at the time of formatting, described above, still remained. Why does the shift not occur in order in my case?

VOID
WdfFltrWrite(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t Length
) {

WDFFLTR_REQUEST        requestEntry;
PWDFFLTR_WRITE_REQUEST writeRequest;
PIRP                   wdmWriteIrp;
NTSTATUS               status;
WDFMEMORY reqMemory;
PVOID pOutputBuffer;
PDEVICE_CONTEXT devCont;
WDFREQUEST newRequest;

WDF_OBJECT_ATTRIBUTES  attributes;
WDFMEMORY  writeBufferMemHandle;

devCont = WdfFltrGetDeviceContext(WdfIoQueueGetDevice(Queue));
BOOLEAN boold = ext->Filter.CryptKeyLoaded;
BOOLEAN ballll;
//
// Get the WDM IRP
//
wdmWriteIrp = WdfRequestWdmGetIrp(Request);



PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(wdmWriteIrp);

DbgPrint("CryptKey loaded: ByteOffset = %x; Length = %x;\n", irpStack->Parameters.Write.ByteOffset, irpStack->Parameters.Write.Length);
if (devCont->Filter.CryptKeyLoaded && irpStack->Parameters.Write.ByteOffset.QuadPart >= 1313792 && irpStack->Parameters.Write.ByteOffset.QuadPart < 5242880) {
	
	
	

	pOutputBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, Length,
		'tag1');
	status = WdfRequestRetrieveInputMemory(Request, &reqMemory);
	if (!NT_SUCCESS(status))
	{
		WdfRequestCompleteWithInformation(
			Request,
			status,
			0
		);
	}

	if (pOutputBuffer != NULL)
	{
		status = WdfMemoryCopyToBuffer(reqMemory, 0, pOutputBuffer, Length);
	}
	else {
		status = STATUS_INSUFFICIENT_RESOURCES;
	}
	if (!NT_SUCCESS(status))
	{
		ExFreePool(pOutputBuffer);
		pOutputBuffer = NULL;
		WdfRequestCompleteWithInformation(
			Request,
			status,
			0
		);
	}
	for (int i = 0; i < 64; i++)
	{
		DbgPrint(" %x", *((PUCHAR)pOutputBuffer + i));
		if (i % 32 == 0 && i != 0)
			DbgPrint("\n");
	}
	CryptSectors(
		&devCont->Filter.CryptKey,
		pOutputBuffer,
		NULL,
		irpStack->Parameters.Write.ByteOffset,
		irpStack->Parameters.Write.Length >> 9,
		TRUE);

	for (int i = 0; i < 64; i++)
	{
		DbgPrint(" %x", *((PUCHAR)pOutputBuffer + i));
		if (i % 32 == 0 && i != 0)
			DbgPrint("\n");
	}

	WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
	attributes.ParentObject = devCont->TargetToSendRequestsTo;
	status = WdfRequestCreate(&attributes, devCont->TargetToSendRequestsTo, &newRequest);
	if (!NT_SUCCESS(status)) {
		return;
	}

	status = WdfMemoryCreate(&attributes,
		NonPagedPool,
		0,
		Length,
		&writeBufferMemHandle,
		&pOutputBuffer);
	if (!NT_SUCCESS(status)) {
		return;
	}

	status = WdfIoTargetFormatRequestForWrite(devCont->TargetToSendRequestsTo, newRequest, writeBufferMemHandle,
		NULL, &irpStack->Parameters.Write.ByteOffset.QuadPart);
	if (!NT_SUCCESS(status))
	{
		return;
	}

	WdfFltrForwardRequestWithCompletion(WdfIoQueueGetDevice(Queue),
		newRequest,
		WdfFltrWriteComplete,
		Request);

	DbgPrint("CryptKey loaded: ByteOffset = %x;\n", irpStack->Parameters.Write.ByteOffset);
			return;
}
	WdfFltrForwardRequest(WdfIoQueueGetDevice(Queue), Request);

return;

}

VOID
WdfFltrWriteComplete(
IN WDFREQUEST Request,
IN WDFIOTARGET Target,
IN PWDF_REQUEST_COMPLETION_PARAMS Params,
IN WDFCONTEXT Context
) {

WDFFLTR_REQUEST       requestEntry;
PWDFFLTR_WRITE_REQUEST writeRequest;

//
// Restart completion processing.
//
WdfRequestComplete((WDFREQUEST)Context, Params->IoStatus.Status);

}

BOOLEAN
CryptSectors(
PGOST_CRYPT_KEY Key,
PVOID BufferIn,
PVOID BufferOut,
LARGE_INTEGER StartSector,
ULONG SectorsCount,
BOOLEAN Encrypt)
{
while (SectorsCount–)
{
for (int i = 0; i < 512; i++)
{
*((PUCHAR)BufferIn+i) ^= 0xAA;
}
BufferIn = (PCHAR)BufferIn + SECTOR_SIZE;
BufferOut = (PCHAR)BufferOut + SECTOR_SIZE;
StartSector.QuadPart += SECTOR_SIZE;
}
return TRUE;
}

Why does the shift not occur in order in my case?

Sorry… I don’t understand the question. What “shift” are we talking about?

I guess you’re trying to do some sort of full volume encryption? I’ve written this code myself, and I don’t recall having to do anything unusual.

What is it that doesn’t work?

(and, no… I didn’t read your code – it’s one of my rules: If it’s more than 5 lines, and you’re not paying me, I’m not reading your code to debug it. No offense intended, that’s just my personal rule to keep from spending all my time here).

Peter

Alexey_Yerakhavets wrote:

Hi, Peter. It did not help. The error at the time of formatting, described above, still remained. Why does the shift not occur in order in my case?

There are several rather large problems with your code.

If you get any errors in the first part of your function, you complete
the request and free the buffer, but you keep on going in the function. 
The first time you dereference pOutputBuffer, you’ll get a blue screen.

You go to all the trouble to allocate pOutputBuffer and copy the data
there, but then you call WdfMemoryCreate, which will create brand new
empty memory, and overwrite the pointer.  Thus, that original memory
will leak, and the buffer you pass down will be all zero.  Of course,
the memory would have leaked anyway; you don’t free it anywhere.

In CryptSectors, you have “StartSector.QuadPart += SECTOR_SIZE;”.
SECTOR_SIZE is in bytes.  You just want to bump that value by 1.  Of
course, since you don’t use either BufferOut or StartSector in that
routine, it doesn’t really matter

You got me right, Peter. During fast formatting, I’m trying to encrypt a FAT table. But for some reason, not the entire FAT table is encrypted. When formatting without encryption, I have the following.
ByteOffset = 0x140c00; Length = 0x20000;
ByteOffset = 0x160c00; Length = 0x20000;
ByteOffset = 0x180c00; Length = 0x20000;
ByteOffset = 0x1a0c00; Length = 0x20000;
ByteOffset = 0x1c0c00; Length = 0x20000;

ByteOffset = 0x4e0600; Length = 0x1fa00;
All FAT table is formatted at 20,000 bytes.
Where 0x140с00 is the beginning of the first sector of FAT table, and 0x500000 is the end of the FAT table.
But if I try copy the buffer and change the data, create a new Request describing the now changed data, send it down the stack, I have the following:
ByteOffset = 0x140c00; Length = 0x20000;
ByteOffset = 0x1c0c00; Length = 0x20000;
ByteOffset = 0x240c00; Length = 0x20000;
ByteOffset = 0x2c0c00; Length = 0x20000;

( I do not receive data on the write with the following byteoffset:
ByteOffset = 0x160c00; Length = 0x20000;
ByteOffset = 0x180c00; Length = 0x20000;
ByteOffset = 0x1a0c00; Length = 0x20000;

ByteOffset = 0x1e0c00; Length = 0x20000;
ByteOffset = 0x200c00; Length = 0x20000;
ByteOffset = 0x220c00; Length = 0x20000;
)

Is it possible that I do not need to encrypt the FAT table during formatting, but can I encrypt the FAT table in some other way?

Mr Yerakhavets… You saw Mr. Roberts’ post, right? The one just after mine? He pointed to several issues in your code.

Peter

I corrected the code on the recommendations of Tim. True, I’m not quite sure about the free memory (“Of course,
the memory would have leaked anyway; you don’t free it anywhere.”). On the site msdn in memory.h I did not find the function of freeing memory or I misunderstood something in Tim’s answer :slight_smile:

Alexey_Yerakhavets wrote:

I corrected the code on the recommendations of Tim. True, I’m not quite sure about the free memory ("Of course,

the memory would have leaked anyway; you don’t free it anywhere."). On the site msdn in memory.h I did not find the function of freeing memory or I misunderstood something in Tim’s answer

You can’t be serious! You have this:
    pOutputBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, Length, ‘tag1’);
That’s an allocation.  Someone, somewhere, has to explicitly free that
memory.  You do not do so, except in one error case after CopyToBuffer:
    ExFreePool(pOutputBuffer);

In the later error exits where you return, or in the success case where
you submit the request, you never free that memory.

You just aren’t very careful with your error checks.  If
WdfRequestRetrieveInputMemory fails, you complete the request, but you
don’t return, so you keep on executing code.  That happens several more
times.  Once you have completed the request, there is no point in
continuing on, but you must clean up after yourself.

And look at what happens if the ExAllocatePoolWithTag happens to fail. 
You’ll call WdfRequestRetrieveInputMemory (but why?), then you’ll take
the else clause and call ExFreePool.  ExFreePool does not handle null
pointers, so it will crash.

Disk filters require extremely careful coding.  The cost of an error is
just way too high.  If you’re not prepared to do that, then you
shouldn’t be writing disk filters.

Thank you, Tim, for the advice. I am new to driver development. And it so happened that I needed to learn exactly this type of driver. I understand that my question is possibly stupid, but for me this method is really not clear. Is it possible to get advice on the method of changing the data described by Peter?

Copy the buffer, change the data, create a new Request describing the now changed data, send it down the stack, and when it completes, >complete the original request.

As I understood at the beginning I need to create a structure memory object and allocate a memory buffer of the specified size.
status = WdfMemoryCreate(&attributes,
NonPagedPool,
0,
Length,
&writeBufferMemHandle,
&pOutputBuffer);
-Where Length is the size of the buffer in the original request.

Next, I need to get a copy of the buffer from the write request and change it.
WdfRequestRetrieveInputMemory (Request, &reqMemory);
WdfMemoryCopyToBuffer(reqMemory, 0, pOutputBuffer, Length);

Then I create a new request and build a write request for the I / O purpose and send this request.
WdfRequestCreate(&attributes, devCont->TargetToSendRequestsTo, &newRequest);
WdfIoTargetFormatRequestForWrite(devCont->TargetToSendRequestsTo, newRequest, writeBufferMemHandle,
NULL, &irpStack->Parameters.Write.ByteOffset.QuadPart);
WdfRequestSetCompletionRoutine(newRequest, WdfFltrWriteComplete, Request);
-Where Request is original request;
WdfRequestSend(newRequest, devCont->TargetToSendRequestsTo, NULL));

After completing my request, I complete the original request.
WdfRequestComplete((WDFREQUEST)Context, Params->IoStatus.Status);

Do I understand this process correctly?

On Oct 28, 2018, at 5:10 AM, Alexey_Yerakhavets wrote:
>
> Thank you, Tim, for the advice. I am new to driver development. And it so happened that I needed to learn exactly this type of driver. I understand that my question is possibly stupid, but for me this method is really not clear. Is it possible to get advice on the method of changing the data described by Peter?

It’s not that the question is stupid. The issue is that you have made mistakes that even a regular C programmer should not be making. I am very sympathetic to new driver developers, as most people here will tell you, but you shouldn’t come to driver development unless you are already a pretty good programmer. The driver world adds new complications to the already complicated world of programming.

>> Copy the buffer, change the data, create a new Request describing the now changed data, send it down the stack, and when it completes, >complete the original request.
>
> As I understood at the beginning I need to create a structure memory object and allocate a memory buffer of the specified size.
>
> status = WdfMemoryCreate(&attributes,
> NonPagedPool,
> 0,
> Length,
> &writeBufferMemHandle,
> &pOutputBuffer);
>
> -Where Length is the size of the buffer in the original request.
>
> Next, I need to get a copy of the buffer from the write request and change it.

Yes, but this is NOT what you did in the last code you posted. You allocated a buffer, copied into the buffer, and then called WdfMemoryCreate, which promptly created a brand new buffer and overwrote the address of the one you allocated originally. Now, it IS possible to create a WDFMEMORY object that wraps a pre-existing buffer, using WdfMemoryCreatePreAllocated, but it’s also possible to arrange the code in the order you describe here.

> WdfRequestRetrieveInputMemory (Request, &reqMemory);
> WdfMemoryCopyToBuffer(reqMemory, 0, pOutputBuffer, Length);
>
> Then I create a new request and build a write request for the I / O purpose and send this request.
>
> WdfRequestCreate(&attributes, devCont->TargetToSendRequestsTo, &newRequest);
> WdfIoTargetFormatRequestForWrite(devCont->TargetToSendRequestsTo, newRequest, writeBufferMemHandle,
> NULL, &irpStack->Parameters.Write.ByteOffset.QuadPart);
> WdfRequestSetCompletionRoutine(newRequest, WdfFltrWriteComplete, Request);
> -Where Request is original request;
>
> WdfRequestSend(newRequest, devCont->TargetToSendRequestsTo, NULL));
>
> After completing my request, I complete the original request.
>
> WdfRequestComplete((WDFREQUEST)Context, Params->IoStatus.Status);

But you STILL have not arranged to delete that WDFMEMORY object you created, nor have you deleted the WDFREQUEST you created. Someone has to do that. As it sits, both are owned by your driver and will stay in memory until your driver exits.

The easy way to handle that is to have the WDFMEMORY be owned by your new WDFREQUEST. You can do that by setting the parent in the attributes when you call WdfMemoryCreate, and then by calling WdfObjectDelete in your completion routine.

Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Thank you, Tim, for your advice and patience. I made corrections that you indicated to me. But I encountered an error in the create memory function. 0xC0000004 STATUS_INFO_LENGTH_MISMATCH

status = WdfMemoryCreate(&attributes,
NonPagedPool,
0,
Length,
&writeBufferMemHandle,
&pOutputBuffer);

-Where Length is the size of the buffer in the original request. (size_t Length)
At the time of error Lenght = 0x20000

Exactly the same error if I use the ExAllocatePoolWithTag(NonPagedPool, Length,‘tag1’); and then WdfMemoryCreatePreallocated(&attributes, pOutputBuffer, Length, &writeBufferMemHandle);
What could be the reason for this error?

Alexey_Yerakhavets wrote:

Thank you, Tim, for your advice and patience. I made corrections that you indicated to me. But I encountered an error in the create memory function. 0xC0000004 STATUS_INFO_LENGTH_MISMATCH

status = WdfMemoryCreate(&attributes,
NonPagedPool,
0,
Length,
&writeBufferMemHandle,
&pOutputBuffer);

-Where Length is the size of the buffer in the original request. (size_t Length)

At the time of error Lenght = 0x20000

I don’t believe you got that error from that function.  It can’t
generate that error.  The request APIs can generate that error if the
size of the memory buffer doesn’t match the size in the IRP. You will
need to check again.

I managed to implement the algorithm proposed by Peter.

Copy the buffer, change the data, create a new Request describing the now changed data, send it down the stack, and when it completes, complete the original request.

But this did not solve the error due to which I created this discussion.
During fast formatting, I’m trying to encrypt a FAT table. But for some reason, not the entire FAT table is encrypted. When formatting without encryption, I have the following.
ByteOffset = 0x140c00; Length = 0x20000;
ByteOffset = 0x160c00; Length = 0x20000;
ByteOffset = 0x180c00; Length = 0x20000;
ByteOffset = 0x1a0c00; Length = 0x20000;
ByteOffset = 0x1c0c00; Length = 0x20000;

ByteOffset = 0x4e0600; Length = 0x1fa00;
All FAT table is formatted at 20,000 bytes.
Where 0x140с00 is the beginning of the first sector of FAT table, and 0x500000 is the end of the FAT table.
But if I try copy the buffer and change the data, create a new Request describing the now changed data, send it down the stack, I have the following:
ByteOffset = 0x140c00; Length = 0x20000;
ByteOffset = 0x1c0c00; Length = 0x20000;
ByteOffset = 0x240c00; Length = 0x20000;
ByteOffset = 0x2c0c00; Length = 0x20000;

( I do not receive data on the write with the following byteoffset:
ByteOffset = 0x160c00; Length = 0x20000;
ByteOffset = 0x180c00; Length = 0x20000;
ByteOffset = 0x1a0c00; Length = 0x20000;

ByteOffset = 0x1e0c00; Length = 0x20000;
ByteOffset = 0x200c00; Length = 0x20000;
ByteOffset = 0x220c00; Length = 0x20000;
)
I understand that without direct debugging it is difficult to find out the reason, but can there be any ideas?

Are you seeing read requests during the format?

Hi, Scott. If I format with encryption, then the read request really disappears. Usually after writing 0x20,000 bytes, then the same 0x20,000 bytes reading was processed. What makes the read request disappear?