MalwareSourceCode/Win32/Proof of Concepts/HookDeviceIocontrlFile/HookDeviceIoControlFileDrv/HookDeviceIoControlFile/rules.c
vxunderground 900263ea6f updates and moves
n/a
2022-04-11 20:00:13 -05:00

696 lines
18 KiB
C

#include "stdafx.h"
// defined in driver.cpp
extern UNICODE_STRING m_RegistryPath;
extern KMUTEX m_CommonMutex;
BOOLEAN g_RuleInited = FALSE;
ERESOURCE g_RuleResource;
LIST_ENTRY g_DenyRuleList;
LIST_ENTRY g_AllowRuleList;
LIST_ENTRY g_DbgcbRuleList;
FORCEINLINE
VOID
RuleLock(
__in BOOLEAN Exclusive
)
{
KeEnterCriticalRegion();
if (Exclusive)
ExAcquireResourceExclusiveLite(&g_RuleResource, TRUE);
else
ExAcquireResourceSharedLite(&g_RuleResource, TRUE);
}
FORCEINLINE
VOID
RuleUnlock()
{
ExReleaseResourceLite(&g_RuleResource);
KeLeaveCriticalRegion();
}
NTSTATUS FltInitRuleList()
{
NTSTATUS Status = 0;
InitializeListHead( &g_DenyRuleList );
InitializeListHead( &g_AllowRuleList );
InitializeListHead( &g_DbgcbRuleList );
Status = ExInitializeResourceLite( &g_RuleResource );
if(!NT_SUCCESS(Status))
return Status;
g_RuleInited = TRUE;
return STATUS_SUCCESS;
}
VOID FltUnInitRuleList()
{
if(!g_RuleInited)
return;
FltFlushAllList();
g_RuleInited = FALSE;
ExDeleteResourceLite(&g_RuleResource);
}
//--------------------------------------------------------------------------------------
wchar_t xchrlower_w(wchar_t chr)
{
if ((chr >= 'A') && (chr <= 'Z'))
{
return chr + ('a'-'A');
}
return chr;
}
//--------------------------------------------------------------------------------------
BOOLEAN EqualUnicodeString_r(PUNICODE_STRING Str1, PUNICODE_STRING Str2, BOOLEAN CaseInSensitive)
{
USHORT CmpLen = min(Str1->Length, Str2->Length) / sizeof(WCHAR);
USHORT i = 0;
for ( i = 1; i < CmpLen; i++)
{
WCHAR Chr1 = Str1->Buffer[Str1->Length / sizeof(WCHAR) - i],
Chr2 = Str2->Buffer[Str2->Length / sizeof(WCHAR) - i];
if (CaseInSensitive)
{
Chr1 = xchrlower_w(Chr1);
Chr2 = xchrlower_w(Chr2);
}
if (Chr1 != Chr2)
{
return FALSE;
}
}
return TRUE;
}
PIOCTL_FILTER FltAdd(PIOCTL_FILTER f, PLIST_ENTRY ListEntry, ULONG KdCommandLength)
{
ULONG Length = 0;
PIOCTL_FILTER f_entry = NULL;
if(!g_RuleInited || !ListEntry || !f)
return NULL;
Length = KdCommandLength + sizeof(IOCTL_FILTER);
f_entry = (PIOCTL_FILTER)ExAllocatePool(NonPagedPool, Length);
if (f_entry)
{
RtlZeroMemory(f_entry, Length);
RtlCopyMemory(f_entry, f, sizeof(IOCTL_FILTER));
InsertHeadList(ListEntry, &f_entry->List);
return f_entry;
}
else
{
DbgMsg(__FILE__, __LINE__, "ExAllocatePool() fails\n");
}
return NULL;
}
VOID DeferenceRuleCount(PIOCTL_FILTER Item)
{
if(!g_RuleInited)
return;
RuleLock(TRUE);
Item->ReferenceCount--;
RuleUnlock();
}
PIOCTL_FILTER FltAddDenyRule(PIOCTL_FILTER f, ULONG KdCommandLength)
{
PIOCTL_FILTER Item = NULL;
if(!g_RuleInited)
return NULL;
RuleLock(TRUE);
Item = FltAdd(f, &g_DenyRuleList, KdCommandLength);
Item->ReferenceCount +=1 ;
RuleUnlock();
return Item;
}
PIOCTL_FILTER FltAddAllowRule(PIOCTL_FILTER f, ULONG KdCommandLength)
{
PIOCTL_FILTER Item = NULL;
if(!g_RuleInited)
return NULL;
RuleLock(TRUE);
Item = FltAdd(f, &g_AllowRuleList, KdCommandLength);
Item->ReferenceCount +=1 ;
RuleUnlock();
return Item;
}
PIOCTL_FILTER FltAddDbgcbRule(PIOCTL_FILTER f, ULONG KdCommandLength)
{
PIOCTL_FILTER Item = NULL;
if(!g_RuleInited)
return NULL;
RuleLock(TRUE);
Item = FltAdd(f, &g_DbgcbRuleList, KdCommandLength);
Item->ReferenceCount +=1 ;
RuleUnlock();
return Item;
}
//--------------------------------------------------------------------------------------
void FltFlushList(PLIST_ENTRY ListEntryHead)
{
PLIST_ENTRY ListEntry = NULL;
PLIST_ENTRY ListRemove = NULL;
PIOCTL_FILTER RuleItem = NULL;
if(!g_RuleInited || !ListEntryHead)
return;
ListEntry = ListEntryHead->Flink;
while(ListEntry != ListEntryHead)
{
RuleItem = CONTAINING_RECORD(ListEntry, IOCTL_FILTER, List);
if(RuleItem->ReferenceCount != 0)
{
ListEntry = ListEntry->Flink;
continue;
}
if (RuleItem->Type == FLT_DEVICE_NAME ||
RuleItem->Type == FLT_DRIVER_NAME ||
RuleItem->Type == FLT_PROCESS_PATH)
{
RtlFreeUnicodeString(&RuleItem->usName);
}
ListRemove = ListEntry;
ListEntry = ListEntry->Flink;
RemoveEntryList(ListRemove);
ExFreePool(RuleItem);
}
}
void FltFlushAllList()
{
if(!g_RuleInited)
return ;
RuleLock(TRUE);
FltFlushList(&g_DenyRuleList);
FltFlushList(&g_AllowRuleList);
FltFlushList(&g_DbgcbRuleList);
RuleUnlock();
}
//--------------------------------------------------------------------------------------
PIOCTL_FILTER FltMatch(
PLIST_ENTRY ListEntryHead,
PUNICODE_STRING fDeviceName,
PUNICODE_STRING fDriverName,
ULONG IoControlCode,
PUNICODE_STRING fProcessName)
{
PIOCTL_FILTER ret = NULL;
PIOCTL_FILTER RuleItem = NULL;
PLIST_ENTRY ListEntry = NULL;
if(!ListEntryHead)
return NULL;
// match parameters by filter list
ListEntry = ListEntryHead->Flink;
while (ListEntry != ListEntryHead)
{
RuleItem = CONTAINING_RECORD(ListEntry, IOCTL_FILTER, List);
if (RuleItem->bDbgcbAction)
{
// skip entries with debugger commands
goto next;
}
if (RuleItem->Type == FLT_DEVICE_NAME)
{
if (EqualUnicodeString_r(&RuleItem->usName, fDeviceName, TRUE))
{
ret = RuleItem;
break;
}
}
else if (RuleItem->Type == FLT_DRIVER_NAME)
{
if (EqualUnicodeString_r(&RuleItem->usName, fDriverName, TRUE))
{
ret = RuleItem;
break;
}
}
else if (RuleItem->Type == FLT_IOCTL_CODE)
{
if (RuleItem->IoctlCode == IoControlCode)
{
ret = RuleItem;
break;
}
}
else if (RuleItem->Type == FLT_PROCESS_PATH)
{
if (EqualUnicodeString_r(&RuleItem->usName, fProcessName, TRUE))
{
ret = RuleItem;
break;
}
}
next:
ListEntry = ListEntry->Flink;
}
return ret;
}
BOOLEAN FltMatchDeny(
PUNICODE_STRING fDeviceName,
PUNICODE_STRING fDriverName,
ULONG IoControlCode,
PUNICODE_STRING fProcessName)
{
PIOCTL_FILTER Rule = NULL;
if(!g_RuleInited)
return FALSE;
RuleLock(FALSE);
Rule = FltMatch(&g_DenyRuleList, fDeviceName, fDriverName, IoControlCode, fProcessName);
RuleUnlock();
if(Rule)
return TRUE;
else
return FALSE;
}
BOOLEAN FltMatchAllow(
PUNICODE_STRING fDeviceName,
PUNICODE_STRING fDriverName,
ULONG IoControlCode,
PUNICODE_STRING fProcessName)
{
PIOCTL_FILTER Rule = NULL;
if(!g_RuleInited)
return FALSE;
RuleLock(FALSE);
if(IsListEmpty(&g_AllowRuleList))
return TRUE;
Rule = FltMatch(&g_AllowRuleList, fDeviceName, fDriverName, IoControlCode, fProcessName);
RuleUnlock();
if(Rule)
return TRUE;
else
return FALSE;
}
//--------------------------------------------------------------------------------------
char *FltGetKdCommand(
PUNICODE_STRING fDeviceName,
PUNICODE_STRING fDriverName,
ULONG IoControlCode,
PUNICODE_STRING fProcessName)
{
char *lpszCmd = NULL;
PLIST_ENTRY ListEntry = NULL;
PIOCTL_FILTER RuleItem = NULL;
if(!g_RuleInited)
return NULL;
RuleLock(FALSE);
// match parameters by filter list
ListEntry = g_DbgcbRuleList.Flink;
while (ListEntry != &g_DbgcbRuleList)
{
RuleItem = CONTAINING_RECORD(ListEntry, IOCTL_FILTER, List);
if (!RuleItem->bDbgcbAction)
{
// skip entries with debugger commands
goto next;
}
if (RuleItem->Type == FLT_DEVICE_NAME)
{
if (EqualUnicodeString_r(&RuleItem->usName, fDeviceName, TRUE))
{
lpszCmd = RuleItem->szKdCommand;
break;
}
}
else if (RuleItem->Type == FLT_DRIVER_NAME)
{
if (EqualUnicodeString_r(&RuleItem->usName, fDriverName, TRUE))
{
lpszCmd = RuleItem->szKdCommand;
break;
}
}
else if (RuleItem->Type == FLT_IOCTL_CODE)
{
if (RuleItem->IoctlCode == IoControlCode)
{
lpszCmd = RuleItem->szKdCommand;
break;
}
}
else if (RuleItem->Type == FLT_PROCESS_PATH)
{
if (EqualUnicodeString_r(&RuleItem->usName, fProcessName, TRUE))
{
lpszCmd = RuleItem->szKdCommand;
break;
}
}
next:
ListEntry = ListEntry->Flink;
}
RuleUnlock();
return lpszCmd;
}
//--------------------------------------------------------------------------------------
BOOLEAN FltIsMatchedRequest(
PUNICODE_STRING fDeviceName,
PUNICODE_STRING fDriverName,
ULONG IoControlCode,
PUNICODE_STRING fProcessName)
{
if(!g_RuleInited)
return FALSE;
// match process by allow/deny list
if (FltMatchAllow(fDeviceName, fDriverName, IoControlCode, fProcessName) &&
FltMatchDeny(fDeviceName, fDriverName, IoControlCode, fProcessName) == FALSE)
{
return TRUE;
}
return FALSE;
}
//--------------------------------------------------------------------------------------
BOOLEAN SaveRules(PLIST_ENTRY ListEntryHead, HANDLE hKey, PUNICODE_STRING usValueName)
{
BOOLEAN bRet = FALSE;
ULONG BuffSize = 0, RulesToSerialize = 0;
PLIST_ENTRY ListEntry = NULL;
PIOCTL_FILTER RuleItem = NULL;
if(!ListEntryHead)
return FALSE;
// calculate reqired buffer size
ListEntry = ListEntryHead->Flink;
while (ListEntry != ListEntryHead)
{
RuleItem = CONTAINING_RECORD(ListEntry, IOCTL_FILTER, List);
if (!RuleItem->bDbgcbAction)
{
BuffSize += sizeof(IOCTL_FILTER_SERIALIZED);
if (RuleItem->Type == FLT_DEVICE_NAME ||
RuleItem->Type == FLT_DRIVER_NAME ||
RuleItem->Type == FLT_PROCESS_PATH)
{
// we an have object name
BuffSize += RuleItem->usName.Length;
}
RulesToSerialize++;
}
ListEntry = ListEntry->Flink;
}
if (BuffSize > 0)
{
// allocate memory for serialized rules
PUCHAR Buff = (PUCHAR)M_ALLOC(BuffSize);
if (Buff)
{
NTSTATUS ns = STATUS_UNSUCCESSFUL;
PIOCTL_FILTER_SERIALIZED f_s = NULL;
RtlZeroMemory(Buff, BuffSize);
f_s = (PIOCTL_FILTER_SERIALIZED)Buff;
// serialize available entries
ListEntry = ListEntryHead->Flink;
while (ListEntry != ListEntryHead)
{
RuleItem = CONTAINING_RECORD(ListEntry, IOCTL_FILTER, List);
if (!RuleItem->bDbgcbAction)
{
ULONG NextEntryOffset = sizeof(IOCTL_FILTER_SERIALIZED);
f_s->Type = RuleItem->Type;
f_s->IoctlCode = RuleItem->IoctlCode;
if (RuleItem->Type == FLT_DEVICE_NAME ||
RuleItem->Type == FLT_DRIVER_NAME ||
RuleItem->Type == FLT_PROCESS_PATH)
{
// we have an object name
f_s->NameLen = RuleItem->usName.Length;
NextEntryOffset += f_s->NameLen;
memcpy(&f_s->Name, RuleItem->usName.Buffer, f_s->NameLen);
}
// go to the next serialized entry
f_s = (PIOCTL_FILTER_SERIALIZED)((PUCHAR)f_s + NextEntryOffset);
}
ListEntry = ListEntry->Flink;
}
ns = ZwSetValueKey(hKey, usValueName, 0, REG_BINARY, Buff, BuffSize);
if (NT_SUCCESS(ns))
{
bRet = TRUE;
DbgMsg(
__FILE__, __LINE__,
__FUNCTION__"(): %d rules (%d bytes) saved in '%wZ'\n",
RulesToSerialize, BuffSize, usValueName
);
}
else
{
DbgMsg(__FILE__, __LINE__, "ZwSetValueKey() fails; status: 0x%.8x\n", ns);
}
M_FREE(Buff);
}
else
{
DbgMsg(__FILE__, __LINE__, "M_ALLOC() fails\n");
}
}
return bRet;
}
BOOLEAN SaveDenyRules(HANDLE hKey, PUNICODE_STRING usValueName)
{
BOOLEAN bRet = FALSE;
if(!g_RuleInited)
return FALSE;
RuleLock(FALSE);
bRet = SaveRules(&g_DenyRuleList, hKey, usValueName);
RuleUnlock();
return bRet;
}
BOOLEAN SaveAllowRules(HANDLE hKey, PUNICODE_STRING usValueName)
{
BOOLEAN bRet = FALSE;
if(!g_RuleInited)
return FALSE;
RuleLock(FALSE);
bRet = SaveRules(&g_AllowRuleList, hKey, usValueName);
RuleUnlock();
return bRet;
}
//--------------------------------------------------------------------------------------
BOOLEAN LoadRules(PLIST_ENTRY ListEntryHead, HANDLE hKey, PUNICODE_STRING usValueName)
{
BOOLEAN bRet = FALSE;
PKEY_VALUE_FULL_INFORMATION KeyInfo = NULL;
ULONG Length = 0, RulesLoaded = 0;
NTSTATUS ns = 0;
if(!ListEntryHead)
return FALSE;
// query buffer size
ns = ZwQueryValueKey(
hKey,
usValueName,
KeyValueFullInformation,
KeyInfo,
0,
&Length
);
if (ns == STATUS_BUFFER_OVERFLOW ||
ns == STATUS_BUFFER_TOO_SMALL)
{
// allocate buffer
PKEY_VALUE_FULL_INFORMATION KeyInfo = (PKEY_VALUE_FULL_INFORMATION)M_ALLOC(Length);
if (KeyInfo)
{
// query value
ns = ZwQueryValueKey(
hKey,
usValueName,
KeyValueFullInformation,
KeyInfo,
Length,
&Length
);
if (NT_SUCCESS(ns))
{
if (KeyInfo->DataLength > 0)
{
// deserialize rules
PUCHAR Buff = (PUCHAR)KeyInfo + KeyInfo->DataOffset;
PIOCTL_FILTER_SERIALIZED f_s = (PIOCTL_FILTER_SERIALIZED)Buff;
while ((ULONG)((PUCHAR)f_s - Buff) < KeyInfo->DataLength)
{
// add rule into list
IOCTL_FILTER Flt;
RtlZeroMemory(&Flt, sizeof(Flt));
Flt.Type = f_s->Type;
Flt.IoctlCode = f_s->IoctlCode;
if ((f_s->Type == FLT_DEVICE_NAME ||
f_s->Type == FLT_DRIVER_NAME ||
f_s->Type == FLT_PROCESS_PATH) &&
f_s->NameLen > 0)
{
// we have an object name
if (AllocUnicodeString(&Flt.usName, (USHORT)f_s->NameLen))
{
Flt.usName.Length = (USHORT)f_s->NameLen;
memcpy(Flt.usName.Buffer, &f_s->Name, f_s->NameLen);
DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): '%wZ'\n", &Flt.usName);
}
else
{
goto err;
}
}
if (!FltAdd(&Flt, ListEntryHead, 0))
{
if (Flt.usName.Buffer)
{
RtlFreeUnicodeString(&Flt.usName);
}
}
else
{
RulesLoaded++;
}
err:
// go to the next serialized entry
f_s = (PIOCTL_FILTER_SERIALIZED)((PUCHAR)f_s +
sizeof(IOCTL_FILTER_SERIALIZED) + f_s->NameLen);
}
}
DbgMsg(
__FILE__, __LINE__,
__FUNCTION__"(): %d rules loaded from '%wZ'\n",
RulesLoaded, usValueName
);
bRet = TRUE;
}
else
{
DbgMsg(__FILE__, __LINE__, "ZwQueryValueKey() fails; status: 0x%.8x\n", ns);
}
M_FREE(KeyInfo);
}
else
{
DbgMsg(__FILE__, __LINE__, "M_ALLOC() fails\n");
}
}
else
{
DbgMsg(__FILE__, __LINE__, __FUNCTION__"() WARNING: '%wZ' value is not set\n", usValueName);
}
return bRet;
}
BOOLEAN LoadDenyRules(HANDLE hKey, PUNICODE_STRING usValueName)
{
BOOLEAN bRet = FALSE;
if(!g_RuleInited)
return FALSE;
RuleLock(TRUE);
bRet = LoadRules(&g_DenyRuleList, hKey, usValueName);
RuleUnlock();
return bRet;
}
BOOLEAN LoadAllowRules(HANDLE hKey, PUNICODE_STRING usValueName)
{
BOOLEAN bRet = FALSE;
if(!g_RuleInited)
return FALSE;
RuleLock(TRUE);
bRet = LoadRules(&g_AllowRuleList, hKey, usValueName);
RuleUnlock();
return bRet;
}
//--------------------------------------------------------------------------------------