From 2acec63b2ed75bf4b71ad257db573c4b8f9639e7 Mon Sep 17 00:00:00 2001 From: tumagonx Date: Tue, 8 Aug 2017 10:54:53 +0700 Subject: initial commit --- userland.c | 573 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 573 insertions(+) create mode 100644 userland.c (limited to 'userland.c') diff --git a/userland.c b/userland.c new file mode 100644 index 0000000..805a413 --- /dev/null +++ b/userland.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * userland.c + * + * Abstract: + * + * This module defines various types routines used to interact with userland agent service. + * + * Author: + * + * Eugene Tsyrklevich 18-Apr-2004 + * + * Revision History: + * + * None. + */ + + +#include "userland.h" +#include "procname.h" +#include "process.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, InitUserland) +#pragma alloc_text (PAGE, ShutdownUserland) +#pragma alloc_text (PAGE, UserlandPostBootup) +#endif + + +BOOLEAN ActiveUserAgent = FALSE; + +PKEVENT UserlandRequestUserEvent = NULL; +HANDLE UserlandRequestUserEventHandle = NULL; + +PUSERLAND_REQUEST_HEADER UserlandRequestList = NULL; +KSPIN_LOCK gUserlandRequestListSpinLock; + +BOOLEAN CacheSid = TRUE; +ULONG CachedSidSize = 0, CachedSidReplySize = 0; +PVOID CachedSid = NULL; +PSID_RESOLVE_REPLY CachedSidReply = NULL; + +UCHAR SeqId = 0; /* global sequence id used for matching up userland requests & replies */ + + + +/* + * UserlandPostBootup() + * + * Description: + * Initializes userland related data once computer is done booting up. + * + * \BaseNamedObjects object directory is not created until the Win32® system initializes. + * Therefore, drivers that are loaded at boot time cannot create event objects in their DriverEntry + * routines that are visible to user-mode programs. + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +UserlandPostBootup() +{ + UNICODE_STRING uszUserlandRequestEvent; + + + ASSERT(BootingUp == FALSE); + + + KeInitializeSpinLock(&gUserlandRequestListSpinLock); + + + /* Create an event for userland agent service to monitor */ +#define USERLAND_REQUEST_EVENT_NAME L"\\BaseNamedObjects\\OzoneUserlandRequestEvent" + + RtlInitUnicodeString(&uszUserlandRequestEvent, USERLAND_REQUEST_EVENT_NAME); + + + if ((UserlandRequestUserEvent = IoCreateNotificationEvent(&uszUserlandRequestEvent, &UserlandRequestUserEventHandle)) == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("UserlandPostBootup: IoCreateNotificationEvent failed\n")); + return FALSE; + } + + KeClearEvent(UserlandRequestUserEvent); + + + return TRUE; +} + + + +/* + * IssueUserlandSidResolveRequest() + * + * Description: + * Resolves binary SID to its ASCII representation by querying a userland agent. + * + * Parameters: + * . + * + * Returns: + * TRUE to indicate success, FALSE otherwise. + */ + +BOOLEAN +IssueUserlandSidResolveRequest(PIMAGE_PID_ENTRY Process) +{ + PSID_RESOLVE_REQUEST pSidResolveRequest; + USHORT Size; + PCHAR UserSidBuffer; + KIRQL irql; + LARGE_INTEGER liDelay; + NTSTATUS status; + + + ASSERT(Process); + + + if (!ActiveUserAgent) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_VERBOSE, ("IssueUserlandSidResolveRequest: no Agent Service\n")); + return FALSE; + } + + if (KeGetCurrentIrql() != PASSIVE_LEVEL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: Running at high IRQL\n")); + return FALSE; + } + + if (Process->ProcessId == SystemProcessId) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: Cannot issue requests on behalf of SYSTEM process\n")); +// return FALSE; + } + + + /* + * the following code assumes that SID_RESOLVE_REQUESTS consists of USERLAND_REQUEST_HEADER + * followed by PSID_AND_ATTRIBUTES. + * + * GetCurrentUserSid() allocates memory for user sid + PSID_AND_ATTRIBUTES and converts SID + * from absolute into relative format. + */ + + Size = sizeof(USERLAND_REQUEST_HEADER); + UserSidBuffer = GetCurrentUserSid(&Size); + + if (UserSidBuffer == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: out of memory\n")); + return FALSE; + } + + /* check for a previously resolved cached SID */ + while (CacheSid == TRUE) + { + int SidSize = Size - sizeof(USERLAND_REQUEST_HEADER); + + + /* cache hit? */ + if (CachedSid && CachedSidReply && + SidSize == CachedSidSize && + memcmp(UserSidBuffer + sizeof(USERLAND_REQUEST_HEADER), CachedSid, CachedSidSize) == 0) + { + Process->WaitingForUserRequestId = 0; + Process->UserlandReply = ExAllocatePoolWithTag(PagedPool, CachedSidReplySize, _POOL_TAG); + + if (Process->UserlandReply == NULL) + { + ExFreePoolWithTag(UserSidBuffer, _POOL_TAG); + return FALSE; + } + + memcpy(Process->UserlandReply, CachedSidReply, CachedSidReplySize); + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: Cache hit. Returning username %S\n", CachedSidReply->UserName)); + + ExFreePoolWithTag(UserSidBuffer, _POOL_TAG); + + return TRUE; + } + + + /* cache miss */ + CachedSidSize = SidSize; + + if (CachedSid) + ExFreePoolWithTag(CachedSid, _POOL_TAG); + + CachedSid = ExAllocatePoolWithTag(PagedPool, SidSize, _POOL_TAG); + + if (CachedSid == NULL) + { + ExFreePoolWithTag(UserSidBuffer, _POOL_TAG); + return FALSE; + } + + memcpy(CachedSid, UserSidBuffer + sizeof(USERLAND_REQUEST_HEADER), SidSize); + + if (CachedSidReply) + { + ExFreePoolWithTag(CachedSidReply, _POOL_TAG); + CachedSidReply = NULL; + } + + break; + } + + + pSidResolveRequest = (PSID_RESOLVE_REQUEST) UserSidBuffer; + + + pSidResolveRequest->RequestHeader.RequestType = USERLAND_SID_RESOLVE_REQUEST; + pSidResolveRequest->RequestHeader.RequestSize = Size; + pSidResolveRequest->RequestHeader.ProcessId = Process->ProcessId; + + if (++SeqId == 0) SeqId = 1; + pSidResolveRequest->RequestHeader.SeqId = SeqId; + + + KeAcquireSpinLock(&gUserlandRequestListSpinLock, &irql); + { + pSidResolveRequest->RequestHeader.Next = UserlandRequestList; + UserlandRequestList = (PUSERLAND_REQUEST_HEADER) pSidResolveRequest; + } + KeReleaseSpinLock(&gUserlandRequestListSpinLock, irql); + + + Process->WaitingForUserRequestId = SeqId; + Process->UserlandReply = NULL; + + + KeClearEvent(&Process->UserlandRequestDoneEvent); + + + /* signal the userland agent service */ + if (UserlandRequestUserEvent) + { + /* pulse twice since userland sometimes misses single pulses */ + KeSetEvent(UserlandRequestUserEvent, 0, FALSE); + KeClearEvent(UserlandRequestUserEvent); + + KeSetEvent(UserlandRequestUserEvent, 0, FALSE); + KeClearEvent(UserlandRequestUserEvent); + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: UserlandRequestUserEvent is NULL\n")); + } + + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%d IssueUserlandSidResolveRequest: Waiting for agent service\n", CURRENT_PROCESS_PID)); + + + /* wait for the userland service to reply (wait for maximum of USERLAND_REQUEST_TIMEOUT seconds) */ + liDelay.QuadPart = SECONDS(USERLAND_REQUEST_TIMEOUT); + + status = KeWaitForSingleObject(&Process->UserlandRequestDoneEvent, UserRequest, KernelMode, FALSE, &liDelay); + + + Process->WaitingForUserRequestId = 0; + + if (status == STATUS_TIMEOUT) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: KeWaitForSingleObject timed out\n")); + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandSidResolveRequest: KeWaitForSingleObject returned\n")); + + /* cache the resolved Sid */ + if (CacheSid == TRUE && Process->UserlandReply != NULL) + { + CachedSidReply = ExAllocatePoolWithTag(PagedPool, Process->UserlandReply->ReplySize, _POOL_TAG); + + if (CachedSidReply) + { + memcpy(CachedSidReply, Process->UserlandReply, Process->UserlandReply->ReplySize); + + CachedSidReplySize = Process->UserlandReply->ReplySize; + } + } + } + + + /* at this point UserSidBuffer/pSidResolveRequest has already been deallocated in driver.c!DriverDeviceControl */ + + + return TRUE; +} + + + +/* + * IssueUserlandAskUserRequest() + * + * Description: + * . + * + * Parameters: + * . + * + * Returns: + * . + */ + +ACTION_TYPE +IssueUserlandAskUserRequest(RULE_TYPE RuleType, UCHAR OperationType, PCHAR ObjectName) +{ + ACTION_TYPE Action = ACTION_DENY_DEFAULT; + PIMAGE_PID_ENTRY Process; + PASK_USER_REQUEST pAskUserRequest; + USHORT Size; + KIRQL irql; + LARGE_INTEGER liDelay; + NTSTATUS status; + +#define NAME_BUFFER_SIZE 256 + ANSI_STRING asObjectName; + UNICODE_STRING usObjectName; + WCHAR ObjectNameW[NAME_BUFFER_SIZE] = { 0 }; + + + ASSERT(ObjectName); + + + if (!ActiveUserAgent) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: no Agent Service\n")); + return Action; + } + + if (KeGetCurrentIrql() != PASSIVE_LEVEL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: Running at high IRQL\n")); + return FALSE; + } + + + if (CURRENT_PROCESS_PID == SystemProcessId) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: Cannot issue requests on behalf of SYSTEM process\n")); + return FALSE; + } + + + Process = FindImagePidEntry(CURRENT_PROCESS_PID, 0); + if (Process == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: Process %d not found\n", CURRENT_PROCESS_PID)); + return Action; + } + + + /* \KnownDlls section rules need to be converted to DLLs */ + if (RuleType == RULE_SECTION) + { + if (_strnicmp(ObjectName, "\\KnownDlls\\", 11) == 0) + { + ObjectName += 11; + RuleType = RULE_DLL; + OperationType = OP_LOAD; + } + } + + + ObjectName = FilterObjectName(ObjectName); + + usObjectName.Length = 0; + usObjectName.MaximumLength = sizeof(ObjectNameW); + usObjectName.Buffer = ObjectNameW; + + RtlInitAnsiString(&asObjectName, ObjectName); + + RtlAnsiStringToUnicodeString(&usObjectName, &asObjectName, FALSE); + ObjectNameW[ asObjectName.Length ] = 0; + + + /* extra +1 for ProcessName zero termination */ + Size = sizeof(ASK_USER_REQUEST) + (wcslen(ObjectNameW) + wcslen(Process->ImageName) + 1) * sizeof(WCHAR); + + pAskUserRequest = ExAllocatePoolWithTag(NonPagedPool, Size, _POOL_TAG); + + if (pAskUserRequest == NULL) + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: out of memory\n")); + return Action; + } + + + pAskUserRequest->RequestHeader.RequestType = USERLAND_ASK_USER_REQUEST; + pAskUserRequest->RequestHeader.RequestSize = Size; + pAskUserRequest->RequestHeader.ProcessId = Process->ProcessId; + + if (++SeqId == 0) SeqId = 1; + pAskUserRequest->RequestHeader.SeqId = SeqId; + + pAskUserRequest->RuleType = RuleType; + pAskUserRequest->OperationType = OperationType; + pAskUserRequest->ObjectNameLength = (USHORT) wcslen(ObjectNameW); + pAskUserRequest->ProcessNameLength = (USHORT) wcslen(Process->ImageName); + + wcscpy(pAskUserRequest->ObjectName, ObjectNameW); + + /* process name follows the object name */ + wcscpy(pAskUserRequest->ObjectName + pAskUserRequest->ObjectNameLength + 1, Process->ImageName); + + + KeAcquireSpinLock(&gUserlandRequestListSpinLock, &irql); + { + pAskUserRequest->RequestHeader.Next = UserlandRequestList; + UserlandRequestList = (PUSERLAND_REQUEST_HEADER) pAskUserRequest; + } + KeReleaseSpinLock(&gUserlandRequestListSpinLock, irql); + + + Process->WaitingForUserRequestId = SeqId; + Process->UserlandReply = NULL; + + + KeClearEvent(&Process->UserlandRequestDoneEvent); + + + /* signal the userland agent service */ + if (UserlandRequestUserEvent) + { + /* pulse twice since userland sometimes misses single pulses */ + KeSetEvent(UserlandRequestUserEvent, 0, FALSE); + KeClearEvent(UserlandRequestUserEvent); + + KeSetEvent(UserlandRequestUserEvent, 0, FALSE); + KeClearEvent(UserlandRequestUserEvent); + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: UserlandRequestUserEvent is NULL\n")); + } + + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%d IssueUserlandAskUserRequest: Waiting for agent service\n", CURRENT_PROCESS_PID)); + + + /* wait for the user/userland service to reply (wait for maximum of USERLAND_REQUEST_TIMEOUT seconds) */ + liDelay.QuadPart = SECONDS(USERLAND_REQUEST_TIMEOUT); + + status = KeWaitForSingleObject(&Process->UserlandRequestDoneEvent, UserRequest, KernelMode, FALSE, &liDelay); + + + Process->WaitingForUserRequestId = 0; + + if (status != STATUS_TIMEOUT) + { + PASK_USER_REPLY pAskUserReply = (PASK_USER_REPLY) Process->UserlandReply; + + if (pAskUserReply) + { + Action = pAskUserReply->Action; + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: received back action %d\n", Action)); + + ExFreePoolWithTag(Process->UserlandReply, _POOL_TAG); + Process->UserlandReply = NULL; + } + + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: IssueUserlandAskUserRequest returned\n")); + } + else + { + LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("IssueUserlandAskUserRequest: KeWaitForSingleObject timed out\n")); + + //XXX need to remove pAskUserRequest from the list + } + + + /* at this point pAskUserRequest has already been deallocated in driver.c!DriverDeviceControl */ + + + return Action; +} + + + +/* + * InitUserland() + * + * Description: + * Initializes all the userland related data. + * + * NOTE: Called once during driver initialization (DriverEntry()). + * + * Parameters: + * None. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +InitUserland() +{ + if (BootingUp == FALSE) + { + if (UserlandPostBootup() == FALSE) + + return FALSE; + } + + + return TRUE; +} + + + +/* + * ShutdownUserland() + * + * Description: + * XXX. + * + * NOTE: Called once during driver unload (DriverUnload()). + * + * Parameters: + * None. + * + * Returns: + * Nothing. + */ + +VOID +ShutdownUserland() +{ + PUSERLAND_REQUEST_HEADER tmp; + KIRQL irql; + + + if (UserlandRequestUserEventHandle) + ZwClose(UserlandRequestUserEventHandle); + + + KeAcquireSpinLock(&gUserlandRequestListSpinLock, &irql); + { + while (UserlandRequestList) + { + tmp = UserlandRequestList; + UserlandRequestList = UserlandRequestList->Next; + + ExFreePoolWithTag(tmp, _POOL_TAG); + } + } + KeReleaseSpinLock(&gUserlandRequestListSpinLock, irql); + + + if (CachedSid) + { + ExFreePoolWithTag(CachedSid, _POOL_TAG); + CachedSid = NULL; + } + + if (CachedSidReply) + { + ExFreePoolWithTag(CachedSidReply, _POOL_TAG); + CachedSidReply = NULL; + } +} -- cgit v1.3