diff options
Diffstat (limited to 'src/vm/amsi.cpp')
-rw-r--r-- | src/vm/amsi.cpp | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/vm/amsi.cpp b/src/vm/amsi.cpp new file mode 100644 index 0000000000..47da6708e5 --- /dev/null +++ b/src/vm/amsi.cpp @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// File: amsi.cpp +// + +#include "common.h" +#include "amsi.h" + +namespace +{ + // https://docs.microsoft.com/en-us/windows/desktop/api/amsi/ + DECLARE_HANDLE(HAMSICONTEXT); + DECLARE_HANDLE(HAMSISESSION); + + enum AMSI_RESULT + { + AMSI_RESULT_CLEAN = 0, + AMSI_RESULT_NOT_DETECTED = 1, + AMSI_RESULT_BLOCKED_BY_ADMIN_START = 0x4000, + AMSI_RESULT_BLOCKED_BY_ADMIN_END = 0x4fff, + AMSI_RESULT_DETECTED = 0x8000 + } AMSI_RESULT; + + bool AmsiResultIsMalware(DWORD result) + { + return result >= AMSI_RESULT_DETECTED; + } + + bool AmsiResultIsBlockedByAdmin(DWORD result) + { + return result >= AMSI_RESULT_BLOCKED_BY_ADMIN_START + && result <= AMSI_RESULT_BLOCKED_BY_ADMIN_END; + } + + using PAMSI_AMSISCANBUFFER_API = HRESULT(WINAPI *)( + _In_ HAMSICONTEXT amsiContext, + _In_ PVOID buffer, + _In_ ULONG length, + _In_ LPCWSTR contentName, + _In_opt_ HAMSISESSION session, + _Out_ DWORD *result); + + using PAMSI_AMSIINITIALIZE_API = HRESULT(WINAPI *)( + _In_ LPCWSTR appName, + _Out_ HAMSICONTEXT *amsiContext); + + PAMSI_AMSISCANBUFFER_API AmsiScanBuffer; + HAMSICONTEXT s_amsiContext; + CRITSEC_COOKIE s_csAmsi; + + bool InitializeLock() + { + if (s_csAmsi != nullptr) + return true; + + CRITSEC_COOKIE lock = ClrCreateCriticalSection(CrstLeafLock, CRST_REENTRANCY); + if (lock == nullptr) + return false; + + if (InterlockedCompareExchangeT<CRITSEC_COOKIE>(&s_csAmsi, lock, nullptr) != nullptr) + ClrDeleteCriticalSection(lock); + + return true; + } +} + +// Here we will invoke into AmsiScanBuffer, a centralized area for non-OS +// programs to report into Defender (and potentially other anti-malware tools). +// This should only run on in memory loads, Assembly.Load(byte[]) for example. +// Loads from disk are already instrumented by Defender, so calling AmsiScanBuffer +// wouldn't do anything. +bool Amsi::IsBlockedByAmsiScan(PVOID flatImageBytes, COUNT_T size) +{ + STANDARD_VM_CONTRACT; + + if (!InitializeLock()) + return false; + + // Lazily initialize AMSI because it is very expensive + { + CRITSEC_Holder csh(s_csAmsi); + + // Cache that we failed if this didn't work so we don't keep trying to reinitialize + static bool amsiInitializationAttempted = false; + if (s_amsiContext == nullptr && !amsiInitializationAttempted) + { + HMODULE amsi = CLRLoadLibraryEx(W("amsi.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (amsi != nullptr) + { + PAMSI_AMSIINITIALIZE_API AmsiInitialize = (PAMSI_AMSIINITIALIZE_API)GetProcAddress(amsi, "AmsiInitialize"); + if (AmsiInitialize != nullptr) + { + HAMSICONTEXT amsiContext = nullptr; + if (AmsiInitialize(W("coreclr"), &amsiContext) == S_OK) + { + AmsiScanBuffer = (PAMSI_AMSISCANBUFFER_API)GetProcAddress(amsi, "AmsiScanBuffer"); + if (AmsiScanBuffer != nullptr) + { + s_amsiContext = amsiContext; + } + } + } + } + + amsiInitializationAttempted = true; + } + } + + if (s_amsiContext == nullptr || AmsiScanBuffer == nullptr) + return false; + + DWORD result; + HRESULT hr = AmsiScanBuffer(s_amsiContext, flatImageBytes, size, nullptr, nullptr, &result); + if (hr == S_OK && (AmsiResultIsMalware(result) || AmsiResultIsBlockedByAdmin(result))) + return true; + + return false; +}
\ No newline at end of file |