From 2acec63b2ed75bf4b71ad257db573c4b8f9639e7 Mon Sep 17 00:00:00 2001 From: tumagonx Date: Tue, 8 Aug 2017 10:54:53 +0700 Subject: initial commit --- pathproc.c | 1093 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1093 insertions(+) create mode 100644 pathproc.c (limited to 'pathproc.c') diff --git a/pathproc.c b/pathproc.c new file mode 100644 index 0000000..8778868 --- /dev/null +++ b/pathproc.c @@ -0,0 +1,1093 @@ +/* + * Copyright (c) 2004 Security Architects Corporation. All rights reserved. + * + * Module Name: + * + * pathproc.c + * + * Abstract: + * + * This module implements various pathname handling routines. + * + * Author: + * + * Eugene Tsyrklevich 19-Feb-2004 + * + * Revision History: + * + * None. + */ + + +#include "pathproc.h" +#include "procname.h" +#include "policy.h" +#include "learn.h" +#include "log.h" + + +/* + * ResolveFilename() XXX rewrite + * + * Description: + * Get canonical name for a file by resolving symbolic links. + * + * Parameters: + * szFileName - filename to resolve. + * szResult - output buffer. + * szResultSize - size of an output buffer. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +ResolveFilename(IN PCHAR szFileName, OUT PCHAR szResult, IN USHORT szResultSize) +{ + CHAR *p, c; + OBJECT_ATTRIBUTES oa; + ANSI_STRING FileNameAnsi; + UNICODE_STRING FileNameUnicode; + HANDLE hLink; + NTSTATUS rc; + int NumberOfLinks = 0; + WCHAR buffer[chMAX_PATH]; + CHAR buffer2[chMAX_PATH]; + + +restart: + + *szResult = '\0'; + + if (szFileName[0] == '\\' && szFileName[1] == '\\') + szFileName++; + + /* move to the end of the object name */ + for (p = szFileName; *p != '\0'; p++) + ; + + /* process the object name from end to the beginning */ + while (p != szFileName) + { + /* find the last slash */ + if (*p != '\\' && *p != '\0') + { + p--; + continue; + } + + c = *p; + *p = '\0'; + + + RtlInitAnsiString(&FileNameAnsi, szFileName); + RtlAnsiStringToUnicodeString(&FileNameUnicode, &FileNameAnsi, TRUE); + + InitializeObjectAttributes(&oa, &FileNameUnicode, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL); + + rc = ZwOpenSymbolicLinkObject(&hLink, GENERIC_READ, &oa); + + if (NT_SUCCESS(rc)) + { + UNICODE_STRING target; + ANSI_STRING targeta; + + + target.Buffer = buffer; + target.MaximumLength = bMAX_PATH; + target.Length = 0; + + rc = ZwQuerySymbolicLinkObject(hLink, &target, NULL); + + ZwClose(hLink); + + + if (NT_SUCCESS(rc)) + { + targeta.Length = 0; + targeta.MaximumLength = szResultSize; + targeta.Buffer = szResult; + + RtlUnicodeStringToAnsiString(&targeta, &target, FALSE); + targeta.Buffer[targeta.Length] = '\0'; + +//XXX szResultSize -= targeta.Length; + + RtlFreeUnicodeString(&FileNameUnicode); + *p = c; + +//XXX can we have circular links? + if (NumberOfLinks++ < MAX_NUMBER_OF_LINKS) + { + strncat(szResult, p, szResultSize); + +// if (NumberOfLinks > 1) +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("ResolveFilename: NumberOfLinks=%d. Resolved %s to %s. Restarting.\n", NumberOfLinks, szFileName, szResult)); + + /* + * switch szFileName to a different buffer. we cannot reuse szFileName buffer + * since the resolved link might end up being longer than the original buffer + */ + + szFileName = (PCHAR) buffer2; + strcpy(szFileName, szResult); + + goto restart; + } + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("ResolveFilename: NumberOfLinks=%d. bailing out. %s\n", NumberOfLinks, szResult)); + + break; + } + } + + RtlFreeUnicodeString(&FileNameUnicode); + + *p-- = c; + } + + strncat(szResult, p, szResultSize); + + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("ResolveFilename: name=%s number of links=%d\n", szResult, NumberOfLinks)); + + + return TRUE; +} + + + +BOOLEAN +ResolveFilenameW(IN PUNICODE_STRING szFileName, OUT PCHAR szResult, IN USHORT szResultSize) +{ + WCHAR *p, c; + OBJECT_ATTRIBUTES oa; + ANSI_STRING FileNameAnsi; + UNICODE_STRING FileNameUnicode; + HANDLE hLink; + NTSTATUS rc; + int NumberOfLinks = 0; + WCHAR buffer[chMAX_PATH]; + USHORT OriginalLength; + UNICODE_STRING target; + ANSI_STRING targeta; + + + + ASSERT(szFileName); + ASSERT(szResult); + + OriginalLength = szFileName->Length; + + +restart: + + *szResult = '\0'; + + /* move to the end of the object name */ + p = (PWCHAR) ((PCHAR)szFileName->Buffer + szFileName->Length); + + /* process the object name from end to the beginning */ + while (p != szFileName->Buffer) + { + /* find the last slash */ +// p = wcsrchr(p, L'\\'); +/* + if (p == NULL) + { + p = szFileName->Buffer; + break; + } +*/ + if (*p != L'\\' && *p != L'\0') + { + p--; + continue; + } + + + c = *p; + *p = L'\0'; +// szFileName->Length = OriginalLength - (p - szFileName->Buffer); + + +// RtlInitAnsiString(&FileNameAnsi, szFileName); +// RtlAnsiStringToUnicodeString(&FileNameUnicode, &FileNameAnsi, TRUE); +// InitializeObjectAttributes(&oa, &FileNameUnicode, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL); + + InitializeObjectAttributes(&oa, szFileName, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL); + + rc = ZwOpenSymbolicLinkObject(&hLink, GENERIC_READ, &oa); + + if (! NT_SUCCESS(rc)) + { + *p-- = c; + continue; + } + + target.Buffer = buffer; + target.MaximumLength = bMAX_PATH; + target.Length = 0; + + rc = ZwQuerySymbolicLinkObject(hLink, &target, NULL); + + ZwClose(hLink); + + + if (! NT_SUCCESS(rc)) + { + *p-- = c; + continue; + } + + + *p = c; + + +//XXX can we have circular links? + if (NumberOfLinks++ < MAX_NUMBER_OF_LINKS) + { + wcscat(buffer, p); + RtlInitUnicodeString(szFileName, buffer); + + goto restart; + } + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("ResolveFilename: NumberOfLinks=%d. bailing out. %s\n", NumberOfLinks, szResult)); + + break; + } + + +// wcscat(szResult, p); + + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("ResolveFilename: name=%s number of links=%d\n", szResult, NumberOfLinks)); + + + return TRUE; +} + + + +/* + * GetPathFromOA() + * + * Description: + * Resolve an object handle to an object name. + * + * Parameters: + * ObjectAttributes - opaque structure describing an object handle. + * OutBuffer - output buffer where an object name will be saved to. + * OutBufferSize - size of an output buffer. + * ResolveLinks - do symbolic links need to be resolved? + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +#define FINISH_GetPathFromOA(msg) \ + do { \ + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, msg); \ + return FALSE; \ + } while(0) + +BOOLEAN +GetPathFromOA(IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PCHAR OutBuffer, IN USHORT OutBufferSize, IN BOOLEAN ResolveLinks) +{ + NTSTATUS rc; + PVOID Object = NULL; + ULONG len; + CHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + bMAX_PATH]; + POBJECT_NAME_INFORMATION pONI = (POBJECT_NAME_INFORMATION) Buffer; + BOOLEAN ret = FALSE; + UNICODE_STRING ObjectName; + + PUNICODE_STRING pusFilename = NULL; + ANSI_STRING name; + + + if (! ARGUMENT_PRESENT(ObjectAttributes) || OutBuffer == NULL) + + return(FALSE); + + + try + { + if (KeGetPreviousMode() != KernelMode) + + ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG)); + + + if (ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES)) + + FINISH_GetPathFromOA(("GetPathFromOA: Invalid ObjectAttributes length %d\n", ObjectAttributes->Length)); + + + if (! ARGUMENT_PRESENT(ObjectAttributes->ObjectName) ) + + return FALSE; + + + if (KeGetPreviousMode() != KernelMode) + { + ProbeForRead(ObjectAttributes->ObjectName, sizeof(UNICODE_STRING), sizeof(ULONG)); + + ObjectName = ProbeAndReadUnicodeString(ObjectAttributes->ObjectName); + } + else + { + ObjectName = *ObjectAttributes->ObjectName; + } + + + if (ObjectName.Length == 0) + + return FALSE; + + + if (((ObjectName.Length & (sizeof(WCHAR) - 1)) != 0) || + (ObjectName.Length > bMAX_PATH - sizeof(WCHAR)) ) + + FINISH_GetPathFromOA(("GetPathFromOA: invalid wchar string length = %d\n", ObjectName.Length)); + + + if (KeGetPreviousMode() != KernelMode) + + ProbeForRead(ObjectName.Buffer, ObjectName.Length, sizeof(WCHAR)); + } + + except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("GetPathFromOA(): caught an exception. status = 0x%x\n", status)); + + return FALSE; + } + + + pusFilename = &ObjectName; + + + /* + * is the filename referenced in relation to some directory? + * if so, append the filename to a specified directory name + */ + + if (ARGUMENT_PRESENT(ObjectAttributes->RootDirectory)) + { + if (! NT_SUCCESS( ObReferenceObjectByHandle(ObjectAttributes->RootDirectory, 0, 0, + KernelMode, &Object, NULL) )) + { + FINISH_GetPathFromOA(("GetPathFromOA(): ObReferenceObjectByHandle() failed. Object = %x\n", Object)); + } + + if (Object == NULL) + { + FINISH_GetPathFromOA(("GetPathFromOA(): Object = NULL\n")); + } + + + if (! NT_SUCCESS( ObQueryNameString(Object, pONI, bMAX_PATH, &len) )) + { + ObDereferenceObject(Object); + FINISH_GetPathFromOA(("GetPathFromOA(): ObQueryNameString() failed\n")); + } + + + ObDereferenceObject(Object); + Object = NULL; + + + /* extracted directory name */ + pusFilename = &pONI->Name; + + + /* is the directory name too long? */ + + if (pusFilename->Length >= bMAX_PATH - sizeof(WCHAR)) + FINISH_GetPathFromOA(("GetPathFromOA(): directory name is too long\n")); + + + /* + * pusFilename points to a buffer of MAX_PATH size, ObQueryNameString() sets MaximumLength to the length + * of the directory name, we need to reset this back to MAX_PATH to be able to append a filename + * (reusing the same buffer) + */ + pusFilename->MaximumLength = bMAX_PATH; + + + if (pusFilename->Buffer[ (pusFilename->Length / sizeof(WCHAR)) - 1 ] != L'\\') + { + pusFilename->Buffer[ pusFilename->Length / sizeof(WCHAR) ] = L'\\'; + pusFilename->Length += sizeof(WCHAR); + } + + if (RtlAppendUnicodeStringToString(pusFilename, ObjectAttributes->ObjectName) == STATUS_BUFFER_TOO_SMALL) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("GetPathFromOA: 1 %S\n", pusFilename->Buffer)); + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("GetPathFromOA: 2 %S\n", ObjectAttributes->ObjectName->Buffer)); + FINISH_GetPathFromOA(("GetPathFromOA(): RtlAppendUnicodeStringToString() = STATUS_BUFFER_TOO_SMALL\n")); + } + } + + + if (NT_SUCCESS(RtlUnicodeStringToAnsiString(&name, pusFilename, TRUE))) + { + if (ResolveLinks == TRUE) + { + ret = ResolveFilename(name.Buffer, OutBuffer, OutBufferSize); + } + else + { + if (name.Length >= OutBufferSize - 1) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("GetPathFromOA: Pathname too long %d\n", name.Length)); + + OutBuffer[0] = 0; + + ret = FALSE; + } + else + { + strcpy(OutBuffer, name.Buffer); + + ret = TRUE; + } + } + + RtlFreeAnsiString(&name); + } + + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("%d GetPathFromOA: %s (%S)\n", (ULONG) PsGetCurrentProcessId(), OutBuffer, pusFilename->Buffer)); + + + return ret; +} + + + + +BOOLEAN +GetPathFromOAW(IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PCHAR OutBuffer, IN USHORT OutBufferSize, IN BOOLEAN ResolveLinks) +{ + NTSTATUS rc; + PVOID Object = NULL; + ULONG len; + CHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + bMAX_PATH]; + POBJECT_NAME_INFORMATION pONI = (POBJECT_NAME_INFORMATION) Buffer; + BOOLEAN ret = FALSE; + UNICODE_STRING ObjectName; + + PUNICODE_STRING pusFilename = NULL; + ANSI_STRING name; + + + if (! ARGUMENT_PRESENT(ObjectAttributes) || OutBuffer == NULL) + + return(FALSE); + + + try + { + if (KeGetPreviousMode() != KernelMode) + + ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG)); + + + if (ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES)) + + FINISH_GetPathFromOA(("GetPathFromOA: Invalid ObjectAttributes length %d\n", ObjectAttributes->Length)); + + + if (! ARGUMENT_PRESENT(ObjectAttributes->ObjectName) ) + + return FALSE; + + + if (KeGetPreviousMode() != KernelMode) + { + ProbeForRead(ObjectAttributes->ObjectName, sizeof(UNICODE_STRING), sizeof(ULONG)); + + ObjectName = ProbeAndReadUnicodeString(ObjectAttributes->ObjectName); + } + else + { + ObjectName = *ObjectAttributes->ObjectName; + } + + + if (ObjectName.Length == 0) + + return FALSE; + + + if (((ObjectName.Length & (sizeof(WCHAR) - 1)) != 0) || + (ObjectName.Length > bMAX_PATH - sizeof(WCHAR)) ) + + FINISH_GetPathFromOA(("GetPathFromOA: invalid wchar string length = %d\n", ObjectName.Length)); + + + if (KeGetPreviousMode() != KernelMode) + + ProbeForRead(ObjectName.Buffer, ObjectName.Length, sizeof(WCHAR)); + } + + except(EXCEPTION_EXECUTE_HANDLER) + { + NTSTATUS status = GetExceptionCode(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("GetPathFromOA(): caught an exception. status = 0x%x\n", status)); + + return FALSE; + } + + + pusFilename = &ObjectName; + + + /* + * is the filename referenced in relation to some directory? + * if so, append the filename to a specified directory name + */ + + if (ARGUMENT_PRESENT(ObjectAttributes->RootDirectory)) + { + if (! NT_SUCCESS( ObReferenceObjectByHandle(ObjectAttributes->RootDirectory, 0, 0, + KernelMode, &Object, NULL) )) + { + ObDereferenceObject(Object); + FINISH_GetPathFromOA(("GetPathFromOA(): ObReferenceObjectByHandle() failed\n")); + } + + + if (Object == NULL) + { + ObDereferenceObject(Object); + FINISH_GetPathFromOA(("GetPathFromOA(): Object = NULL\n")); + } + + + if (! NT_SUCCESS( ObQueryNameString(Object, pONI, bMAX_PATH, &len) )) + { + ObDereferenceObject(Object); + FINISH_GetPathFromOA(("GetPathFromOA(): ObQueryNameString() failed\n")); + } + + + ObDereferenceObject(Object); + Object = NULL; + + + /* extracted directory name */ + pusFilename = &pONI->Name; + + + /* is the directory name too long? */ + + if (pusFilename->Length >= bMAX_PATH - sizeof(WCHAR)) + FINISH_GetPathFromOA(("GetPathFromOA(): directory name is too long\n")); + + + /* + * pusFilename points to a buffer of MAX_PATH size, ObQueryNameString() sets MaximumLength to the length + * of the directory name, we need to reset this back to MAX_PATH to be able to append a filename + * (reusing the same buffer) + */ + pusFilename->MaximumLength = bMAX_PATH; + + + pusFilename->Buffer[ pusFilename->Length / sizeof(WCHAR) ] = L'\\'; + pusFilename->Length += sizeof(WCHAR); + + + if (RtlAppendUnicodeStringToString(pusFilename, ObjectAttributes->ObjectName) == STATUS_BUFFER_TOO_SMALL) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("GetPathFromOA: 1 %S\n", pusFilename->Buffer)); + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("GetPathFromOA: 2 %S\n", ObjectAttributes->ObjectName->Buffer)); + FINISH_GetPathFromOA(("GetPathFromOA(): RtlAppendUnicodeStringToString() = STATUS_BUFFER_TOO_SMALL\n")); + } + } + + + if (ResolveLinks == TRUE) + { + ret = ResolveFilenameW(pusFilename, OutBuffer, OutBufferSize); + } + +//XXX + if (NT_SUCCESS(RtlUnicodeStringToAnsiString(&name, pusFilename, TRUE))) + { + if (ResolveLinks == TRUE) + { + ret = ResolveFilename(name.Buffer, OutBuffer, OutBufferSize); + } + else + { + if (name.Length >= OutBufferSize - 1) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("GetPathFromOA: Pathname too long %d\n", name.Length)); + + OutBuffer[0] = 0; + + ret = FALSE; + } + else + { + strcpy(OutBuffer, name.Buffer); + + ret = TRUE; + } + } + + RtlFreeAnsiString(&name); + } + + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("%d GetPathFromOA: %s (%S)\n", (ULONG) PsGetCurrentProcessId(), OutBuffer, pusFilename->Buffer)); + + + return ret; +} + + +/* + * ConvertLongFileNameToShort() + * + * Description: + * Converts long windows filenames to their DOS short equivalent filenames + * (i.e. c:\program files to c:\progra~1). + * + * Parameters: + * LongFileName - long filename buffer. + * ShortFileName - output buffer where a short filename is written to. + * ShortFileNameSize - size of an output buffer (in bytes). + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +#if 0 + +BOOLEAN +ConvertLongFileNameToShort(IN PCHAR LongFileName, OUT PCHAR ShortFileName, IN USHORT ShortFileNameSize) +{ + int LongFileNameIndex = 0, ShortFileNameIndex = 0, CurrentFileNameLength, TotalLength, ExtensionLength, NumberOfSpaces; + BOOLEAN ProcessingExtension = FALSE; + CHAR ch, Extension[3]; + + + if (LongFileName == NULL) + return FALSE; + + TotalLength = strlen(LongFileName); + + + /* if the filename does not start with X:\ then assume \device\blah\ format and skip over the first 2 slashes */ + if (LongFileName[0] == '\\') + { + int Slashes = 0; + + do + { + if ( (ch = ShortFileName[ShortFileNameIndex++] = LongFileName[LongFileNameIndex++]) == '\0') return TRUE; + if (ch == '\\') ++Slashes; + } while (Slashes != 3); + } + + for (NumberOfSpaces = ExtensionLength = CurrentFileNameLength = 0; ; LongFileNameIndex++) + { + /* if we finished traversing the entire directory path or reached a '\' then process the filename (append the extension if necessary) */ + if (LongFileNameIndex == TotalLength || LongFileName[LongFileNameIndex] == '\\') + { + /* + * if the filename is longer than 8 chars or extension is longer than 3 chars then we need + * to create a 6 char filename followed by a '~1' and the first 3 chars of the last extension + */ + + if (CurrentFileNameLength > 8 || ExtensionLength > 3 || NumberOfSpaces > 0) + { + CurrentFileNameLength -= NumberOfSpaces; + + if (CurrentFileNameLength > 7) + { + ShortFileName[ShortFileNameIndex - 2] = '~'; + ShortFileName[ShortFileNameIndex - 1] = '1'; + } + else if (CurrentFileNameLength == 7) + { + ShortFileName[ShortFileNameIndex - 1] = '~'; + ShortFileName[ShortFileNameIndex++] = '1'; + } + else + { + ShortFileName[ShortFileNameIndex++] = '~'; + ShortFileName[ShortFileNameIndex++] = '1'; + } + } + + if (ExtensionLength > 0) + { + ShortFileName[ShortFileNameIndex++] = '.'; + ShortFileName[ShortFileNameIndex++] = Extension[0]; + + if (ExtensionLength > 1) + { + ShortFileName[ShortFileNameIndex++] = Extension[1]; + + if (ExtensionLength > 2) + ShortFileName[ShortFileNameIndex++] = Extension[2]; + } + + ExtensionLength = 0; + ProcessingExtension = FALSE; + } + + /* if we are done traversing the entire path than we can bail */ + if (LongFileNameIndex == TotalLength) + break; + + ShortFileName[ShortFileNameIndex++] = '\\'; + NumberOfSpaces = CurrentFileNameLength = 0; + + continue; + } + + if (LongFileName[LongFileNameIndex] == '.') + { + ProcessingExtension = TRUE; + ExtensionLength = 0; + continue; + } + + if (ProcessingExtension == TRUE) + { + if (ExtensionLength++ < 3) + Extension[ExtensionLength - 1] = LongFileName[LongFileNameIndex]; + + continue; + } + + if (((CurrentFileNameLength++) - NumberOfSpaces) < 8) + { + if (LongFileName[LongFileNameIndex] != ' ') + ShortFileName[ShortFileNameIndex++] = LongFileName[LongFileNameIndex]; + else + ++NumberOfSpaces; + } + } + + + ShortFileName[ShortFileNameIndex++] = 0; + + + return TRUE; +} + +#endif + + + +/* + * GetNameFromHandle() + * + * Description: + * Resolve an object handle to an object name. + * + * Parameters: + * ObjectHandle - handle of an object whose name we are trying to obtain. + * OutBuffer - output buffer where an object name will be saved to. + * OutBufferSize - size of an output buffer (in bytes). + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +PWSTR +GetNameFromHandle(IN HANDLE ObjectHandle, OUT PWSTR OutBuffer, IN USHORT OutBufferSize) +{ + PVOID Object = NULL; + NTSTATUS rc; + POBJECT_NAME_INFORMATION pONI = (POBJECT_NAME_INFORMATION) OutBuffer; + ULONG len; + + + rc = ObReferenceObjectByHandle(ObjectHandle, GENERIC_READ, NULL, KernelMode, &Object, NULL); + if (! NT_SUCCESS(rc)) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("%d GetNameFromHandle: ObReferenceObjectByHandle failed\n", (ULONG) PsGetCurrentProcessId())); + return NULL; + } + + + rc = ObQueryNameString(Object, pONI, OutBufferSize - sizeof(OBJECT_NAME_INFORMATION)*sizeof(WCHAR), &len); + if (! NT_SUCCESS(rc)) + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_VERBOSE, ("%d GetNameFromHandle: ObQueryNameString failed\n", (ULONG) PsGetCurrentProcessId())); + return NULL; + } + + +// _snprintf(OutBuffer, OutBufferSize, "%S", pONI->Name.Buffer); +// OutBuffer[OutBufferSize - 1] = 0; + +// LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("%S (%s)\n", pONI->Name.Buffer, OutBuffer)); + + + ObDereferenceObject(Object); + + + return pONI->Name.Buffer; +// return TRUE; +} + + + +/* + * StripFileMacros() + * + * Description: + * Strip file names of %SystemRoot% and %SystemDrive% macros as well as any specifications. + * + * Parameters: + * Path - ASCII file path to parse. + * Buffer - pointer to an Object where the final result will be saved. + * BufferSize - size of the output Buffer. + * + * Returns: + * Pointer to a stripped ASCII path. + */ + +PCHAR +StripFileMacros(IN PCHAR Path, OUT PCHAR Buffer, IN USHORT BufferSize) +{ + if (_strnicmp(Path, "%systemdrive%:", 14) == 0) + { + return Path + 14; + } + + + if (_strnicmp(Path, "%systemroot%\\", 13) == 0) + { + if (_snprintf(Buffer, MAX_PATH, "%s%s", SystemRootUnresolved, Path + 12) < 0) + return NULL; + + Path = Buffer; + } + + + if (Path[1] == ':' && Path[2] == '\\' && (isalpha(Path[0]) || Path[0] == '?' || Path[0] == '*')) + { + Path += 2; + } + + + return Path; +} + + + +/* + * FixupFilename() + * + * Description: + * Get canonical name for a file (without the drive specification, i.e. \windows\blah.exe) + * + * Parameters: + * szFileName - filename to resolve. + * szResult - output buffer. + * szResultSize - size of an output buffer. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +BOOLEAN +FixupFilename(IN PCHAR szFileName, OUT PCHAR szResult, IN USHORT szResultSize) +{ + /* skip over \??\ */ + if (_strnicmp(szFileName, "\\??\\", 4) == 0) + { + szFileName += 4; + } + + + /* replace "\SystemRoot" references with the actual system root directory */ + if (_strnicmp(szFileName, "\\SystemRoot\\", 12) == 0) + { + _snprintf(szResult, szResultSize, "%s\\%s", SystemRootDirectory, szFileName + 12); + szResult[ szResultSize - 1 ] = 0; + + return TRUE; + } + + + /* skip over X: drive specifications */ + if (isalpha(szFileName[0]) && szFileName[1] == ':' && szFileName[2] == '\\') + { + szFileName += 2; + } + + + strncpy(szResult, szFileName, szResultSize); + szResult[ szResultSize - 1 ] = 0; + + + return TRUE; +} + + + +/* + * AreMalformedExtensionsAllowed() + * + * Description: + * Check whether the current process is allowed to run binaries with malformed file extensions. + * + * Parameters: + * None. + * + * Returns: + * FALSE if binaries with malformed extensions are not allowed to run. TRUE otherwise. + */ + +BOOLEAN +AreMalformedExtensionsAllowed() +{ + PIMAGE_PID_ENTRY CurrentProcess; + BOOLEAN MalformedExtensionsAllowed = FALSE; + + + /* check the global policy first */ + if (! IS_EXTENSION_PROTECTION_ON(gSecPolicy)) + return TRUE; + + + /* now check the process specific policy */ + CurrentProcess = FindImagePidEntry(CURRENT_PROCESS_PID, 0); + + if (CurrentProcess != NULL) + { + MalformedExtensionsAllowed = ! IS_EXTENSION_PROTECTION_ON(CurrentProcess->SecPolicy); + } + else + { + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("%d AreMalformedExtensionsAllowed: CurrentProcess = NULL!\n", (ULONG) PsGetCurrentProcessId())); + } + + + return MalformedExtensionsAllowed; +} + + + +/* + * VerifyExecutableName() + * + * Description: + * Make sure the executed binary does not have a funny filename. + * Look out for non-standard extensions (.exe, etc) and double + * extensions that are commonly "ab"used by malware. + * + * Parameters: + * szFileName - filename to verify. + * + * Returns: + * TRUE to indicate success, FALSE if failed. + */ + +#define CHECK_LEARNING_MODE() \ + if (LearningMode == TRUE) \ + { \ + TURN_EXTENSION_PROTECTION_OFF(NewPolicy); \ + return TRUE; \ + } + +BOOLEAN +VerifyExecutableName(IN PCHAR szFileName) +{ + SHORT i, len; + BOOLEAN FirstExtension = TRUE; + + + if (LearningMode == FALSE && AreMalformedExtensionsAllowed() == TRUE) + { + /* no need to check anything further, malformed extensions are allowed */ + return TRUE; + } + + + if ((len = (SHORT) strlen(szFileName)) == 0) + return TRUE; + + + for (i = len - 1; i >= 0; i--) + { + /* bail out once we reach the end of the filename */ + if (szFileName[i] == '\\') + break; + + if (szFileName[i] == '.') + { + if (FirstExtension == FALSE) + { + CHECK_LEARNING_MODE(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("VerifyExecutableName: Executing a binary with more than one extension '%s'\n", szFileName)); + + LogAlert(ALERT_SS_PROCESS, OP_PROC_EXECUTE, ALERT_RULE_PROCESS_EXEC_2EXTS, ACTION_LOG, ALERT_PRIORITY_HIGH, NULL, 0, szFileName); + + return FALSE; + } + + if (len - i != 4) + { + CHECK_LEARNING_MODE(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("VerifyExecutableName: Executing a binary with an unknown extension '%s'\n", szFileName)); + + LogAlert(ALERT_SS_PROCESS, OP_PROC_EXECUTE, ALERT_RULE_PROCESS_EXEC_UNKNOWN, ACTION_LOG, ALERT_PRIORITY_HIGH, NULL, 0, szFileName); + + return FALSE; + } + else + { + if (_stricmp(szFileName + i + 1, "exe") != 0 && + _stricmp(szFileName + i + 1, "com") != 0 && + _stricmp(szFileName + i + 1, "bat") != 0 && + _stricmp(szFileName + i + 1, "scr") != 0 && + _stricmp(szFileName + i + 1, "dir") != 0 && + _stricmp(szFileName + i + 1, "tmp") != 0 && + _stricmp(szFileName + i + 1, "_mp") != 0) + { + CHECK_LEARNING_MODE(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("VerifyExecutableName: Executing a binary with an unknown extension '%s'\n", szFileName)); + + LogAlert(ALERT_SS_PROCESS, OP_PROC_EXECUTE, ALERT_RULE_PROCESS_EXEC_UNKNOWN, ACTION_LOG, ALERT_PRIORITY_HIGH, NULL, 0, szFileName); + + return FALSE; + } + } + + FirstExtension = FALSE; + } + } + + + if (FirstExtension == TRUE) + { + CHECK_LEARNING_MODE(); + + LOG(LOG_SS_PATHPROC, LOG_PRIORITY_DEBUG, ("VerifyExecutableName: Executing binary without an extension '%s'\n", szFileName)); + + LogAlert(ALERT_SS_PROCESS, OP_PROC_EXECUTE, ALERT_RULE_PROCESS_EXEC_NOEXT, ACTION_LOG, ALERT_PRIORITY_HIGH, NULL, 0, szFileName); + + return FALSE; + } + + + return TRUE; +} -- cgit v1.3