Hey, I have worked for a month on a simple XOR encryption driver and also faced the most frequently discussed problem: The notepad-does-not-show-decrypted-data problem.
I spent days and nights figuring out how to get around that, and I would like to share my knowledge. I will explain how to correctly handle this problem and use the SwapBuffers example as the basis.
Here It goes:
Pre-Work: We disable Fast IO for now, by adding this to the Top of the PreRead Function:
if(FLT_IS_FASTIO_OPERATION(Data)){
return FLT_PREOP_DISALLOW_FASTIO;
}
a) Next Step: SwapBuffers comes with two functions, PreRead and PostRead. First step is to let only the files pass through this routines that we really want to encrypt.
We do a simple check right after the Fast IO Disallow function:
To only allow encrypting files that contain “.blah” do this right after the Fast IO if clause:
Make sure we dont treat .LNK files here.
IMPORTANT: Use &FltObjects->FileObject->FileName, and not the Filter Manager Filename functions, as they fail from time to time…this is the only safe method to go!
if(SfFindInUnicodeString(&FltObjects->FileObject->FileName,L".blah",L".BLAH",5) && !SfFindInUnicodeString(&FltObjects->FileObject->FileName,L"lnk.“,L"LNK.”,4))
{
}
else
{
// NO Encrypted File!
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
This will cause to exit the Function right away if we dont have an encrypted file here.
b) Right below that we have to make sure that we will only decrypt PAGING/IO, otherwise Notepad would see the decrypted file, while Wordpad would reencrypt it again and show Encrypted content again. When we only decrypt Paging IO we will have decrypted content in the memory, and when reading from it we wont run the decryption routine on it again:
Just add this code below the code from above:
// Only decrypt Paging IO
if(iopb->IrpFlags & IRP_PAGING_IO){
}
else
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
No Paging IO just exit here!
c) Thats basically all, now just add your decryption mechanism to PostRead and PostReadSafe.
Right above this code from said Post Routines
RtlCopyMemory( origBuf,
p2pCtx->SwappedBuffer,
Data->IoStatus.Information );
add your routine. You can use this simple XOR method for testing:
char* pp=p2pCtx->SwappedBuffer;
unsigned int i=0;
// We have a encrypted, we may decrypt the newBuf now!
DbgPrint(“SwappedBuffers found a DRMS file but not-safe!\n”);
for(i=0;iIoStatus.Information;i++){
pp[i]=pp[i]^‘Z’;
}
d) Thats it, note that Windows may prefetch some files during start, so make sure you start this filter on Boot time.
To do that just adjust the .inf in this way:
[MiniFilter.Service]
DisplayName = %ServiceName%
Description = %ServiceDescription%
ServiceBinary = %12%%DriverName%.sys ;%windir%\system32\drivers<br>Dependencies = “FltMgr”
ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER
StartType = 0 ;SERVICE_BOOT_START
ErrorControl = 1 ;SERVICE_ERROR_NORMAL
LoadOrderGroup = “FSFilter Encryption”
AddReg = MiniFilter.AddRegistry
And here is the Unicode String Search function that might help you:
BOOLEAN SfFindInUnicodeString(PUNICODE_STRING object, PWCHAR findit,PWCHAR finditupper, int len)
{
wchar_t* searchBuffer;
BOOLEAN result;
if(object->Buffer==NULL)
return FALSE;
if(object->Length return FALSE; // this is obvious
searchBuffer=ExAllocatePoolWithTag(PagedPool, object->Length + sizeof(WCHAR) , ‘myta’);
// If Allocation fails, return NULL
if (searchBuffer == NULL) return FALSE;
RtlCopyMemory(searchBuffer, object->Buffer, object->Length);
searchBuffer[object->Length / sizeof(WCHAR)] = UNICODE_NULL;
result = (wcsstr(searchBuffer,findit)!=NULL) | (wcsstr(searchBuffer,finditupper)!=NULL);
// FREE BUFFER
ExFreePoolWithTag(searchBuffer,‘myta’);
// Return
return result;
}