BEGINNERS TUTORIAL: Encryption Driver and the Notepad Problem!

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;

}

Because I never wrote an encryption filter I am not commenting on what you
are saying. But a quick look shows your function SfFindInUnicodeString is
failing in the following scenarios:

-false positive if extension is not the extension but embedded in the
filename (example: you.lnk.txt)
-failing if not either completely uppercase or lowercase (example:
yourfile.Lnk)

Better approach is to use an approved method such as
FsRtlIsNameInExpression.

/Daniel

wrote in message news:xxxxx@ntfsd…
> 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;
>
> }
>

From one relative newbie to another: I don’t mean to take away from the
accomplishment of figuring out how to do this, but don’t you think you
could handle capitalization in the file exension a little more robustly?
You’ve missed 6 possible capitalizations of .lnK and 14 of .bLAh. If
you’re going to take the time and memory to copy the string, you may as
well tolower (or toupper) it and handle this correctly. Also if a
filename contains an embedded .blah or .lnk, you’ll ignore it. Maybe it
isn’t common to do it on windows, but I’d guess most people who use unix
are used to being able to tack .bak or .old onto a filename and have it
do predicatble things. foo.blah.bak, for example will be ignored (maybe
correctly?) but so will c:\x.blah* (probably not what you meant to
happen).

Also, as a small stylistic nitpick, having an empty if block doesn’t
reflect well on your knowledge of boolean logic, and neither does using
bitwise or instead of logical or in your SfFindInUnicodeString.

Incidentally, having recently fought my own newbie’s battle with
identifying files on reads/writes, it might be worth noting this snippet
from the MSDN. I’m not sure if you’re using it outside of the create,
but if you are you might want to read this:

FileName

A pointer to a read-only Unicode string that holds the name of the file
opened on the volume. If the volume is being opened, the Length member
of the UNICODE_STRING structure pointed to by FileName will be zero.
Note that this file name is valid only during the processing
of an IRP_MJ_CREATE request.

http://msdn2.microsoft.com/en-us/library/aa906961.aspx

(emphasis mine)

If you can come up with a better way to keep track of files than
filename, you can check the filename on the IRP_MJ_CREATE, and won’t
need to do any string comparison at DISPATCH_LEVEL in the postop.

~Eric

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@web.de
Sent: Monday, October 22, 2007 11:10 AM
To: Windows File Systems Devs Interest List
Subject: [ntfsd] BEGINNERS TUTORIAL: Encryption Driver and the Notepad
Problem!

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".B
LAH",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;

}


NTFSD is sponsored by OSR

For our schedule debugging and file system seminars (including our new
fs mini-filter seminar) visit:
http://www.osr.com/seminars

You are currently subscribed to ntfsd as: xxxxx@edsiohio.com To
unsubscribe send a blank email to xxxxx@lists.osr.com

Correction to the following:

may as well tolower (or toupper) it and handle this correctly. Also if
a filename contains an embedded .blah or .lnk, you’ll ignore it. Maybe
it isn’t common to do it on windows, but I’d guess most people who use
unix are used to being able to tack .bak or .old onto a filename and
have it do predicatble things. foo.blah.bak, for example will be
ignored (maybe
correctly?) but so will c:\x.blah* (probably not what you meant to
happen).

Correction:

Actually, you’ll get a false positive, Daniel got it right.

By the way, it looks like you got the hard part figured out (somebody
who has done an encryption filter could probably tell you more about
that), it’s just some little things.

~Eric

I appreciate any answers. :slight_smile:

I know many of you are way more experienced than i am. But most who develop a filter driver work for companies and may not disclose any code. I am doing it for fun and I love giving other noobs some kind of starting point when writing encryption drivers…hopefully i can minimize the amount of encryption questions as many of you are getting bothered.

Anyways…thank you very very much for any input. Youre absolutely right about the Find in Unicode Function. As I could not find one in the strsafe i puzzled something together and indeed…it has some flaws. I will try to make it better and post it into this thread…to beginned will get the whole informations balled up!

>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!

NO!

This has been discussed 17,391 times on this list. You MAY NOT access
FileObject->FileName anywhere except pre-create. You WILL blue screen.

You need to make your encryption decision in the create path, where you set
a flag in your stream context. In read/write, just check the flag. Filter
manager’s name functions will work in the create path. The reason they
sometimes fail in the read/write path is that it’s not safe.

  • Dan.

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@web.de
Sent: Monday, October 22, 2007 9:10 AM
To: Windows File Systems Devs Interest List
Subject: [ntfsd] BEGINNERS TUTORIAL: Encryption Driver and the Notepad
Problem!

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;

}


NTFSD is sponsored by OSR

For our schedule debugging and file system seminars (including our new fs
mini-filter seminar) visit:
http://www.osr.com/seminars

You are currently subscribed to ntfsd as: xxxxx@privtek.com To unsubscribe
send a blank email to xxxxx@lists.osr.com

Excellent post… Only it contains more RTFM bugs than it contains good comments.
Read the FAQ.


Kind regards, Dejan (MSN support: xxxxx@alfasp.com)
http://www.alfasp.com
File system audit, security and encryption kits.

You missed the hardest problem to solve when implementing an encryption
product in a filter driver. The encryption method you are using can
handle single byte alignment. This won’t work if you use something a
little stringer such as AES, maybe on 16 byte cipher blocks? Thus all of
the IO must be padded out to a 16 byte boundary. You can’t safely move
the EOF out during a paging write so you need to handle this in the
cached side. As well, you don;t want to show the user this padded file
size, you want to show them the real file size. Hence you need to deal
with size faking in the file information requests as well as in the
directory enumeration calls. This latter then begs the question, how do
you recognize a file to be encrypted when processing a directory
enumeration buffer?

Again, the size faking required, when implemented in a file system
filter driver, is not a closed problem. There are things which must be
accepted in the implementation as best approaches. The only way to
implement a 100%, fully functional encryption driver is where you own
all file objects and use the underlying file system as a storage area.

Pete

Kernel Drivers
Windows File System and Device Driver Consulting
www.KernelDrivers.com
(303)546-0300

xxxxx@web.de wrote:

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;
>
> }
>
> —
> NTFSD is sponsored by OSR
>
> For our schedule debugging and file system seminars
> (including our new fs mini-filter seminar) visit:
> http://www.osr.com/seminars
>
> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com

Just take the remaining bytes (those left after processing all 16 byte
blocks) and add enough bytes from the previous block that has already been
encrypted to bring the total number of bytes to be encrypted up to 16.
Encrypt those and place them as the last 16 bytes in the buffer, overwriting
how ever many bytes you previously encrypted that were needed for this last
block. When decrypting within the last 16 bytes, always do the last 16
first and then the blocks of 16 before that last block next.

This will work for any block size, within reason. I think a block size of
500TB might be a little excessive for this logic to be useful. This is just
crypto 101. If done correctly, CBC (cipher block chaining) can work too,
but many leave it out for this last block if it isn’t a sector multiple.

“Peter Scott” wrote in message
news:xxxxx@ntfsd…
>
> You missed the hardest problem to solve when implementing an encryption
> product in a filter driver. The encryption method you are using can handle
> single byte alignment. This won’t work if you use something a little
> stringer such as AES, maybe on 16 byte cipher blocks? Thus all of the IO
> must be padded out to a 16 byte boundary. You can’t safely move the EOF
> out during a paging write so you need to handle this in the cached side.
> As well, you don;t want to show the user this padded file size, you want
> to show them the real file size. Hence you need to deal with size faking
> in the file information requests as well as in the directory enumeration
> calls. This latter then begs the question, how do you recognize a file to
> be encrypted when processing a directory enumeration buffer?
>
> Again, the size faking required, when implemented in a file system filter
> driver, is not a closed problem. There are things which must be accepted
> in the implementation as best approaches. The only way to implement a
> 100%, fully functional encryption driver is where you own all file objects
> and use the underlying file system as a storage area.
>
> Pete
>
> Kernel Drivers
> Windows File System and Device Driver Consulting
> www.KernelDrivers.com
> (303)546-0300
>
> xxxxx@web.de wrote:
>> 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;
>>
>> } —
>> NTFSD is sponsored by OSR
>>
>> For our schedule debugging and file system seminars
>> (including our new fs mini-filter seminar) visit:
>> http://www.osr.com/seminars
>>
>> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
>> To unsubscribe send a blank email to xxxxx@lists.osr.com
>

I don’t want to get off topic but you are simply shifting the problem
around. Now you need to maintain this association between teh last 2 16
byte blocks and be sure that when one is updated, the other is also updated.

I really don’t see any ease of complexity in the implementation you
described, which I have done both on several occasions.

Pete

Kernel Drivers
Windows File System and Device Driver Consulting
www.KernelDrivers.com
(303)546-0300

David J. Craig wrote:

Just take the remaining bytes (those left after processing all 16 byte
blocks) and add enough bytes from the previous block that has already been
encrypted to bring the total number of bytes to be encrypted up to 16.
Encrypt those and place them as the last 16 bytes in the buffer, overwriting
how ever many bytes you previously encrypted that were needed for this last
block. When decrypting within the last 16 bytes, always do the last 16
first and then the blocks of 16 before that last block next.

This will work for any block size, within reason. I think a block size of
500TB might be a little excessive for this logic to be useful. This is just
crypto 101. If done correctly, CBC (cipher block chaining) can work too,
but many leave it out for this last block if it isn’t a sector multiple.

“Peter Scott” wrote in message
> news:xxxxx@ntfsd…
>> You missed the hardest problem to solve when implementing an encryption
>> product in a filter driver. The encryption method you are using can handle
>> single byte alignment. This won’t work if you use something a little
>> stringer such as AES, maybe on 16 byte cipher blocks? Thus all of the IO
>> must be padded out to a 16 byte boundary. You can’t safely move the EOF
>> out during a paging write so you need to handle this in the cached side.
>> As well, you don;t want to show the user this padded file size, you want
>> to show them the real file size. Hence you need to deal with size faking
>> in the file information requests as well as in the directory enumeration
>> calls. This latter then begs the question, how do you recognize a file to
>> be encrypted when processing a directory enumeration buffer?
>>
>> Again, the size faking required, when implemented in a file system filter
>> driver, is not a closed problem. There are things which must be accepted
>> in the implementation as best approaches. The only way to implement a
>> 100%, fully functional encryption driver is where you own all file objects
>> and use the underlying file system as a storage area.
>>
>> Pete
>>
>> Kernel Drivers
>> Windows File System and Device Driver Consulting
>> www.KernelDrivers.com
>> (303)546-0300
>>
>> xxxxx@web.de wrote:
>>> 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;
>>>
>>> } —
>>> NTFSD is sponsored by OSR
>>>
>>> For our schedule debugging and file system seminars
>>> (including our new fs mini-filter seminar) visit:
>>> http://www.osr.com/seminars
>>>
>>> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
>>> To unsubscribe send a blank email to xxxxx@lists.osr.com
>
>
>
> —
> NTFSD is sponsored by OSR
>
> For our schedule debugging and file system seminars
> (including our new fs mini-filter seminar) visit:
> http://www.osr.com/seminars
>
> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com

My apologies, I actually have not used this method as described by
David. And still, without size faking in place, I do not see how you can
recover the last portion of an encrypted buffer without storing it
somewhere. My point was not where do you get these few bytes to fill in
the remaining block but that you need to fake the size of the file to
the caller.

Please explain if I missed something here.

Pete

Kernel Drivers
Windows File System and Device Driver Consulting
www.KernelDrivers.com
(303)546-0300

David J. Craig wrote:

Just take the remaining bytes (those left after processing all 16 byte
blocks) and add enough bytes from the previous block that has already been
encrypted to bring the total number of bytes to be encrypted up to 16.
Encrypt those and place them as the last 16 bytes in the buffer, overwriting
how ever many bytes you previously encrypted that were needed for this last
block. When decrypting within the last 16 bytes, always do the last 16
first and then the blocks of 16 before that last block next.

This will work for any block size, within reason. I think a block size of
500TB might be a little excessive for this logic to be useful. This is just
crypto 101. If done correctly, CBC (cipher block chaining) can work too,
but many leave it out for this last block if it isn’t a sector multiple.

“Peter Scott” wrote in message
> news:xxxxx@ntfsd…
>> You missed the hardest problem to solve when implementing an encryption
>> product in a filter driver. The encryption method you are using can handle
>> single byte alignment. This won’t work if you use something a little
>> stringer such as AES, maybe on 16 byte cipher blocks? Thus all of the IO
>> must be padded out to a 16 byte boundary. You can’t safely move the EOF
>> out during a paging write so you need to handle this in the cached side.
>> As well, you don;t want to show the user this padded file size, you want
>> to show them the real file size. Hence you need to deal with size faking
>> in the file information requests as well as in the directory enumeration
>> calls. This latter then begs the question, how do you recognize a file to
>> be encrypted when processing a directory enumeration buffer?
>>
>> Again, the size faking required, when implemented in a file system filter
>> driver, is not a closed problem. There are things which must be accepted
>> in the implementation as best approaches. The only way to implement a
>> 100%, fully functional encryption driver is where you own all file objects
>> and use the underlying file system as a storage area.
>>
>> Pete
>>
>> Kernel Drivers
>> Windows File System and Device Driver Consulting
>> www.KernelDrivers.com
>> (303)546-0300
>>
>> xxxxx@web.de wrote:
>>> 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;
>>>
>>> } —
>>> NTFSD is sponsored by OSR
>>>
>>> For our schedule debugging and file system seminars
>>> (including our new fs mini-filter seminar) visit:
>>> http://www.osr.com/seminars
>>>
>>> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
>>> To unsubscribe send a blank email to xxxxx@lists.osr.com
>
>
>
> —
> NTFSD is sponsored by OSR
>
> For our schedule debugging and file system seminars
> (including our new fs mini-filter seminar) visit:
> http://www.osr.com/seminars
>
> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com

Let’s assume the file is some multiple of 16 bytes excluding sparse and
other weird options. Then life is easy and it will encrypt/decrypt easily.
Then we come to some file that has from 1 to 15 bytes dangling at the end.
The last 17 to 31 bytes are mapped as below:

EEEEEEEEEEEEEEEEUUUUUUUUUUUUUUU

I used E & U for obvious reasons. You have encrypted the last 16 byte block
that is complete. You then have 15 bytes of plaintext. This can be any
number from 1 to 15, but I chose 15 for this example. Take those 15 bytes
and the last byte from the last encrypted block to get a 16 byte block to
encrypt. Write those 16 bytes overlaying the one byte from the last 16 byte
block and the 15 remaining bytes.

When you get to the last 17 to 31 bytes of the file, you have to decrypt the
last 16 bytes first. The last 15 bytes you decrypted are now plaintext.
Replace the 16th byte of that last complete block (the one with E’s) with
the first byte of those 16 you just decrypted. You can then decrypt that 16
byte block and have all plaintext. From 1 to 15 bytes will be double
encrypted which won’t work securely with a simple XOR since that may have
the plaintext stored in the file, but DES, 3DES, AES, Blowfish, etc should
work.

It is possible to delay encrypting that last whole 16 byte block until after
you do the remainder. That is a minor implementation detail that should not
affect the security of any standard secure encryption algorithm.

“Peter Scott” wrote in message
news:xxxxx@ntfsd…
>
> My apologies, I actually have not used this method as described by David.
> And still, without size faking in place, I do not see how you can recover
> the last portion of an encrypted buffer without storing it somewhere. My
> point was not where do you get these few bytes to fill in the remaining
> block but that you need to fake the size of the file to the caller.
>
> Please explain if I missed something here.
>
> Pete
>
> Kernel Drivers
> Windows File System and Device Driver Consulting
> www.KernelDrivers.com
> (303)546-0300
>
> David J. Craig wrote:
>> Just take the remaining bytes (those left after processing all 16 byte
>> blocks) and add enough bytes from the previous block that has already
>> been encrypted to bring the total number of bytes to be encrypted up to
>> 16. Encrypt those and place them as the last 16 bytes in the buffer,
>> overwriting how ever many bytes you previously encrypted that were needed
>> for this last block. When decrypting within the last 16 bytes, always do
>> the last 16 first and then the blocks of 16 before that last block next.
>>
>> This will work for any block size, within reason. I think a block size
>> of 500TB might be a little excessive for this logic to be useful. This
>> is just crypto 101. If done correctly, CBC (cipher block chaining) can
>> work too, but many leave it out for this last block if it isn’t a sector
>> multiple.
>>
>>
>> “Peter Scott” wrote in message
>> news:xxxxx@ntfsd…
>>> You missed the hardest problem to solve when implementing an encryption
>>> product in a filter driver. The encryption method you are using can
>>> handle single byte alignment. This won’t work if you use something a
>>> little stringer such as AES, maybe on 16 byte cipher blocks? Thus all of
>>> the IO must be padded out to a 16 byte boundary. You can’t safely move
>>> the EOF out during a paging write so you need to handle this in the
>>> cached side. As well, you don;t want to show the user this padded file
>>> size, you want to show them the real file size. Hence you need to deal
>>> with size faking in the file information requests as well as in the
>>> directory enumeration calls. This latter then begs the question, how do
>>> you recognize a file to be encrypted when processing a directory
>>> enumeration buffer?
>>>
>>> Again, the size faking required, when implemented in a file system
>>> filter driver, is not a closed problem. There are things which must be
>>> accepted in the implementation as best approaches. The only way to
>>> implement a 100%, fully functional encryption driver is where you own
>>> all file objects and use the underlying file system as a storage area.
>>>
>>> Pete
>>>
>>> Kernel Drivers
>>> Windows File System and Device Driver Consulting
>>> www.KernelDrivers.com
>>> (303)546-0300
>>>
>>> xxxxx@web.de wrote:
>>>> 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;
>>>>
>>>> } —
>>>> NTFSD is sponsored by OSR
>>>>
>>>> For our schedule debugging and file system seminars
>>>> (including our new fs mini-filter seminar) visit:
>>>> http://www.osr.com/seminars
>>>>
>>>> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
>>>> To unsubscribe send a blank email to xxxxx@lists.osr.com
>>
>>
>>
>> —
>> NTFSD is sponsored by OSR
>>
>> For our schedule debugging and file system seminars
>> (including our new fs mini-filter seminar) visit:
>> http://www.osr.com/seminars
>>
>> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
>> To unsubscribe send a blank email to xxxxx@lists.osr.com
>

Thanks for the clarification and yes, I have used this approach,
briefly. But I still say that you are only shifting the complexity to
now maintaining these dependent blocks of data. As well, as you pointed
out, CBC would introduce even more complexity to get right.

Also, this does not alleviate the problem at hand where the file size
can change, say by the file system calling CcSetFileSizes() directly,
without the filter driver knowing about the change. In this case, the
EOF has now changed, your end mark which you count back 16 bytes is off
and you are screwed. At least with the padding approach you are not
completely lost and you can recover from the scenario.

Pete

Kernel Drivers
Windows File System and Device Driver Consulting
www.KernelDrivers.com
(303)546-0300

David J. Craig wrote:

Let’s assume the file is some multiple of 16 bytes excluding sparse and
other weird options. Then life is easy and it will encrypt/decrypt easily.
Then we come to some file that has from 1 to 15 bytes dangling at the end.
The last 17 to 31 bytes are mapped as below:

EEEEEEEEEEEEEEEEUUUUUUUUUUUUUUU

I used E & U for obvious reasons. You have encrypted the last 16 byte block
that is complete. You then have 15 bytes of plaintext. This can be any
number from 1 to 15, but I chose 15 for this example. Take those 15 bytes
and the last byte from the last encrypted block to get a 16 byte block to
encrypt. Write those 16 bytes overlaying the one byte from the last 16 byte
block and the 15 remaining bytes.

When you get to the last 17 to 31 bytes of the file, you have to decrypt the
last 16 bytes first. The last 15 bytes you decrypted are now plaintext.
Replace the 16th byte of that last complete block (the one with E’s) with
the first byte of those 16 you just decrypted. You can then decrypt that 16
byte block and have all plaintext. From 1 to 15 bytes will be double
encrypted which won’t work securely with a simple XOR since that may have
the plaintext stored in the file, but DES, 3DES, AES, Blowfish, etc should
work.

It is possible to delay encrypting that last whole 16 byte block until after
you do the remainder. That is a minor implementation detail that should not
affect the security of any standard secure encryption algorithm.

“Peter Scott” wrote in message
> news:xxxxx@ntfsd…
>> My apologies, I actually have not used this method as described by David.
>> And still, without size faking in place, I do not see how you can recover
>> the last portion of an encrypted buffer without storing it somewhere. My
>> point was not where do you get these few bytes to fill in the remaining
>> block but that you need to fake the size of the file to the caller.
>>
>> Please explain if I missed something here.
>>
>> Pete
>>
>> Kernel Drivers
>> Windows File System and Device Driver Consulting
>> www.KernelDrivers.com
>> (303)546-0300
>>
>> David J. Craig wrote:
>>> Just take the remaining bytes (those left after processing all 16 byte
>>> blocks) and add enough bytes from the previous block that has already
>>> been encrypted to bring the total number of bytes to be encrypted up to
>>> 16. Encrypt those and place them as the last 16 bytes in the buffer,
>>> overwriting how ever many bytes you previously encrypted that were needed
>>> for this last block. When decrypting within the last 16 bytes, always do
>>> the last 16 first and then the blocks of 16 before that last block next.
>>>
>>> This will work for any block size, within reason. I think a block size
>>> of 500TB might be a little excessive for this logic to be useful. This
>>> is just crypto 101. If done correctly, CBC (cipher block chaining) can
>>> work too, but many leave it out for this last block if it isn’t a sector
>>> multiple.
>>>
>>>
>>> “Peter Scott” wrote in message
>>> news:xxxxx@ntfsd…
>>>> You missed the hardest problem to solve when implementing an encryption
>>>> product in a filter driver. The encryption method you are using can
>>>> handle single byte alignment. This won’t work if you use something a
>>>> little stringer such as AES, maybe on 16 byte cipher blocks? Thus all of
>>>> the IO must be padded out to a 16 byte boundary. You can’t safely move
>>>> the EOF out during a paging write so you need to handle this in the
>>>> cached side. As well, you don;t want to show the user this padded file
>>>> size, you want to show them the real file size. Hence you need to deal
>>>> with size faking in the file information requests as well as in the
>>>> directory enumeration calls. This latter then begs the question, how do
>>>> you recognize a file to be encrypted when processing a directory
>>>> enumeration buffer?
>>>>
>>>> Again, the size faking required, when implemented in a file system
>>>> filter driver, is not a closed problem. There are things which must be
>>>> accepted in the implementation as best approaches. The only way to
>>>> implement a 100%, fully functional encryption driver is where you own
>>>> all file objects and use the underlying file system as a storage area.
>>>>
>>>> Pete
>>>>
>>>> Kernel Drivers
>>>> Windows File System and Device Driver Consulting
>>>> www.KernelDrivers.com
>>>> (303)546-0300
>>>>
>>>> xxxxx@web.de wrote:
>>>>> 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;
>>>>>
>>>>> } —
>>>>> NTFSD is sponsored by OSR
>>>>>
>>>>> For our schedule debugging and file system seminars
>>>>> (including our new fs mini-filter seminar) visit:
>>>>> http://www.osr.com/seminars
>>>>>
>>>>> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
>>>>> To unsubscribe send a blank email to xxxxx@lists.osr.com
>>>
>>>
>>> —
>>> NTFSD is sponsored by OSR
>>>
>>> For our schedule debugging and file system seminars
>>> (including our new fs mini-filter seminar) visit:
>>> http://www.osr.com/seminars
>>>
>>> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
>>> To unsubscribe send a blank email to xxxxx@lists.osr.com
>
>
>
> —
> NTFSD is sponsored by OSR
>
> For our schedule debugging and file system seminars
> (including our new fs mini-filter seminar) visit:
> http://www.osr.com/seminars
>
> You are currently subscribed to ntfsd as: xxxxx@kerneldrivers.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com

> 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

Bad idea. To support the apps which do noncached non-MMF IO from user mode, you
must decrypt noncached IO including paging IO and not only paging IO.


Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

and this is just the beginning to a robust security product, I see much more
scenarios, to name a few
open for backup intent (archiving decrypted files?)
remote shares (sending decrypted data over the net?)
protection against voluntary bsod (leaving keys and decrypted cached data
dumped to files?)
hibernation?..
J.

“Maxim S. Shatskih” wrote in message
news:xxxxx@ntfsd…
> > 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
>
> Bad idea. To support the apps which do noncached non-MMF IO from user
mode, you
> must decrypt noncached IO including paging IO and not only paging IO.
>
> –
> Maxim Shatskih, Windows DDK MVP
> StorageCraft Corporation
> xxxxx@storagecraft.com
> http://www.storagecraft.com
>
>