mirror of
https://github.com/manyfacedllama/amsi-tracer
synced 2026-06-08 15:43:28 +00:00
242 lines
7.6 KiB
C++
242 lines
7.6 KiB
C++
#include "stdafx.h"
|
|
#include <string>
|
|
#include <chrono>
|
|
#include <shlwapi.h>
|
|
|
|
using namespace Microsoft::WRL;
|
|
using namespace std::chrono;
|
|
using namespace std;
|
|
|
|
HMODULE g_currentModule;
|
|
|
|
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved)
|
|
{
|
|
switch (reason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
g_currentModule = module;
|
|
DisableThreadLibraryCalls(module);
|
|
Module<InProc>::GetModule().Create();
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
Module<InProc>::GetModule().Terminate();
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#pragma region COM server boilerplate
|
|
HRESULT WINAPI DllCanUnloadNow()
|
|
{
|
|
return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv)
|
|
{
|
|
return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
|
|
}
|
|
#pragma endregion
|
|
|
|
// Simple RAII class to ensure memory is freed.
|
|
template<typename T>
|
|
class HeapMemPtr
|
|
{
|
|
public:
|
|
HeapMemPtr() { }
|
|
HeapMemPtr(const HeapMemPtr& other) = delete;
|
|
HeapMemPtr(HeapMemPtr&& other) : p(other.p) { other.p = nullptr; }
|
|
HeapMemPtr& operator=(const HeapMemPtr& other) = delete;
|
|
HeapMemPtr& operator=(HeapMemPtr&& other) {
|
|
auto t = p; p = other.p; other.p = t;
|
|
}
|
|
|
|
~HeapMemPtr()
|
|
{
|
|
if (p) HeapFree(GetProcessHeap(), 0, p);
|
|
}
|
|
|
|
HRESULT Alloc(size_t size)
|
|
{
|
|
p = reinterpret_cast<T*>(HeapAlloc(GetProcessHeap(), 0, size));
|
|
return p ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
T* Get() { return p; }
|
|
operator bool() { return p != nullptr; }
|
|
|
|
private:
|
|
T* p = nullptr;
|
|
};
|
|
|
|
class
|
|
// Register it as the top provider (top-down)
|
|
DECLSPEC_UUID("00000000-0000-0000-0000-000000000000")
|
|
AmsiTracer : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IAntimalwareProvider, FtmBase>
|
|
{
|
|
public:
|
|
IFACEMETHOD(Scan)(_In_ IAmsiStream* stream, _Out_ AMSI_RESULT* result) override;
|
|
IFACEMETHOD_(void, CloseSession)(_In_ ULONGLONG session) override;
|
|
IFACEMETHOD(DisplayName)(_Outptr_ LPWSTR* displayName) override;
|
|
|
|
private:
|
|
LONG m_requestNumber = 0;
|
|
wstring processId = to_wstring(GetCurrentProcessId());
|
|
wstring threadId = to_wstring(GetCurrentThreadId());
|
|
};
|
|
|
|
template<typename T>
|
|
T GetFixedSizeAttribute(_In_ IAmsiStream* stream, _In_ AMSI_ATTRIBUTE attribute)
|
|
{
|
|
T result;
|
|
|
|
ULONG actualSize;
|
|
if (SUCCEEDED(stream->GetAttribute(attribute, sizeof(T), reinterpret_cast<PBYTE>(&result), &actualSize)) &&
|
|
actualSize == sizeof(T))
|
|
{
|
|
return result;
|
|
}
|
|
return T();
|
|
}
|
|
|
|
HeapMemPtr<wchar_t> GetStringAttribute(_In_ IAmsiStream* stream, _In_ AMSI_ATTRIBUTE attribute)
|
|
{
|
|
HeapMemPtr<wchar_t> result;
|
|
|
|
ULONG allocSize;
|
|
ULONG actualSize;
|
|
if (stream->GetAttribute(attribute, 0, nullptr, &allocSize) == E_NOT_SUFFICIENT_BUFFER &&
|
|
SUCCEEDED(result.Alloc(allocSize)) &&
|
|
SUCCEEDED(stream->GetAttribute(attribute, allocSize, reinterpret_cast<PBYTE>(result.Get()), &actualSize)) &&
|
|
actualSize <= allocSize)
|
|
{
|
|
return result;
|
|
}
|
|
return HeapMemPtr<wchar_t>();
|
|
}
|
|
|
|
HRESULT AmsiTracer::Scan(_In_ IAmsiStream* stream, _Out_ AMSI_RESULT* result)
|
|
{
|
|
LONG requestNumber = InterlockedIncrement(&m_requestNumber);
|
|
|
|
auto epoch = duration_cast<milliseconds>(
|
|
system_clock::now().time_since_epoch()
|
|
).count();
|
|
wchar_t processFilePath[MAX_PATH] = { 0 };
|
|
GetModuleFileNameW(NULL, processFilePath, MAX_PATH);
|
|
wstring processFileName = PathFindFileNameW(processFilePath);
|
|
wstring fileName = to_wstring(epoch) + L"_"
|
|
+ processFileName + L"_"
|
|
+ processId + L"_"
|
|
+ threadId + L"_"
|
|
+ to_wstring(requestNumber)
|
|
+ L".dmp";
|
|
|
|
auto appName = GetStringAttribute(stream, AMSI_ATTRIBUTE_APP_NAME);
|
|
auto contentName = GetStringAttribute(stream, AMSI_ATTRIBUTE_CONTENT_NAME);
|
|
auto contentSize = GetFixedSizeAttribute<ULONGLONG>(stream, AMSI_ATTRIBUTE_CONTENT_SIZE);
|
|
auto session = GetFixedSizeAttribute<ULONGLONG>(stream, AMSI_ATTRIBUTE_SESSION);
|
|
auto contentAddress = GetFixedSizeAttribute<PBYTE>(stream, AMSI_ATTRIBUTE_CONTENT_ADDRESS);
|
|
|
|
// Create base folder if it doesn't exist
|
|
wstring baseFolder = L"C:\\amsi_tracer";
|
|
CreateDirectoryW(baseFolder.c_str(), NULL);
|
|
|
|
// Dump into file
|
|
wstring traceFilePath = baseFolder + L"\\" + fileName;
|
|
HANDLE h = CreateFile(traceFilePath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
WriteFile(h, contentAddress, contentSize, NULL, nullptr);
|
|
CloseHandle(h);
|
|
// AMSI_RESULT_NOT_DETECTED means "We did not detect a problem but let other providers scan it, too."
|
|
*result = AMSI_RESULT_NOT_DETECTED;
|
|
return S_OK;
|
|
}
|
|
|
|
void AmsiTracer::CloseSession(_In_ ULONGLONG session)
|
|
{
|
|
}
|
|
|
|
HRESULT AmsiTracer::DisplayName(_Outptr_ LPWSTR *displayName)
|
|
{
|
|
*displayName = const_cast<LPWSTR>(L"AMSI Tracer");
|
|
return S_OK;
|
|
}
|
|
|
|
CoCreatableClass(AmsiTracer);
|
|
|
|
#pragma region Install / uninstall
|
|
|
|
HRESULT SetKeyStringValue(_In_ HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR valueName, _In_ PCWSTR stringValue)
|
|
{
|
|
LONG status = RegSetKeyValue(key, subkey, valueName, REG_SZ, stringValue, (wcslen(stringValue) + 1) * sizeof(wchar_t));
|
|
return HRESULT_FROM_WIN32(status);
|
|
}
|
|
|
|
STDAPI DllRegisterServer()
|
|
{
|
|
wchar_t modulePath[MAX_PATH];
|
|
if (GetModuleFileName(g_currentModule, modulePath, ARRAYSIZE(modulePath)) >= ARRAYSIZE(modulePath))
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
// Create a standard COM registration for our CLSID.
|
|
// The class must be registered as "Both" threading model
|
|
// and support multithreaded access.
|
|
wchar_t clsidString[40];
|
|
if (StringFromGUID2(__uuidof(AmsiTracer), clsidString, ARRAYSIZE(clsidString)) == 0)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
wchar_t keyPath[200];
|
|
HRESULT hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\\Classes\\CLSID\\%ls", clsidString);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, keyPath, nullptr, L"AmsiTracer");
|
|
if (FAILED(hr)) return hr;
|
|
|
|
hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\\Classes\\CLSID\\%ls\\InProcServer32", clsidString);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, keyPath, nullptr, modulePath);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, keyPath, L"ThreadingModel", L"Both");
|
|
if (FAILED(hr)) return hr;
|
|
|
|
// Register this CLSID as an anti-malware provider.
|
|
hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\\Microsoft\\AMSI\\Providers\\%ls", clsidString);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, keyPath, nullptr, L"AmsiTracer");
|
|
if (FAILED(hr)) return hr;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDAPI DllUnregisterServer()
|
|
{
|
|
wchar_t clsidString[40];
|
|
if (StringFromGUID2(__uuidof(AmsiTracer), clsidString, ARRAYSIZE(clsidString)) == 0)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
// Unregister this CLSID as an anti-malware provider.
|
|
wchar_t keyPath[200];
|
|
HRESULT hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\\Microsoft\\AMSI\\Providers\\%ls", clsidString);
|
|
if (FAILED(hr)) return hr;
|
|
LONG status = RegDeleteTree(HKEY_LOCAL_MACHINE, keyPath);
|
|
if (status != NO_ERROR && status != ERROR_PATH_NOT_FOUND) return HRESULT_FROM_WIN32(status);
|
|
|
|
// Unregister this CLSID as a COM server.
|
|
hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\\Classes\\CLSID\\%ls", clsidString);
|
|
if (FAILED(hr)) return hr;
|
|
status = RegDeleteTree(HKEY_LOCAL_MACHINE, keyPath);
|
|
if (status != NO_ERROR && status != ERROR_PATH_NOT_FOUND) return HRESULT_FROM_WIN32(status);
|
|
|
|
return S_OK;
|
|
}
|
|
#pragma endregion
|