Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV
Before Posting...
Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


Issues in the disk encryption filter

ntdeveloper31ntdeveloper31 Member Posts: 43

Hi,

I'm writing a basic kmdf-based upper disk filter that will encrypt vhds. (This is my first attempt at writing non-fs minifilter driver, so please bear with me).

I've reached to a point where I can attach to a test vhd and can see all the READs, WRITEs and IOCTLs happening on it. So far so good. Now I want to introduce encryption (during WRITE) and decryption piece (during POST READ) to this. For this I need to obviously first access data buffers for these IO requests which I do using WdfRequestRetrieveInputMemory / WdfRequestRetrieveOutputMemory functions. Couple of issues I've come across are:

  1. Keeping the encryption part aside, I seem to be getting STATUS_ACCESS_VOILATION while copying data back from my local buffer to InputMemory. So in the write event callback,

    • WdfRequestRetrieveInputMemory(Request, &InputMemory);
      WdfMemoryGetBuffer(InputMemory, &BufferSize);
      LocalBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, BufferSize, POOL_TAG);
      WdfMemoryCopyToBuffer(InputMemory, 0, LocalBuffer, BufferSize);
      //
      // This is where I would add encryption part, could be as simple as LocalBuffer[i]++ to begin with
      //
      WdfMemoryCopyFromBuffer(InputMemory, 0, (PVOID)LocalBuffer, BufferSize); --> I get STATUS_ACCESS_VOILATION at this call. Any idea why ?
      WdfRequestSend(Request, Target, &options);
  2. Also, I understand that even for full-disk encryption, certain parts of the disk should not be subject to encryption/decryption process. E.g.
    (https://community.osr.com/discussion/64507/problems-with-a-disk-encryption-filter-driver) IoReadPartitionTable is not subject to filtering. It ignores the disk upper filters.
    So what are the sectors (I know about sector 0) I should watch out for and refrain from encrypting?

For testing, I create and attach a vhd manually using disk management. At this point my filter gets attached and receives all the READ, WRITE notifications.
The goal is to have encryption / decryption working transparently during vhd's lifetime and have operations such as creating and formatting volume with NTFS, and using it for fs operations succeed.

Thanks

Comments

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,596

    The input buffer for a write request is only guaranteed to be readable. Remember that you are using a kernel-mapped pointer to the user's memory pages. You don't want to screw with the user's memory. You're going to have allocate your own buffer to contain your encrypted data.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • ntdeveloper31ntdeveloper31 Member Posts: 43
    Hi Tim,

    Thanks for your reply.

    I do allocate my own buffer and perform encryption on that but how do I tell wdf to consider this newly encrypted buffer instead of the original input buffer and pass that on to next lower driver ? I thought WdfMemoryCopyFromBuffer() will do that for me. Or perhaps I should just assign this new buffer to InputMemory instead of copying contents over.

    Thanks
    Nik
  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,596

    I suppose you could fetch the IRP and manipulate the fields directly, but as far as I know, the approved method is to create a new WDFREQUEST and supply the buffer there. I don't think you can change the buffer in an existing request.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • ntdeveloper31ntdeveloper31 Member Posts: 43

    Hi Tim,

    Sorry I couldn't post here earlier as I was pulled away into some other issues and thanks for pointing in the right direction.

    While your suggestion makes sense, I also found another relevant thread (https://community.osr.com/discussion/216562/wdfrequest-buffer) where Doron mentioned,
    "Then all you need to do is allocate a WDFMEMORY with the new expanded size, for a write operation copy the caller's buffer content into the new buffer, format the request with your WDFMEMORY and adjusted size, and send the request down the stack."

    which is an easier route. But I for the life of me can't make out what's going wrong below:

    VOID
    FilterEvtIoWrite(
    IN WDFQUEUE Queue,
    IN WDFREQUEST OrgRequest,
    IN size_t Length
    )
    {
    NTSTATUS status = STATUS_SUCCESS;
    WDFDEVICE Device;
    WDFMEMORY EncryptedMemory = NULL;
    PVOID EncryptedDataBuffer = NULL;

    WDF_REQUEST_COMPLETION_PARAMS EncryptedParams;
    WDF_REQUEST_COMPLETION_PARAMS_INIT(&EncryptedParams);
    
    try
    {
        Device = WdfIoQueueGetDevice(Queue);
    
        WDF_REQUEST_PARAMETERS non_irp_params;
    
        WDF_REQUEST_PARAMETERS_INIT(&non_irp_params);
    
        WdfRequestGetParameters(OrgRequest, &non_irp_params);
    
        DbgPrint("KmdfDiskFilter: IO -> ORIGINAL WRITE; DeviceOffset -> %lld; Length -> %llu; \n", non_irp_params.Parameters.Write.DeviceOffset, Length);
    
        if (Length != 0)
        {
            WDFMEMORY InputMemory;
    
            status = WdfRequestRetrieveInputMemory(OrgRequest, &InputMemory);
            if (!NT_SUCCESS(status))
            {
                DbgPrint("KmdfDiskFilter: WdfRequestRetrieveInputMemory failed, Error Code: [0x%x]\n", status);
    
                leave;
            }
    
            size_t BufferSize;
    
            PVOID InputBuffer = WdfMemoryGetBuffer(InputMemory, &BufferSize);
    
            WDF_OBJECT_ATTRIBUTES EncryptedMemoryAttributes;
            WDF_OBJECT_ATTRIBUTES_INIT(&EncryptedMemoryAttributes);
    
            // Allocate WDF memory object.
            status = WdfMemoryCreate(&EncryptedMemoryAttributes, NonPagedPoolNx, POOL_TAG, BufferSize, &EncryptedMemory, &EncryptedDataBuffer);
            if (!NT_SUCCESS(status))
            {
                DbgPrint("KmdfDiskFilter: WdfMemoryCreate failed, Error Code: [0x%x]\n", status);
    
                leave;
            }
    
            // Encryption function.
            //for (size_t i = 0; i < EncryptedDataBuffer; i++)
            //{
            //  ((PUCHAR)EncryptedDataBuffer)[i]++;
            //}
    
            // Copying original data
            status = WdfMemoryCopyFromBuffer(EncryptedMemory, 0, InputBuffer, BufferSize);
            if (!NT_SUCCESS(status))
            {
                DbgPrint("KmdfDiskFilter: WdfMemoryCopyFromBuffer failed, Error Code: [0x%x]\n", status);
    
                leave;
            }
    
            status = WdfIoTargetFormatRequestForWrite(WdfDeviceGetIoTarget(Device), OrgRequest, EncryptedMemory, NULL, NULL);
            if (!NT_SUCCESS(status))
            {
                DbgPrint("KmdfDiskFilter: WdfIoTargetFormatRequestForWrite failed, Error Code: [0x%x]\n", status);
    
                leave;
            }
    
            WdfRequestSetCompletionRoutine(OrgRequest, FilterEncryptedWriteComplete, (WDFCONTEXT)EncryptedMemory);
        }
    }
    finally
    { }
    
    if (!WdfRequestSend(OrgRequest, WdfDeviceGetIoTarget(Device), WDF_NO_SEND_OPTIONS))
    {
        status = WdfRequestGetStatus(OrgRequest);
    
        DbgPrint("KmdfDiskFilter: WdfRequestSend failed, Error Code: [0x%x]\n", status);
    }
    
    return;
    

    }

    VOID
    FilterEncryptedWriteComplete(
    IN WDFREQUEST Request,
    IN WDFIOTARGET Target,
    IN PWDF_REQUEST_COMPLETION_PARAMS Params,
    IN WDFCONTEXT Context
    )
    {
    UNREFERENCED_PARAMETER(Target);

    WDFMEMORY EncryptedMemory = (WDFMEMORY)Context;
    
    if (EncryptedMemory)
    {
        WdfObjectDelete(EncryptedMemory);
    }
    
    DbgPrint("KmdfDiskFilter: IO -> ENCRYPTED WRITE; BytesWritten -> %llu; Status -> [0x%x]\n", Params->IoStatus.Information, Params->IoStatus.Status);
    
    WdfRequestComplete(Request, Params->IoStatus.Status);
    

    }

    I'm trying to format a vhd (using NTFS) with my upper filter attached but it always comes back saying it was unable to format disk.
    What makes it worse is its not even encrypted data - its just another copy of the same buffer that I received in the first place.

    There must be something obvious that I'm missing or doing something stupid.

    Thanks

  • ntdeveloper31ntdeveloper31 Member Posts: 43

    Or it could be the case that the steps are fine and its just that the test-case of formatting a vhd (using NTFS) requires some special / additional handling?

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Internals & Software Drivers 30 Nov 2020 LIVE ONLINE
Writing WDF Drivers 7 Dec 2020 LIVE ONLINE
Developing Minifilters Early 2021 LIVE ONLINE