In the previous part we discussed the I/O Manager, but we need to dig into this deeper by explaining how we are implementing all these dispatch routines.
I already said it does not matter which file system we are talking about, standard Windows NTFS, FAT, ReFS or a personal one. Every single one of them need to handle Windows IRP's. What happens inside the dispatch routine depends entirely on the file system, for example, NTFS maintains a hidden database file ($MFT) which stores relevant file system information, while FAT does not offer this complexity. Also the way FAT read/write to disk sectors is significantly different from NTFS.
Create requests (FsDispatchCreate) - Open/Creates a new handle
This is the most used request in all existing file systems. Every time we call CreateFile/OpenFile (kernel32.dll) or ZwCreateFile/ZwOpenFile (ntdll.dll) we are producing the following IRP: IRP_MJ_CREATE. As the reader can observe, the system will always call this IRP when dealing with file system operations, since we can not read/write/query/set or open volumes if we do not get a valid handle first. There is an interesting routine, KeExpandKernelStackAndCalloutEx which we will use to avoid the stack overheat IRP_MJ_CREATE produces.
The main parameters we are passing to the structure are pIrp (IRP structure) and pStack (PIO_STACK_LOCATION structure). The FsDispatchCalloutRoutine implements the actual dispatch routine.
The first conditional will check whether we are creating a new file or opening a handle to the file system itself (the device object). If we are just opening a handle to the volume we will return a FILE_OPEN flag to the caller (Note there is no security/permissions check as this software is not commercial, so no efforts have been applied in order to fix this).
If we are introducing a new file to the disk sectors on the other hand (the format should be \\Device\\SecureVol\\TEST.TXT) the FileName UNICODE member of FILE_OBJECT structure will hold \\TEST.TXT. After converting this string to ansi, we are ready to introduce it inside the file system.
Write requests (FsDispatchWrite) - Writes a data buffer to existing file
This request produces the IRP_MJ_WRITE. Every time we call WriteFile (kernel32.dll) or ZwWriteFile (ntdll.dll) from user mode, the system will trigger this IRP. Our first FAT feature is developed in this request. We have the ability to encrypt files ''on the fly'' by setting a global value via ioctl which is managed in FsDispatchDeviceControl
The first part of the function copies the file handle data to FileCtx. First, the function checks if the file is not locked, if it does, then ZwWriteFile from user-mode returns STATUS_FILE_LOCKED_WITH_WRITERS (note this scenario can only occur with text files where we might append data). Also it maps the physical pages to system space. For the mapped files feature, function checks if a valid PE structure exists so it can insert all binary files in the list (Note only binary files will be mapped in SecureVol)
The
second part of the function consists in the buffer encryption.
SecureVol uses rc4 to perform this task. First it initializes the
context, allocates a block of memory using the file bytes as second
parameter and then encrypts the entire file buffer.The final call is FsReadWriteFile
If the global encrypt variable is set to true we perform the following operations
- Compute a hash using the current file index as identifier
- Lock the insertion to the binary tree
- Check if the hash is already added
- If hash not found, insert it
- Release lock
After mapping, we can see the Buffer variable contents from windbg, it shows the MZ header so we know its a valid binary file.
Next step is to compute the hash via RtlComputeCrc32, check it is not already present in the binary tree and insert it.
After encrypting the entire file buffer
Another view of the FILE_OBJECT structure
We can observe the truncated file (from user-mode) KPROCESS~1.SYS. The next picture will show the caller view once the file has been saved in the file system
In the following article we will explain the IRP_MJ_READ / IRP_MJ_QUERY_INFORMATION / IRP_MJ_SET_INFORMATION type of requests.
Comments
Post a Comment