NTSTATUS HookNtTerminateProcess( IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus ) { NTSTATUS rtStatus = STATUS_SUCCESS; PEPROCESS pEProcess = NULL; PCHAR pStrProcName = NULL; ULONG uPID = 0; // 通过进程句柄来获得该进程所对应的 FileObject 对象 rtStatus = ObReferenceObjectByHandle(ProcessHandle, FILE_READ_DATA, NULL, KernelMode, &pEProcess, NULL); if(!NT_SUCCESS(rtStatus)) { return rtStatus; } // pOldNtTerminateProcess = (PNtTerminateProcess)GetBackupSysServiceAddr((ULONG)ZwTerminateProcess); uPID = (ULONG)PsGetProcessId(pEProcess); pStrProcName = (PCHAR)PsGetProcessImageFileName(pEProcess); DbgPrint("HookNtTerminateProcess:%s\n",pStrProcName); PsGetProcessId(PsGetCurrentProcess()); // rtStatus = pOldNtTerminateProcess(ProcessHandle, ExitStatus); return rtStatus; }
BOOL FASTCALL UserUnregisterUserApiHook(VOID) { PTHREADINFO pti; pti = PsGetCurrentThreadWin32Thread(); /* Fail if the api hook is not registered */ if(!(gpsi->dwSRVIFlags & SRVINFO_APIHOOK)) { return FALSE; } /* Only the process that registered the api hook can uregister it */ if(ppiUahServer != PsGetCurrentProcessWin32Process()) { return FALSE; } ERR("UserUnregisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti->ppi->peProcess)); /* Unregister the api hook */ gpsi->dwSRVIFlags &= ~SRVINFO_APIHOOK; ppiUahServer = NULL; ReleaseCapturedUnicodeString(&strUahModule, UserMode); ReleaseCapturedUnicodeString(&strUahInitFunc, UserMode); /* Notify all applications that the api hook module must be unloaded */ return IntHookModuleUnloaded(pti->rpdesk, WH_APIHOOK, 0); }
OB_PREOP_CALLBACK_STATUS PreOpenCallback(IN PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) { UNREFERENCED_PARAMETER(RegistrationContext); if (OperationInformation->ObjectType != *PsProcessType) return OB_PREOP_SUCCESS; HANDLE CurrentProcessId = PsGetProcessId((PEPROCESS)OperationInformation->Object); if (!IsProcessProtected(CurrentProcessId)) return OB_PREOP_SUCCESS; ReduceRights(&OperationInformation->Parameters->CreateHandleInformation.DesiredAccess); ReduceRights(&OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess); return OB_PREOP_SUCCESS; }
/* Calls ClientLoadLibrary in user32 in order to load or unload a module */ BOOL IntLoadHookModule(int iHookID, HHOOK hHook, BOOL Unload) { PPROCESSINFO ppi; BOOL bResult; ppi = PsGetCurrentProcessWin32Process(); TRACE("IntLoadHookModule. Client PID: %p\n", PsGetProcessId(ppi->peProcess)); /* Check if this is the api hook */ if(iHookID == WH_APIHOOK) { if(!Unload && !(ppi->W32PF_flags & W32PF_APIHOOKLOADED)) { /* A callback in user mode can trigger UserLoadApiHook to be called and as a result IntLoadHookModule will be called recursively. To solve this we set the flag that means that the appliaction has loaded the api hook before the callback and in case of error we remove it */ ppi->W32PF_flags |= W32PF_APIHOOKLOADED; /* Call ClientLoadLibrary in user32 */ bResult = co_IntClientLoadLibrary(&strUahModule, &strUahInitFunc, Unload, TRUE); TRACE("co_IntClientLoadLibrary returned %d\n", bResult ); if (!bResult) { /* Remove the flag we set before */ ppi->W32PF_flags &= ~W32PF_APIHOOKLOADED; } return bResult; } else if(Unload && (ppi->W32PF_flags & W32PF_APIHOOKLOADED)) { /* Call ClientLoadLibrary in user32 */ bResult = co_IntClientLoadLibrary(NULL, NULL, Unload, TRUE); if (bResult) { ppi->W32PF_flags &= ~W32PF_APIHOOKLOADED; } return bResult; } return TRUE; } STUB; return FALSE; }
NTSTATUS MyNtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL, IN NTSTATUS ExitStatus ) { ULONG uPID; NTSTATUS rtStatus; PCHAR pStrProcName; PEPROCESS pEProcess; ANSI_STRING strProcName; NTTERMINATEPROCESS OldNtTerminateProcess = (NTTERMINATEPROCESS)OldServiceAddressTable[SERVICE_ID(ZwTerminateProcess)]; DbgPrint("[MyNtTerminateProcess] called"); rtStatus = ObReferenceObjectByHandle(ProcessHandle, FILE_READ_DATA, NULL, KernelMode, (PVOID*)&pEProcess, NULL); if (!NT_SUCCESS(rtStatus)) { return rtStatus; } uPID = (ULONG)PsGetProcessId(pEProcess); pStrProcName = _strupr((TCHAR *)PsGetProcessImageFileName(pEProcess));//使用微软未公开的PsGetProcessImageFileName函数获取进程名 RtlInitAnsiString(&strProcName, pStrProcName); DbgPrint(("[MyNtTerminateProcess] %u\n",uPID)); if (uPID<1000) { if (uPID != (ULONG)PsGetProcessId(PsGetCurrentProcess())) { return STATUS_ACCESS_DENIED; } } // 对于非保护的进程可以直接调用原来 SSDT 中的 NtTerminateProcess 来结束进程 rtStatus = OldNtTerminateProcess(ProcessHandle, ExitStatus); return rtStatus; }
NTSTATUS kkll_m_process_token(SIZE_T szBufferIn, PVOID bufferIn, PKIWI_BUFFER outBuffer) { NTSTATUS status = STATUS_SUCCESS; PMIMIDRV_PROCESS_TOKEN_FROM_TO pTokenFromTo = (PMIMIDRV_PROCESS_TOKEN_FROM_TO) bufferIn; ULONG fromProcessId, toProcessId; HANDLE hFromProcess, hFromProcessToken; PEPROCESS pFromProcess = PsInitialSystemProcess, pToProcess = NULL; if(pTokenFromTo && (szBufferIn == sizeof(MIMIDRV_PROCESS_TOKEN_FROM_TO))) { if(pTokenFromTo->fromProcessId) status = PsLookupProcessByProcessId((HANDLE) pTokenFromTo->fromProcessId, &pFromProcess); if(NT_SUCCESS(status) && pTokenFromTo->toProcessId) status = PsLookupProcessByProcessId((HANDLE) pTokenFromTo->toProcessId, &pToProcess); } if(NT_SUCCESS(status)) { status = ObOpenObjectByPointer(pFromProcess, OBJ_KERNEL_HANDLE, NULL, 0, *PsProcessType, KernelMode, &hFromProcess); if(NT_SUCCESS(status)) { status = ZwOpenProcessTokenEx(hFromProcess, 0, OBJ_KERNEL_HANDLE, &hFromProcessToken); if(NT_SUCCESS(status)) { status = kprintf(outBuffer, L"Token from %u/%-14S\n", PsGetProcessId(pFromProcess), PsGetProcessImageFileName(pFromProcess)); if(NT_SUCCESS(status)) { if(pToProcess) status = kkll_m_process_token_toProcess(szBufferIn, bufferIn, outBuffer, hFromProcessToken, pToProcess); else status = kkll_m_process_enum(szBufferIn, bufferIn, outBuffer, kkll_m_process_systoken_callback, hFromProcessToken); } ZwClose(hFromProcessToken); } ZwClose(hFromProcess); } } if(pToProcess) ObDereferenceObject(pToProcess); if(pFromProcess && (pFromProcess != PsInitialSystemProcess)) ObDereferenceObject(pFromProcess); return status; }
OB_PREOP_CALLBACK_STATUS ObjectPreCallbackFilter( __in PVOID RegistrationContext, __in POB_PRE_OPERATION_INFORMATION OperationInformation ) { OB_PREOP_CALLBACK_STATUS obReturn = OB_PREOP_SUCCESS; if( OperationInformation->Operation != OB_OPERATION_HANDLE_CREATE ) return obReturn; if( isProtectProcess((UINT)PsGetProcessId( (PEPROCESS)OperationInformation->Object)) && !isPassProcess() ) //if( PsGetProcessId( (PEPROCESS)OperationInformation->Object) == (HANDLE)PROTECTID ) { PEPROCESS p = PsGetCurrentProcess(); kdP(("operation process is %s PID is: %d\n",(char*)p+g_processNameOffset, (UINT32)PsGetCurrentProcessId() )); OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0; } return obReturn; }
/* IntHookModuleUnloaded: Sends a internal message to all threads of the requested desktop and notifies them that a global hook was destroyed and an injected module must be unloaded. As a result, IntLoadHookModule will be called for all the threads that will receive the special purpose internal message. */ BOOL IntHookModuleUnloaded(PDESKTOP pdesk, int iHookID, HHOOK hHook) { PTHREADINFO ptiCurrent; PLIST_ENTRY ListEntry; PPROCESSINFO ppiCsr; ERR("IntHookModuleUnloaded: iHookID=%d\n", iHookID); ppiCsr = PsGetProcessWin32Process(gpepCSRSS); ListEntry = pdesk->PtiList.Flink; while(ListEntry != &pdesk->PtiList) { ptiCurrent = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink); /* FIXME: Do some more security checks here */ /* FIXME: The first check is a reactos specific hack for system threads */ if(!PsIsSystemProcess(ptiCurrent->ppi->peProcess) && ptiCurrent->ppi != ppiCsr) { if(ptiCurrent->ppi->W32PF_flags & W32PF_APIHOOKLOADED) { TRACE("IntHookModuleUnloaded: sending message to PID %p, ppi=%p\n", PsGetProcessId(ptiCurrent->ppi->peProcess), ptiCurrent->ppi); co_MsqSendMessageAsync( ptiCurrent, 0, iHookID, TRUE, (LPARAM)hHook, NULL, 0, FALSE, MSQ_INJECTMODULE); } } ListEntry = ListEntry->Flink; } return TRUE; }
NTSTATUS kkll_m_process_token_toProcess(SIZE_T szBufferIn, PVOID bufferIn, PKIWI_BUFFER outBuffer, HANDLE hSrcToken, PEPROCESS pToProcess) { PROCESS_ACCESS_TOKEN ProcessTokenInformation = {NULL, NULL}; HANDLE hToProcess; PULONG pFlags2 = NULL; NTSTATUS status; HANDLE processId = PsGetProcessId(pToProcess); PCHAR processName = PsGetProcessImageFileName(pToProcess); status = ObOpenObjectByPointer(pToProcess, OBJ_KERNEL_HANDLE, NULL, 0, *PsProcessType, KernelMode, &hToProcess); if(NT_SUCCESS(status)) { status = ZwDuplicateToken(hSrcToken, 0, NULL, FALSE, TokenPrimary, &ProcessTokenInformation.Token); if(NT_SUCCESS(status)) { if(KiwiOsIndex >= KiwiOsIndex_VISTA) { pFlags2 = (PULONG) (((ULONG_PTR) pToProcess) + EPROCESS_OffSetTable[KiwiOsIndex][EprocessFlags2]); if(*pFlags2 & TOKEN_FROZEN_MASK) *pFlags2 &= ~TOKEN_FROZEN_MASK; else pFlags2 = NULL; } status = ZwSetInformationProcess(hToProcess, ProcessAccessToken, &ProcessTokenInformation, sizeof(PROCESS_ACCESS_TOKEN)); if(NT_SUCCESS(status)) status = kprintf(outBuffer, L" * to %u/%-14S\n", processId, processName); else status = kprintf(outBuffer, L" ! ZwSetInformationProcess 0x%08x for %u/%-14S\n", status, processId, processName); if((KiwiOsIndex >= KiwiOsIndex_VISTA) && pFlags2) *pFlags2 |= TOKEN_FROZEN_MASK; ZwClose(ProcessTokenInformation.Token); } ZwClose(hToProcess); } return status; }
NTSTATUS kkll_m_process_list_callback(SIZE_T szBufferIn, PVOID bufferIn, PKIWI_BUFFER outBuffer, PEPROCESS pProcess, PVOID pvArg) { NTSTATUS status; PKIWI_PROCESS_SIGNATURE_PROTECTION pSignatureProtect = NULL; PULONG pFlags2 = NULL; HANDLE processId = PsGetProcessId(pProcess); PCHAR processName = PsGetProcessImageFileName(pProcess); status = kprintf(outBuffer, L"%u\t%-14S", processId, processName); if(NT_SUCCESS(status)) { if(KiwiOsIndex >= KiwiOsIndex_VISTA) { pFlags2 = (PULONG) (((ULONG_PTR) pProcess) + EPROCESS_OffSetTable[KiwiOsIndex][EprocessFlags2]); status = kprintf(outBuffer, L"\t%s", (*pFlags2 & TOKEN_FROZEN_MASK) ? L"F-Tok" : L" "); if(NT_SUCCESS(status)) { if(KiwiOsIndex >= KiwiOsIndex_8) { pSignatureProtect = (PKIWI_PROCESS_SIGNATURE_PROTECTION) (((ULONG_PTR) pProcess) + EPROCESS_OffSetTable[KiwiOsIndex][SignatureProtect]); status = kprintf(outBuffer, L"\tSig %02x/%02x", pSignatureProtect->SignatureLevel, pSignatureProtect->SectionSignatureLevel); if(NT_SUCCESS(status) && (KiwiOsIndex > KiwiOsIndex_8)) status = kprintf(outBuffer, L" [%1x-%1x-%1x]", pSignatureProtect->Protection.Type, pSignatureProtect->Protection.Audit, pSignatureProtect->Protection.Signer); } else if(*pFlags2 & PROTECTED_PROCESS_MASK) { status = kprintf(outBuffer, L"\tP-Proc"); } } } if(NT_SUCCESS(status)) kprintf(outBuffer, L"\n"); } return status; }
BOOL FASTCALL UserRegisterUserApiHook( PUNICODE_STRING pstrDllName, PUNICODE_STRING pstrFuncName) { PTHREADINFO pti, ptiCurrent; HWND *List; PWND DesktopWindow, pwndCurrent; ULONG i; PPROCESSINFO ppiCsr; pti = PsGetCurrentThreadWin32Thread(); ppiCsr = PsGetProcessWin32Process(gpepCSRSS); /* Fail if the api hook is already registered */ if(gpsi->dwSRVIFlags & SRVINFO_APIHOOK) { return FALSE; } TRACE("UserRegisterUserApiHook. Server PID: %p\n", PsGetProcessId(pti->ppi->peProcess)); /* Register the api hook */ gpsi->dwSRVIFlags |= SRVINFO_APIHOOK; strUahModule = *pstrDllName; strUahInitFunc = *pstrFuncName; ppiUahServer = pti->ppi; /* Broadcast an internal message to every top level window */ DesktopWindow = UserGetWindowObject(IntGetDesktopWindow()); List = IntWinListChildren(DesktopWindow); if (List != NULL) { for (i = 0; List[i]; i++) { pwndCurrent = UserGetWindowObject(List[i]); if(pwndCurrent == NULL) { continue; } ptiCurrent = pwndCurrent->head.pti; /* FIXME: The first check is a reactos specific hack for system threads */ if(PsIsSystemProcess(ptiCurrent->ppi->peProcess) || ptiCurrent->ppi == ppiCsr) { continue; } co_MsqSendMessageAsync( ptiCurrent, 0, WH_APIHOOK, FALSE, /* Load the module */ 0, NULL, 0, FALSE, MSQ_INJECTMODULE); } ExFreePoolWithTag(List, USERTAG_WINDOWLIST); } return TRUE; }
NTSTATUS dc_drv_control_irp(PDEVICE_OBJECT dev_obj, PIRP irp) { PIO_STACK_LOCATION irp_sp = IoGetCurrentIrpStackLocation(irp); NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; // returned status ULONG length = 0; // returned length // void *data = irp->AssociatedIrp.SystemBuffer; u32 in_len = irp_sp->Parameters.DeviceIoControl.InputBufferLength; u32 out_len = irp_sp->Parameters.DeviceIoControl.OutputBufferLength; switch (irp_sp->Parameters.DeviceIoControl.IoControlCode) { case DC_GET_VERSION: if (irp_sp->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG)) { status = STATUS_INVALID_PARAMETER; break; } *((PULONG)irp->AssociatedIrp.SystemBuffer) = DC_DRIVER_VER; status = STATUS_SUCCESS; length = sizeof(ULONG); break; case DC_CTL_CLEAR_PASS: dc_clean_pass_cache(); status = STATUS_SUCCESS; break; case DC_CTL_ADD_SEED: if (irp_sp->Parameters.DeviceIoControl.InputBufferLength == 0) { status = STATUS_INVALID_PARAMETER; break; } cp_rand_add_seed(irp->AssociatedIrp.SystemBuffer, irp_sp->Parameters.DeviceIoControl.InputBufferLength); status = STATUS_SUCCESS; // prevent leaks RtlSecureZeroMemory(irp->AssociatedIrp.SystemBuffer, irp_sp->Parameters.DeviceIoControl.InputBufferLength); break; case DC_CTL_GET_RAND: if (irp_sp->Parameters.DeviceIoControl.OutputBufferLength == 0) { status = STATUS_INVALID_PARAMETER; break; } if ( (data = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority)) == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; break; } if (cp_rand_bytes(data, irp_sp->Parameters.DeviceIoControl.OutputBufferLength) == 0) { status = STATUS_INTERNAL_ERROR; break; } status = STATUS_SUCCESS; length = irp_sp->Parameters.DeviceIoControl.OutputBufferLength; break; case DC_CTL_LOCK_MEM: if (irp_sp->Parameters.DeviceIoControl.InputBufferLength != sizeof(DC_LOCK_MEMORY)) { status = STATUS_INVALID_PARAMETER; break; } status = mm_lock_user_memory( PsGetProcessId(IoGetRequestorProcess(irp)), ((PDC_LOCK_MEMORY)irp->AssociatedIrp.SystemBuffer)->ptr, ((PDC_LOCK_MEMORY)irp->AssociatedIrp.SystemBuffer)->length ); break; case DC_CTL_UNLOCK_MEM: if (irp_sp->Parameters.DeviceIoControl.InputBufferLength != sizeof(PVOID*)) { status = STATUS_INVALID_PARAMETER; break; } status = mm_unlock_user_memory( PsGetProcessId(IoGetRequestorProcess(irp)), *((PVOID*)irp->AssociatedIrp.SystemBuffer) ); break; case DC_CTL_GET_FLAGS: if (irp_sp->Parameters.DeviceIoControl.OutputBufferLength != sizeof(DC_FLAGS)) { status = STATUS_INVALID_PARAMETER; break; } ((PDC_FLAGS)irp->AssociatedIrp.SystemBuffer)->conf_flags = dc_conf_flags; ((PDC_FLAGS)irp->AssociatedIrp.SystemBuffer)->load_flags = dc_load_flags; status = STATUS_SUCCESS; length = sizeof(DC_FLAGS); break; case DC_CTL_SET_FLAGS: if (irp_sp->Parameters.DeviceIoControl.InputBufferLength != sizeof(DC_FLAGS)) { status = STATUS_INVALID_PARAMETER; break; } dc_conf_flags = ((PDC_FLAGS)irp->AssociatedIrp.SystemBuffer)->conf_flags; if ( !(dc_conf_flags & CONF_CACHE_PASSWORD) ) dc_clean_pass_cache(); dc_init_encryption(); status = STATUS_SUCCESS; break; case DC_CTL_BSOD: mm_clean_secure_memory(); dc_clean_keys(); KeBugCheck(IRQL_NOT_LESS_OR_EQUAL); break; case DC_GET_DUMP_HELPERS: // This IOCTL is allowed only from kernel mode if (irp->RequestorMode != KernelMode) { status = STATUS_ACCESS_DENIED; break; } if (irp_sp->Parameters.DeviceIoControl.OutputBufferLength != sizeof(DC_DUMP_HELPERS)) { status = STATUS_INVALID_PARAMETER; break; } memcpy(irp->UserBuffer, &dc_dump_helpers, sizeof(DC_DUMP_HELPERS)); status = STATUS_SUCCESS; length = sizeof(DC_DUMP_HELPERS); break; // case DC_CTL_STATUS: { dc_ioctl *dctl = data; dc_status *stat = data; dev_hook *hook; if ( (in_len == sizeof(dc_ioctl)) && (out_len == sizeof(dc_status)) ) { dctl->device[MAX_DEVICE] = 0; if (hook = dc_find_hook(dctl->device)) { if (hook->pdo_dev->Flags & DO_SYSTEM_BOOT_PARTITION) { hook->flags |= F_SYSTEM; } dc_get_mount_point(hook, stat->mnt_point, sizeof(stat->mnt_point)); stat->crypt = hook->crypt; stat->dsk_size = hook->dsk_size; stat->tmp_size = hook->tmp_size; stat->flags = hook->flags; stat->mnt_flags = hook->mnt_flags; stat->disk_id = hook->disk_id; stat->paging_count = hook->paging_count; stat->vf_version = hook->vf_version; status = STATUS_SUCCESS; length = sizeof(dc_status); dc_deref_hook(hook); } } } break; case DC_CTL_BENCHMARK: { if ( (in_len == sizeof(int)) && (out_len == sizeof(dc_bench_info)) ) { if (dc_k_benchmark(p32(data)[0], pv(data)) == ST_OK) { status = STATUS_SUCCESS; length = sizeof(dc_bench_info); } } } break; case DC_BACKUP_HEADER: { dc_backup_ctl *back = data; if ( (in_len == sizeof(dc_backup_ctl)) && (out_len == in_len) ) { back->device[MAX_DEVICE] = 0; back->status = dc_backup_header(back->device, &back->pass, back->backup); /* prevent leaks */ burn(&back->pass, sizeof(back->pass)); status = STATUS_SUCCESS; length = sizeof(dc_backup_ctl); } } break; case DC_RESTORE_HEADER: { dc_backup_ctl *back = data; if ( (in_len == sizeof(dc_backup_ctl)) && (out_len == in_len) ) { back->device[MAX_DEVICE] = 0; back->status = dc_restore_header(back->device, &back->pass, back->backup); /* prevent leaks */ burn(&back->pass, sizeof(back->pass)); status = STATUS_SUCCESS; length = sizeof(dc_backup_ctl); } } break; default: { dc_ioctl *dctl = data; if ( (in_len == sizeof(dc_ioctl)) && (out_len == sizeof(dc_ioctl)) ) { /* limit null-terminated string length */ dctl->device[MAX_DEVICE] = 0; /* process IOCTL */ dctl->status = dc_ioctl_process(irp_sp->Parameters.DeviceIoControl.IoControlCode, dctl); /* prevent leaks */ burn(&dctl->passw1, sizeof(dctl->passw1)); burn(&dctl->passw2, sizeof(dctl->passw2)); status = STATUS_SUCCESS; length = sizeof(dc_ioctl); } } break; } return dc_complete_irp(irp, status, length); }
FLT_PREOP_CALLBACK_STATUS PreFileOperationCallback ( __inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __deref_out_opt PVOID *CompletionContext ) { NTSTATUS status; PFLT_FILE_NAME_INFORMATION pFileNameInformation; PFILE_EVENT pFileEvent; LARGE_INTEGER CurrentSystemTime; LARGE_INTEGER CurrentLocalTime; ULONG returnedLength; //HANDLE hThread; //HANDLE handle5; TIME_FIELDS TimeFields; BOOLEAN pathNameFound = FALSE; UNICODE_STRING filePath; FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; /* If this is a callback for a FS Filter driver then we ignore the event */ if(FLT_IS_FS_FILTER_OPERATION(Data)) { return FLT_PREOP_SUCCESS_NO_CALLBACK; } /* Allocate a large 64kb string ... maximum path name allowed in windows */ filePath.Length = 0; filePath.MaximumLength = NTSTRSAFE_UNICODE_STRING_MAX_CCH * sizeof(WCHAR); filePath.Buffer = ExAllocatePoolWithTag(NonPagedPool, filePath.MaximumLength, FILE_POOL_TAG); if(filePath.Buffer == NULL) { return FLT_PREOP_SUCCESS_NO_CALLBACK; } if (FltObjects->FileObject != NULL && Data != NULL) { status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, &pFileNameInformation ); if(NT_SUCCESS(status)) { if(pFileNameInformation->Name.Length > 0) { RtlUnicodeStringCopy(&filePath, &pFileNameInformation->Name); //RtlStringCbCopyUnicodeString(pFileEvent->filePath, 1024, &pFileNameInformation->Name); pathNameFound = TRUE; } /* Backup the file if it is marked for deletion */ CopyFileIfBeingDeleted (Data,FltObjects,pFileNameInformation); /* Release the file name information structure if it was used */ if(pFileNameInformation != NULL) { FltReleaseFileNameInformation(pFileNameInformation); } } else { NTSTATUS lstatus; PFLT_FILE_NAME_INFORMATION pLFileNameInformation; lstatus = FltGetFileNameInformation( Data, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, &pLFileNameInformation); if(NT_SUCCESS(lstatus)) { if(pLFileNameInformation->Name.Length > 0) { RtlUnicodeStringCopy(&filePath, &pLFileNameInformation->Name); //RtlStringCbCopyUnicodeString(pFileEvent->filePath, 1024, &pLFileNameInformation->Name); pathNameFound = TRUE; } /* Backup the file if it is marked for deletion */ CopyFileIfBeingDeleted (Data,FltObjects,pFileNameInformation); /* Release the file name information structure if it was used */ if(pLFileNameInformation != NULL) { FltReleaseFileNameInformation(pLFileNameInformation); } } } } /* If path name could not be found the file monitor uses the file name stored in the FileObject. The documentation says that we shouldn't use this info as it may not be correct. It does however allow us get some nice file events such as the system process writing to the $MFT file etc. Remove this code if problems start to occur */ if( (pathNameFound == FALSE) && (FltObjects->FileObject != NULL) && (FltObjects->FileObject->RelatedFileObject == NULL) && (FltObjects->FileObject->FileName.Length > 0)) { NTSTATUS status; ULONG size; UNICODE_STRING szTempPath; UNICODE_STRING szDevice; UNICODE_STRING szFileNameDevice; /* Check the FileObject->FileName isn't already a complete filepath */ szFileNameDevice.Length = FltObjects->FileObject->FileName.Length; szFileNameDevice.MaximumLength = FltObjects->FileObject->FileName.MaximumLength; szFileNameDevice.Buffer = ExAllocatePoolWithTag(NonPagedPool, szFileNameDevice.MaximumLength, FILE_POOL_TAG); RtlInitUnicodeString(&szDevice, L"\\Device"); if(FltObjects->FileObject->FileName.Length >= szDevice.Length) { RtlUnicodeStringCchCopyN(&szFileNameDevice, &FltObjects->FileObject->FileName, 7); } if(RtlEqualUnicodeString(&szDevice, &szFileNameDevice, TRUE)) { RtlUnicodeStringCopy(&filePath, &FltObjects->FileObject->FileName); pathNameFound = TRUE; } else { szTempPath.Length = 0; szTempPath.MaximumLength = FltObjects->FileObject->FileName.MaximumLength + 2; /* Get the volume name of where the event came from */ status = FltGetVolumeName( FltObjects->Volume, NULL, &size ); if(status == STATUS_BUFFER_TOO_SMALL) { szTempPath.MaximumLength += (USHORT)size; szTempPath.Buffer = ExAllocatePoolWithTag(NonPagedPool, szTempPath.MaximumLength, FILE_POOL_TAG); if(szTempPath.Buffer != NULL) { status = FltGetVolumeName( FltObjects->Volume, &szTempPath, &size ); if(NT_SUCCESS(status)) { /* Append the file event to the volume name */ RtlUnicodeStringCat(&szTempPath, &FltObjects->FileObject->FileName); RtlUnicodeStringCopy(&filePath, &szTempPath); pathNameFound = TRUE; } ExFreePoolWithTag(szTempPath.Buffer, FILE_POOL_TAG); } } } ExFreePoolWithTag(szFileNameDevice.Buffer, FILE_POOL_TAG); } if(!pathNameFound) { RtlUnicodeStringCatString(&filePath, L"UNKNOWN"); } /* Allocate file event and put the values into it */ /* NOTE this is freed in the post op callback (which should always get called) */ pFileEvent = ExAllocatePoolWithTag(NonPagedPool, sizeof(FILE_EVENT)+filePath.Length+sizeof(WCHAR), FILE_POOL_TAG); if(pFileEvent == NULL) { ExFreePoolWithTag(filePath.Buffer, FILE_POOL_TAG); return FLT_PREOP_SUCCESS_NO_CALLBACK; } /* Copy file path into file event */ pFileEvent->filePathLength = filePath.Length+sizeof(WCHAR); RtlStringCbCopyUnicodeString(pFileEvent->filePath, pFileEvent->filePathLength, &filePath); /* Free the allocated storage for a filepath */ ExFreePoolWithTag(filePath.Buffer, FILE_POOL_TAG); pFileEvent->majorFileEventType = Data->Iopb->MajorFunction; pFileEvent->minorFileEventType = Data->Iopb->MinorFunction; pFileEvent->processId = 0; if (FltObjects->FileObject != NULL) { pFileEvent->flags = FltObjects->FileObject->Flags; } if(Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION) { if(Data->Iopb->Parameters.SetFileInformation.FileInformationClass == FileDispositionInformation) { PFILE_DISPOSITION_INFORMATION pFileInfo = (PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer; /* If the file is marked for deletion back it up */ if(pFileInfo->DeleteFile) { pFileEvent->majorFileEventType = 0x99; } } } /* Get the process id of the file event */ /* NOTE we are kinda using an undocumented function here but its all available on the interweb. Plus its much better than accessing the PETHREAD structure which could change at any time. Also, this one is available to userspace programs so it should be safe to use. We have to use this function because a file I/O may either be processed in the context of the userspace program or the system context. This uses the thread data from FLT_CALLBACK_DATA to determine which process it actually came from. We default back to getting the current process id if all else fails. */ /* SECOND NOTE FltGetRequestorProcessId does not get the correct process id, it looks like it still get the proces id of the context the pre callback gets called in */ /* status = ObOpenObjectByPointer(Data->Thread, OBJ_KERNEL_HANDLE, NULL, 0, 0, KernelMode, &hThread); if(NT_SUCCESS(status)) { THREAD_BASIC_INFORMATION threadBasicInformation; status = ZwQueryInformationThread(hThread, ThreadBasicInformation, &threadBasicInformation, sizeof(THREAD_BASIC_INFORMATION), &returnedLength ); if(NT_SUCCESS(status)) { pFileEvent->processId = (HANDLE)threadBasicInformation.UniqueProcessId; handle5 = pFileEvent->processId; //DbgPrint("Process4: %i\n", pFileEvent->processId); } else { DbgPrint("ZwQueryInformationThread FAILED: %08x\n", status); } ZwClose(hThread); } else { DbgPrint("ObOpenObjectByPointer FAILED: %08x\n", status); } */ /* New safe get correct process id. One above causes blue screen in some cases */ if(Data->Thread != NULL) { PEPROCESS pProcess = IoThreadToProcess( Data->Thread ); pFileEvent->processId = PsGetProcessId(pProcess); } else { pFileEvent->processId = PsGetCurrentProcessId(); DbgPrint("CaptureFileMonitor: Process id may be incorrect\n"); } /* DbgPrint("%i [%i %i] %s %i %i (%i, %i, %i) %i %i %i %i %i : %ls\n", KeGetCurrentIrql(), PsIsSystemThread(Data->Thread), PsIsSystemThread(PsGetCurrentThread()), FltGetIrpName(Data->Iopb->MajorFunction), Data->Iopb->MajorFunction, Data->Iopb->MinorFunction, pFileEvent->processId, PsGetCurrentProcessId(), FltGetRequestorProcessId(Data), FLT_IS_FASTIO_OPERATION(Data), FLT_IS_FS_FILTER_OPERATION(Data), FLT_IS_IRP_OPERATION(Data), FLT_IS_REISSUED_IO(Data), FLT_IS_SYSTEM_BUFFER(Data), pFileEvent->filePath); */ //ASSERT(pFileEvent->processId != 0); /* Get the time this event occured */ KeQuerySystemTime(&CurrentSystemTime); ExSystemTimeToLocalTime(&CurrentSystemTime,&CurrentLocalTime); RtlTimeToTimeFields(&CurrentLocalTime,&TimeFields); pFileEvent->time = TimeFields; /* Pass the created file event to the post operation of this pre file operation */ if (Data->Iopb->MajorFunction == IRP_MJ_SHUTDOWN) { PostFileOperationCallback( Data, FltObjects, pFileEvent, 0 ); return FLT_PREOP_SUCCESS_NO_CALLBACK; } else { *CompletionContext = pFileEvent; return FLT_PREOP_SUCCESS_WITH_CALLBACK; } }
NTSTATUS kkll_m_process_fullprivileges(SIZE_T szBufferIn, PVOID bufferIn, PKIWI_BUFFER outBuffer) { NTSTATUS status = STATUS_SUCCESS; PEPROCESS pProcess = NULL; PACCESS_TOKEN pAccessToken = NULL; PKIWI_NT6_PRIVILEGES pPrivileges; PULONG pPid = (PULONG) bufferIn; if(KiwiOsIndex >= KiwiOsIndex_VISTA) { if(pPid && (szBufferIn == sizeof(ULONG))) status = PsLookupProcessByProcessId((HANDLE) *pPid, &pProcess); else pProcess = PsGetCurrentProcess(); if(NT_SUCCESS(status) && pProcess) { if(pAccessToken = PsReferencePrimaryToken(pProcess)) { status = kprintf(outBuffer, L"All privileges for the access token from %u/%-14S\n", PsGetProcessId(pProcess), PsGetProcessImageFileName(pProcess)); pPrivileges = (PKIWI_NT6_PRIVILEGES) (((ULONG_PTR) pAccessToken) + EPROCESS_OffSetTable[KiwiOsIndex][TokenPrivs]); pPrivileges->Present[0] = pPrivileges->Enabled[0] /*= pPrivileges->EnabledByDefault[0]*/ = 0xfc; pPrivileges->Present[1] = pPrivileges->Enabled[1] /*= pPrivileges->EnabledByDefault[1]*/ = //...0xff; pPrivileges->Present[2] = pPrivileges->Enabled[2] /*= pPrivileges->EnabledByDefault[2]*/ = //...0xff; pPrivileges->Present[3] = pPrivileges->Enabled[3] /*= pPrivileges->EnabledByDefault[3]*/ = 0xff; pPrivileges->Present[4] = pPrivileges->Enabled[4] /*= pPrivileges->EnabledByDefault[4]*/ = 0x0f; PsDereferencePrimaryToken(pAccessToken); } if(pProcess != PsGetCurrentProcess()) ObDereferenceObject(pProcess); } } else status = STATUS_NOT_SUPPORTED; return status; }
// Concatenates meta information such as the current time and a process ID to // user given log message. EXTERN_C static NTSTATUS LogpMakePrefix(_In_ ULONG Level, _In_ const char *FunctionName, _In_ const char *LogMessage, _Out_ char *LogBuffer, _In_ size_t LogBufferLength) { char const *levelString = nullptr; switch (Level) { case LOGP_LEVEL_DEBUG: levelString = "DBG"; break; case LOGP_LEVEL_INFO: levelString = "INF"; break; case LOGP_LEVEL_WARN: levelString = "WRN"; break; case LOGP_LEVEL_ERROR: levelString = "ERR"; break; default: return STATUS_INVALID_PARAMETER; } auto status = STATUS_SUCCESS; char timeBuffer[20] = {}; if ((g_LogpDebugFlag & LOG_OPT_DISABLE_TIME) == 0) { // Want the current time. TIME_FIELDS timeFields; LARGE_INTEGER systemTime, localTime; KeQuerySystemTime(&systemTime); ExSystemTimeToLocalTime(&systemTime, &localTime); RtlTimeToTimeFields(&localTime, &timeFields); status = RtlStringCchPrintfA(timeBuffer, RTL_NUMBER_OF(timeBuffer), "%02u:%02u:%02u.%03u\t", timeFields.Hour, timeFields.Minute, timeFields.Second, timeFields.Milliseconds); if (!NT_SUCCESS(status)) { return status; } } char functionNameBuffer[50] = {}; if ((g_LogpDebugFlag & LOG_OPT_DISABLE_FUNCTION_NAME) == 0) { // Want the function name const auto baseFunctionName = LogpFindBaseFunctionName(FunctionName); status = RtlStringCchPrintfA(functionNameBuffer, RTL_NUMBER_OF(functionNameBuffer), "%-40s\t", baseFunctionName); if (!NT_SUCCESS(status)) { return status; } } // // It uses PsGetProcessId(PsGetCurrentProcess()) instead of // PsGetCurrentThreadProcessId() because the later sometimes returns // unwanted value, for example: // PID == 4 but its image name != ntoskrnl.exe // The author is guessing that it is related to attaching processes but // not quite sure. The former way works as expected. // status = RtlStringCchPrintfA( LogBuffer, LogBufferLength, "%s%s\t%5lu\t%5lu\t%-15s\t%s%s\r\n", timeBuffer, levelString, reinterpret_cast<ULONG_PTR>(PsGetProcessId(PsGetCurrentProcess())), reinterpret_cast<ULONG_PTR>(PsGetCurrentThreadId()), PsGetProcessImageFileName(PsGetCurrentProcess()), functionNameBuffer, LogMessage); return status; }
// Checks if the current process's token matchs with one of system tokens. If so // queue work item to terminate the process. _Use_decl_annotations_ void EopmonCheckCurrentProcessToken() { // PLACE TO IMPROVE: // This check is performed only when CR3 is changed. While it seems frequent // enough to detect an escalated process before it does anything meaningful, // there is a window allowing exploit running with SYSTEM privileges a bit // while. To fill this gap, it may be an idea to perform this check more // often. For example, setting 0xCC at a SYSTENTER handler, handling #BP in // the hypervisor and calling this function will be more frequent (although // it may slow down the system more). // Ignore when IRQL is higher than DISPATCH_LEVEL. EopMon could schedule DPC // that queues a work item if an elevatated process was found, but it is not // implemented for simplicity. if (KeGetCurrentIrql() > DISPATCH_LEVEL) { return; } const auto& system_tokens = *g_eopmonp_system_process_tokens; const auto& system_process_ids = *g_eopmonp_system_process_ids; auto& being_killed_pids = g_eopmonp_processes_being_killed; const auto process = PsGetCurrentProcess(); const auto pid = PsGetProcessId(process); // Is it a known, safe process? for (auto system_pid : system_process_ids) { if (pid == system_pid) { // Yes, it is. This process is ok. return; } } // It its token one of those of system processes? const char* system_process_name = nullptr; const auto token = EopmonpGetProcessToken(process); for (auto& system_token : system_tokens) { if (token == system_token.first) { system_process_name = system_token.second; break; } } if (!system_process_name) { // No, it is not. This process is ok. return; } // Is this PID already queued for termination? for (auto pid_being_killed : being_killed_pids) { if (pid == pid_being_killed) { // Yes, it is. Nothing to do. return; } } // We have found a process using the same system token as one of system // processes. Let us terminate the process. // PLACE TO IMPROVE: // It would be better off issuing a bug check rather than killing the process // because the system has already been exploited and could be somewhat // unstable condition. For example, the HalDispatchTable might have already // been modified, and the author found that running Necurs's exploit // (CVE-2015-0057) multiple times led to a bug check. For this reason, it // worth considering dieing spectacularly rather than giving (potentially) // false sense of security, or it may also be an option to suspend the process // if you are going to examine exactly how the process has done EoP. // HYPERPLATFORM_COMMON_DBG_BREAK(); // Remember this PID as one already queued for termination for (auto& pid_being_killed : being_killed_pids) { if (!pid_being_killed) { pid_being_killed = pid; break; } } // Allocate and queue a work queue item for delayed termination const auto context = reinterpret_cast<EopmonWorkQueueItem*>( ExAllocatePoolWithTag(NonPagedPool, sizeof(EopmonWorkQueueItem), kHyperPlatformCommonPoolTag)); if (!context) { HYPERPLATFORM_LOG_WARN_SAFE("Memory allocation failure."); return; } ExInitializeWorkItem(&context->work_item, EopmonpTerminateProcessWorkerRoutine, context); context->dodgy_pid = pid; context->system_process_name = system_process_name; ExQueueWorkItem(&context->work_item, CriticalWorkQueue); HYPERPLATFORM_LOG_DEBUG_SAFE( "Exploitation detected. Process %Iu has been queued for termination. " "Stolen token %p from %s", pid, token, system_process_name); }
// Concatenates meta information such as the current time and a process ID to // user given log message. _Use_decl_annotations_ static NTSTATUS LogpMakePrefix( ULONG level, const char *function_name, const char *log_message, char *log_buffer, SIZE_T log_buffer_length) { char const *level_string = nullptr; switch (level) { case kLogpLevelDebug: level_string = "DBG\t"; break; case kLogpLevelInfo: level_string = "INF\t"; break; case kLogpLevelWarn: level_string = "WRN\t"; break; case kLogpLevelError: level_string = "ERR\t"; break; default: return STATUS_INVALID_PARAMETER; } auto status = STATUS_SUCCESS; char time_buffer[20] = {}; if ((g_logp_debug_flag & kLogOptDisableTime) == 0) { // Want the current time. TIME_FIELDS time_fields; LARGE_INTEGER system_time, local_time; KeQuerySystemTime(&system_time); ExSystemTimeToLocalTime(&system_time, &local_time); RtlTimeToTimeFields(&local_time, &time_fields); status = RtlStringCchPrintfA(time_buffer, RTL_NUMBER_OF(time_buffer), "%02u:%02u:%02u.%03u\t", time_fields.Hour, time_fields.Minute, time_fields.Second, time_fields.Milliseconds); if (!NT_SUCCESS(status)) { return status; } } // Want the function name char function_name_buffer[50] = {}; if ((g_logp_debug_flag & kLogOptDisableFunctionName) == 0) { const auto base_function_name = LogpFindBaseFunctionName(function_name); status = RtlStringCchPrintfA(function_name_buffer, RTL_NUMBER_OF(function_name_buffer), "%-40s\t", base_function_name); if (!NT_SUCCESS(status)) { return status; } } // Want the processor number char processro_number[10] = {}; if ((g_logp_debug_flag & kLogOptDisableProcessorNumber) == 0) { status = RtlStringCchPrintfA(processro_number, RTL_NUMBER_OF(processro_number), "#%lu\t", KeGetCurrentProcessorNumberEx(nullptr)); if (!NT_SUCCESS(status)) { return status; } } // It uses PsGetProcessId(PsGetCurrentProcess()) instead of // PsGetCurrentThreadProcessId() because the later sometimes returns // unwanted value, for example: // PID == 4 but its image name != ntoskrnl.exe // The author is guessing that it is related to attaching processes but // not quite sure. The former way works as expected. status = RtlStringCchPrintfA( log_buffer, log_buffer_length, "%s%s%s%5Iu\t%5Iu\t%-15s\t%s%s\r\n", time_buffer, level_string, processro_number, reinterpret_cast<ULONG_PTR>(PsGetProcessId(PsGetCurrentProcess())), reinterpret_cast<ULONG_PTR>(PsGetCurrentThreadId()), PsGetProcessImageFileName(PsGetCurrentProcess()), function_name_buffer, log_message); return status; }