#include "precomp.h" typedef struct _TD_CALLBACK_PARAMETERS { ACCESS_MASK AccessBitsToClear; ACCESS_MASK AccessBitsToSet; }TD_CALLBACK_PARAMETERS, * PTD_CALLBACK_PARAMETERS; typedef struct _TD_CALLBACK_REGISTRATION { PVOID RegistrationHandle; PVOID TargetProcess; HANDLE TargetProcessId; TD_CALLBACK_PARAMETERS ProcessParams; TD_CALLBACK_PARAMETERS ThreadParams; ULONG RegistrationId; // Index in the global TdCallbacks array. }TD_CALLBACK_REGISTRATION, * PTD_CALLBACK_REGISTRATION; typedef struct _TD_CALL_CONTEXT { PTD_CALLBACK_REGISTRATION CallbackRegistration; OB_OPERATION Operation; PVOID Object; POBJECT_TYPE ObjectType; } TD_CALL_CONTEXT, * PTD_CALL_CONTEXT; static OB_CALLBACK_REGISTRATION s_obRegistration = { 0 }; static OB_OPERATION_REGISTRATION s_operationRegistrations[2] = { { 0 }, { 0 } }; static PVOID s_registrationHandle = NULL; static TD_CALLBACK_REGISTRATION CallbackRegistration = { 0 }; static BOOLEAN s_callbacksInstalled = FALSE; #define TD_CALL_CONTEXT_TAG '1bCO' #define CB_PROCESS_TERMINATE 0x0001 #define CB_THREAD_TERMINATE 0x0001 #define PROCESS_TERMINATE (0x0001) #define PROCESS_CREATE_THREAD (0x0002) #define PROCESS_SET_SESSIONID (0x0004) #define PROCESS_VM_OPERATION (0x0008) #define PROCESS_VM_READ (0x0010) #define PROCESS_VM_WRITE (0x0020) #define PROCESS_DUP_HANDLE (0x0040) #define PROCESS_CREATE_PROCESS (0x0080) #define PROCESS_SET_QUOTA (0x0100) #define PROCESS_SET_INFORMATION (0x0200) #define PROCESS_QUERY_INFORMATION (0x0400) #define PROCESS_SUSPEND_RESUME (0x0800) #define PROCESS_QUERY_LIMITED_INFORMATION (0x1000) #define PROCESS_SET_LIMITED_INFORMATION (0x2000) #define THREAD_TERMINATE (0x0001) #define THREAD_SUSPEND_RESUME (0x0002) #define THREAD_GET_CONTEXT (0x0008) #define THREAD_SET_CONTEXT (0x0010) #define THREAD_QUERY_INFORMATION (0x0040) #define THREAD_SET_INFORMATION (0x0020) #define THREAD_SET_THREAD_TOKEN (0x0080) #define THREAD_IMPERSONATE (0x0100) #define THREAD_DIRECT_IMPERSONATION (0x0200) #define THREAD_SET_LIMITED_INFORMATION (0x0400) // winnt #define THREAD_QUERY_LIMITED_INFORMATION (0x0800) // winnt #define THREAD_RESUME (0x1000) // winnt #define TD_ASSERT(_exp) \ ((!(_exp)) ? \ (__annotation(L"Debug", L"AssertFail", L#_exp), \ DbgRaiseAssertionFailure(), FALSE) : \ TRUE) BOOLEAN IsObExceptProcess(LPWSTR name) { static wchar_t* s_obExceptionprocess[] = { L"MsMpEng.exe", L"System", L"csrss.exe", L"lsm.exe", L"spoolsv.exe", L"lsass.exe", L"services.exe", L"winlogon.exe", L"smss.exe", L"alg.exe", L"conhost.exe", L"svchost.exe", NULL }; LPWSTR* ep = s_obExceptionprocess; while (*ep) { if (_wcsnicmp(name, *ep, wcslen(name)) == 0) return TRUE; ++ep; } return FALSE; } OB_PREOP_CALLBACK_STATUS PreOperationCallback( IN PVOID RegistrationContext, IN OUT POB_PRE_OPERATION_INFORMATION PreInfo ) { UNREFERENCED_PARAMETER(RegistrationContext); OB_PREOP_CALLBACK_STATUS state = OB_PREOP_SUCCESS; PACCESS_MASK DesiredAccess = NULL; ACCESS_MASK OriginalDesiredAccess = 0; HANDLE pid = 0; HANDLE curPid = PsGetCurrentProcessId(); wchar_t curProcessName[260] = { 0, }; wchar_t curProcessPath[260] = { 0, }; wchar_t processName[260] = { 0, }; wchar_t processPath[260] = { 0, }; HANDLE ProcessIdOfTargetThread; LPCWSTR ObjectTypeName = NULL; LPCWSTR OperationName = NULL; ACCESS_MASK AccessBitsToClear = 0; ACCESS_MASK InitialDesiredAccess = 0; BOOLEAN isProtected = FALSE; //if (PreInfo->Operation != OB_OPERATION_HANDLE_CREATE) // return state; if (!g_bs1Flt.IsProcessProtect) return state; if (KeGetCurrentIrql() > PASSIVE_LEVEL) return state; if (PreInfo->ObjectType == *PsProcessType) { if (PreInfo->Object == PsGetCurrentProcess()) return OB_PREOP_SUCCESS; ObjectTypeName = L"PsProcessType"; AccessBitsToClear = PROCESS_TERMINATE | PROCESS_SUSPEND_RESUME; pid = PsGetProcessId(PreInfo->Object); } else if (PreInfo->ObjectType == *PsThreadType) { ProcessIdOfTargetThread = PsGetThreadProcessId((PETHREAD)PreInfo->Object); if (ProcessIdOfTargetThread == curPid) return OB_PREOP_SUCCESS; ObjectTypeName = L"PsThreadType"; AccessBitsToClear = THREAD_TERMINATE | THREAD_SUSPEND_RESUME; pid = ProcessIdOfTargetThread; } else { KLogEx(DEBUG_TRACE_ERROR, "unexpected object type\n"); return OB_PREOP_SUCCESS; } switch (PreInfo->Operation) { case OB_OPERATION_HANDLE_CREATE: DesiredAccess = &PreInfo->Parameters->CreateHandleInformation.DesiredAccess; OriginalDesiredAccess = PreInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess; if (!FlagOn(*DesiredAccess, (PROCESS_TERMINATE | PROCESS_SUSPEND_RESUME))) { return OB_PREOP_SUCCESS; } OperationName = L"OB_OPERATION_HANDLE_CREATE"; break; case OB_OPERATION_HANDLE_DUPLICATE: DesiredAccess = &PreInfo->Parameters->DuplicateHandleInformation.DesiredAccess; OriginalDesiredAccess = PreInfo->Parameters->DuplicateHandleInformation.OriginalDesiredAccess; if (!FlagOn(*DesiredAccess, (THREAD_TERMINATE | THREAD_SUSPEND_RESUME))) { return OB_PREOP_SUCCESS; } OperationName = L"OB_OPERATION_HANDLE_DUPLICATE"; break; default: KLogEx(DEBUG_TRACE_ERROR, "unexpected object type\n"); return state; } if (PreInfo->KernelHandle == 1) { return OB_PREOP_SUCCESS; } if (curPid == pid) return OB_PREOP_SUCCESS; InitialDesiredAccess = *DesiredAccess; state = UGetProcessFullPathFromPid(curPid, curProcessPath, sizeof(curProcessPath), curProcessName, sizeof(curProcessName)); if (!NT_SUCCESS(state)) { KLogEx(DEBUG_TRACE_ERROR, "UGetProcessFullPathFromPid:: cur stats %x\n", state); return OB_PREOP_SUCCESS; } if(IsObExceptProcess(curProcessName)) return OB_PREOP_SUCCESS; state = UGetProcessFullPathFromPid(pid, processPath, sizeof(processPath), processName, sizeof(processName)); if (!NT_SUCCESS(state)) { KLogEx(DEBUG_TRACE_ERROR, "UGetProcessFullPathFromPid:: tag stats %x\n", state); return OB_PREOP_SUCCESS; } KLogEx(DEBUG_TRACE_ERROR, "curProcessName(%S), processName(%S)\n", curProcessName, processName); isProtected = TRUE; if (_wcsicmp(processName, L"bs1fltputool.exe") == 0) { //PreInfo->Parameters->CreateHandleInformation.DesiredAccess = 0; } else if (PgGetProtectPidState(HandleToULong(pid)) == PG_PID_PROTECT) { //KLogEx(DEBUG_TRACE_INFO, "PgGetProtectPidState(%S)\n", processName); } else if (IsProcessProtectList(PG_PID_PROTECT, processName, (ULONG)wcslen(processName) * (ULONG)sizeof(WCHAR)) == PG_PID_PROTECT) { //KLogEx(DEBUG_TRACE_INFO, "IsProcessProtectList(%S)\n", pocessName); } else { isProtected = FALSE; } if(isProtected) { *DesiredAccess &= ~AccessBitsToClear; SetLog(NULL,NULL,LOG_PROCESS_PROTECT,HandleToULong(pid),HandleToULong(curPid),InitialDesiredAccess,curProcessPath,processPath); KLogEx(DEBUG_TRACE_ERROR, "[PROTECT] tried to kill Target\n"); } KLogEx(DEBUG_TRACE_ERROR, " Client Id: %p:%p:%p\n" " Object: %p\n" " Type: %ls\n" " Operation: %ls (KernelHandle=%d)\n" " OriginalDesiredAccess: 0x%x\n" " DesiredAccess (in): 0x%x\n" " DesiredAccess (out): 0x%x\n", pid, curPid, PsGetCurrentThreadId(), PreInfo->Object, ObjectTypeName, OperationName, PreInfo->KernelHandle, OriginalDesiredAccess, InitialDesiredAccess, *DesiredAccess ); /* if (PreInfo->Operation == OB_OPERATION_HANDLE_CREATE) { DesiredAccess = &PreInfo->Parameters->CreateHandleInformation.DesiredAccess; OriginalDesiredAccess = PreInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess; if (0x0001 | 0x800 & PreInfo->Parameters->CreateHandleInformation.DesiredAccess) // TERMINATE_PROCESS { if ((PreInfo->ObjectType == *PsProcessType) || (PreInfo->ObjectType == *PsThreadType)) { if (PreInfo->ObjectType == *PsProcessType) { pid = PsGetProcessId(PreInfo->Object); } if (PreInfo->ObjectType == *PsThreadType) { pid = PsGetThreadProcessId((PETHREAD)PreInfo->Object); } if (curPid == pid) return state; UGetProcessNameFormPid(curProcessName, curPid); if (_stricmp(curProcessName, "MsMpEng.exe") == 0 || _stricmp(curProcessName, "System") == 0 || _stricmp(curProcessName, "csrss.exe") == 0 || _stricmp(curProcessName, "lsm.exe") == 0 || _stricmp(curProcessName, "spoolsv.exe") == 0 || _stricmp(curProcessName, "lsass.exe") == 0 || _stricmp(curProcessName, "services.exe") == 0 || _stricmp(curProcessName, "winlogon.exe") == 0 || _stricmp(curProcessName, "smss.exe") == 0 || _stricmp(curProcessName, "alg.exe") == 0 || _stricmp(curProcessName, "conhost.exe") == 0) { //KLogEx(DEBUG_TRACE_INFO, "OB_PREOP_SUCCESS\n"); return OB_PREOP_SUCCESS; } UGetProcessNameFormPid(processName, pid); KLogEx(DEBUG_TRACE_INFO, "curProcessName(%s)(%s)\n", curProcessName, processName); if (_stricmp(processName, "bs1flt_tool.ex") == 0) { PreInfo->Parameters->CreateHandleInformation.DesiredAccess = 0; KLogEx(DEBUG_TRACE_INFO, "CREATE PROTECT curProcessName(%s), processName(%s), (%x)\n", curProcessName, processName, (ULONG)*DesiredAccess, OriginalDesiredAccess); } } } } else if (PreInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE) { DesiredAccess = &PreInfo->Parameters->DuplicateHandleInformation.DesiredAccess; OriginalDesiredAccess = PreInfo->Parameters->DuplicateHandleInformation.OriginalDesiredAccess; if ((PreInfo->ObjectType == *PsProcessType) || (PreInfo->ObjectType == *PsThreadType)) { if (PreInfo->ObjectType == *PsProcessType) { pid = PsGetProcessId(PreInfo->Object); } if (PreInfo->ObjectType == *PsThreadType) { pid = PsGetThreadProcessId((PETHREAD)PreInfo->Object); } if (curPid == pid) return state; UGetProcessNameFormPid(curProcessName, curPid); UGetProcessNameFormPid(processName, pid); KLogEx(DEBUG_TRACE_INFO, "DUPLICATE PROTECT curProcessName(%s), processName(%s), (%x)\n", curProcessName, processName, (ULONG)*DesiredAccess, OriginalDesiredAccess); } } */ return state; } void TdCheckAndFreeCallContext( IN OUT POB_POST_OPERATION_INFORMATION PostInfo, IN PTD_CALLBACK_REGISTRATION _CallbackRegistration ) { PTD_CALL_CONTEXT CallContext = (PTD_CALL_CONTEXT)PostInfo->CallContext; if (CallContext != NULL) { TD_ASSERT(CallContext->CallbackRegistration == _CallbackRegistration); TD_ASSERT(CallContext->Operation == PostInfo->Operation); TD_ASSERT(CallContext->Object == PostInfo->Object); TD_ASSERT(CallContext->ObjectType == PostInfo->ObjectType); ExFreePoolWithTag(CallContext, TD_CALL_CONTEXT_TAG); } } VOID PostOperationCallback( IN PVOID RegistrationContext, IN POB_POST_OPERATION_INFORMATION PostInfo ) { PTD_CALLBACK_REGISTRATION CallbackRegistration_ = (PTD_CALLBACK_REGISTRATION)RegistrationContext; TdCheckAndFreeCallContext(PostInfo, CallbackRegistration_); if (PostInfo->ObjectType == *PsProcessType) { // // Ignore requests for processes other than our target process. // if (CallbackRegistration_->TargetProcess != NULL && CallbackRegistration_->TargetProcess != PostInfo->Object ) { return; } // // Also ignore requests that are trying to open/duplicate the current // process. // if (PostInfo->Object == PsGetCurrentProcess()) { return; } } else if (PostInfo->ObjectType == *PsThreadType) { HANDLE ProcessIdOfTargetThread = PsGetThreadProcessId((PETHREAD)PostInfo->Object); // // Ignore requests for threads belonging to processes other than our // target process. // if (CallbackRegistration_->TargetProcess != NULL && CallbackRegistration_->TargetProcessId != ProcessIdOfTargetThread ) { return; } // // Also ignore requests for threads belonging to the current processes. // if (ProcessIdOfTargetThread == PsGetCurrentProcessId()) { return; } } else { TD_ASSERT(FALSE); } } NTSTATUS InstallProcessProtect() { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING altitude = { 0 }; if(s_callbacksInstalled) { KLogEx(DEBUG_TRACE_INFO, "OB callbacks already installed\n"); return STATUS_SUCCESS; } s_operationRegistrations[0].ObjectType = PsProcessType; s_operationRegistrations[0].Operations |= OB_OPERATION_HANDLE_CREATE; s_operationRegistrations[0].Operations |= OB_OPERATION_HANDLE_DUPLICATE; s_operationRegistrations[0].PreOperation = PreOperationCallback; s_operationRegistrations[0].PostOperation = PostOperationCallback; s_operationRegistrations[1].ObjectType = PsThreadType; s_operationRegistrations[1].Operations |= OB_OPERATION_HANDLE_CREATE; s_operationRegistrations[1].Operations |= OB_OPERATION_HANDLE_DUPLICATE; s_operationRegistrations[1].PreOperation = PreOperationCallback; s_operationRegistrations[1].PostOperation = PostOperationCallback; RtlInitUnicodeString(&altitude, OBJECT_ALTITUDE); s_obRegistration.Version = OB_FLT_REGISTRATION_VERSION; s_obRegistration.OperationRegistrationCount = 2; s_obRegistration.Altitude = altitude; s_obRegistration.RegistrationContext = &CallbackRegistration; s_obRegistration.OperationRegistration = s_operationRegistrations; status = ObRegisterCallbacks(&s_obRegistration, &s_registrationHandle); if (!NT_SUCCESS(status)) { KLogEx(DEBUG_TRACE_INFO, "ObRegisterCallbacks: installing OB callbacks failed status (%x)\n", status); return status; } g_bs1Flt.IsProcessProtect = TRUE; s_callbacksInstalled = TRUE; KLogEx(DEBUG_TRACE_INFO, "Sucess\n"); return STATUS_SUCCESS; } VOID UnInstallProcessProtect() { if (!s_callbacksInstalled) return; if (!s_registrationHandle) return; g_bs1Flt.IsProcessProtect = FALSE; ObUnRegisterCallbacks(s_registrationHandle); s_registrationHandle = NULL; s_callbacksInstalled = FALSE; KLogEx(DEBUG_TRACE_ERROR, "Sucess\n"); }