forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Port AMSI scanning for assembly loads (dotnet/coreclr#23231)
* Port AMSI scanning for assembly loads * Define PLATFORM_WINDOWS for Windows build * Remove check for LOAD_LIBRARY_SEARCH_SYSTEM32 support Commit migrated from dotnet/coreclr@3a02976
- Loading branch information
1 parent
1407ff7
commit b20ed44
Showing
5 changed files
with
204 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// 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.h | ||
// | ||
|
||
#ifndef __AMSI_H__ | ||
#define __AMSI_H__ | ||
|
||
namespace Amsi | ||
{ | ||
bool IsBlockedByAmsiScan(void *flatImageBytes, COUNT_T size); | ||
}; | ||
|
||
#endif // __AMSI_H__ |
Oops, something went wrong.