1
0
Fork 0

Initial implementation of the signature check to prevent abuse

(cunty)
This commit is contained in:
Duncan Ogilvie 2023-03-04 02:16:32 +01:00
parent ed0fce1124
commit 1e616021e8
9 changed files with 438 additions and 116 deletions

View File

@ -32,9 +32,7 @@ static bool bDisableGUIUpdate;
#define LOADLIBRARY(name) \
szLib=name; \
hInst=LoadLibraryW(name); \
if(!hInst) \
return L"Error loading library \"" name L"\"!"
hInst=BridgeLoadLibraryCheckedW(name, false);
#define LOADEXPORT(name) \
*((FARPROC*)&name)=GetProcAddress(hInst, #name); \
@ -65,13 +63,12 @@ static bool DirExists(const wchar_t* dir)
return (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
}
BRIDGE_IMPEXP const wchar_t* BridgeInit()
{
//Initialize critial section
InitializeCriticalSection(&csIni);
InitializeCriticalSection(&csTranslate);
static decltype(&BridgeLoadLibraryCheckedW) pLoadLibraryCheckedW;
static decltype(&BridgeLoadLibraryCheckedA) pLoadLibraryCheckedA;
//Settings load
static const wchar_t* InitializeUserDirectory()
{
// Handle user directory
if(!GetModuleFileNameW(0, szUserDirectory, _countof(szUserDirectory)))
return L"Error getting module path!";
@ -133,24 +130,69 @@ BRIDGE_IMPEXP const wchar_t* BridgeInit()
if(!DirExists(userDirUtf16.c_str()))
return L"Specified user directory doesn't exist";
// Read the settings from the INI
wcscpy_s(szIniFile, userDirUtf16.c_str());
wcscat_s(szIniFile, L"\\");
wcscat_s(szIniFile, fileNameWithoutExtension);
wcscat_s(szIniFile, L".ini");
BridgeSettingRead(nullptr);
wcscpy_s(szUserDirectory, userDirUtf16.c_str());
return nullptr;
}
BRIDGE_IMPEXP const wchar_t* BridgeInit()
{
//Initialize critial section
InitializeCriticalSection(&csIni);
InitializeCriticalSection(&csTranslate);
// Signature checking functions
auto hMainModule = GetModuleHandleW(nullptr);
pLoadLibraryCheckedW = (decltype(pLoadLibraryCheckedW))GetProcAddress(hMainModule, "LoadLibraryCheckedW");
pLoadLibraryCheckedA = (decltype(pLoadLibraryCheckedA))GetProcAddress(hMainModule, "LoadLibraryCheckedA");
if(pLoadLibraryCheckedW == nullptr || pLoadLibraryCheckedA == nullptr)
return L"Error finding safe library loading functions!";
auto userDirectoryError = InitializeUserDirectory();
if(userDirectoryError != nullptr)
return userDirectoryError;
// Variables used by the macros below
HINSTANCE hInst;
const wchar_t* szLib;
static wchar_t szError[256] = L"";
//GUI Load
LOADLIBRARY(gui_lib);
LOADEXPORT(_gui_guiinit);
LOADEXPORT(_gui_sendmessage);
LOADEXPORT(_gui_translate_text);
auto titanEngineDll = []()
{
switch(DbgGetDebugEngine())
{
case DebugEngineGleeBug:
return L"GleeBug\\TitanEngine.dll";
case DebugEngineStaticEngine:
return L"StaticEngine\\TitanEngine.dll";
default:
return L"TitanEngine.dll";
}
}();
//DBG Load
auto loadIfExists = [&](const wchar_t* szDll)
{
BridgeLoadLibraryCheckedW(szDll, true);
};
// Imported DLLs (DBG)
LOADLIBRARY(titanEngineDll);
LOADLIBRARY(L"LLVMDemangle.dll");
LOADLIBRARY(L"lz4.dll");
LOADLIBRARY(L"jansson.dll");
LOADLIBRARY(L"DeviceNameResolver.dll");
LOADLIBRARY(L"XEDParse.dll");
//loadIfExists(L"asmjit.dll"); // only loaded with user interaction
//loadIfExists(L"Scylla.dll"); // only loaded with user interaction
loadIfExists(L"msdia140.dll"); // only loaded with user interaction
// DBG
LOADLIBRARY(dbg_lib);
LOADEXPORT(_dbg_dbginit);
LOADEXPORT(_dbg_memfindbaseaddr);
@ -173,15 +215,58 @@ BRIDGE_IMPEXP const wchar_t* BridgeInit()
LOADEXPORT(_dbg_dbgcmddirectexec);
LOADEXPORT(_dbg_getbranchdestination);
LOADEXPORT(_dbg_sendmessage);
// Imported DLLs (GUI)
LOADLIBRARY(L"ldconvert.dll");
loadIfExists(L"Qt5Core.dll");
loadIfExists(L"Qt5Gui.dll");
loadIfExists(L"Qt5WinExtras.dll");
loadIfExists(L"Qt5Widgets.dll");
loadIfExists(L"Qt5Network.dll");
loadIfExists(L"platforms\\qwindows.dll");
loadIfExists(L"imageformats\\qgif.dll");
loadIfExists(L"imageformats\\qicns.dll");
loadIfExists(L"imageformats\\qico.dll");
loadIfExists(L"imageformats\\qjpeg.dll");
loadIfExists(L"Qt5Svg.dll");
loadIfExists(L"imageformats\\qsvg.dll");
loadIfExists(L"imageformats\\qtga.dll");
loadIfExists(L"imageformats\\qtiff.dll");
loadIfExists(L"imageformats\\qwbmp.dll");
loadIfExists(L"imageformats\\qwebp.dll");
loadIfExists(L"bearer\\qgenericbearer.dll");
loadIfExists(L"bearer\\qnativewifibearer.dll");
loadIfExists(L"iconengines\\qsvgicon.dll");
//loadIfExists(L"libeay32.dll"); // only loaded with user interaction
//loadIfExists(L"ssleay32.dll"); // only loaded with user interaction
// GUI
LOADLIBRARY(gui_lib);
LOADEXPORT(_gui_guiinit);
LOADEXPORT(_gui_sendmessage);
LOADEXPORT(_gui_translate_text);
// Backwards compatibility
BridgeLoadLibraryCheckedW(L"x64_bridge.dll", true);
BridgeLoadLibraryCheckedW(L"x64_dbg.dll", true);
return 0;
}
BRIDGE_IMPEXP HMODULE WINAPI BridgeLoadLibraryCheckedW(const wchar_t* szDll, bool allowFailure)
{
return pLoadLibraryCheckedW(szDll, allowFailure);
}
BRIDGE_IMPEXP HMODULE WINAPI BridgeLoadLibraryCheckedA(const char* szDll, bool allowFailure)
{
return pLoadLibraryCheckedA(szDll, allowFailure);
}
BRIDGE_IMPEXP const wchar_t* BridgeStart()
{
if(!_dbg_dbginit || !_gui_guiinit)
return L"\"_dbg_dbginit\" || \"_gui_guiinit\" was not loaded yet, call BridgeInit!";
int errorLine = 0;
BridgeSettingRead(&errorLine);
_dbg_sendmessage(DBG_INITIALIZE_LOCKS, nullptr, nullptr); //initialize locks before any other thread than the main thread are started
_gui_guiinit(0, 0); //remove arguments
if(!BridgeSettingFlush())
@ -1208,7 +1293,12 @@ BRIDGE_IMPEXP void DbgGetSymbolInfo(const SYMBOLPTR* symbolptr, SYMBOLINFO* info
BRIDGE_IMPEXP DEBUG_ENGINE DbgGetDebugEngine()
{
return (DEBUG_ENGINE)_dbg_sendmessage(DBG_GET_DEBUG_ENGINE, nullptr, nullptr);
duint setting = DebugEngineTitanEngine;
if(!BridgeSettingGetUint("Engine", "DebugEngine", &setting))
{
BridgeSettingSetUint("Engine", "DebugEngine", setting);
}
return (DEBUG_ENGINE)setting;
}
BRIDGE_IMPEXP const char* GuiTranslateText(const char* Source)

View File

@ -50,6 +50,10 @@ extern "C"
/// <returns>On error it returns a non-null error message.</returns>
BRIDGE_IMPEXP const wchar_t* BridgeInit();
BRIDGE_IMPEXP HMODULE WINAPI BridgeLoadLibraryCheckedW(const wchar_t* szDll, bool allowFailure);
BRIDGE_IMPEXP HMODULE WINAPI BridgeLoadLibraryCheckedA(const char* szDll, bool allowFailure);
/// <summary>
/// Start the bridge.
/// </summary>

View File

@ -642,51 +642,6 @@ static WString escape(WString cmdline)
return cmdline;
}
#include <delayimp.h>
// https://devblogs.microsoft.com/oldnewthing/20170126-00/?p=95265
static FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
if(dliNotify == dliNotePreLoadLibrary && _stricmp(pdli->szDll, "TitanEngine.dll") == 0)
{
String fullPath = szProgramDir;
fullPath += '\\';
switch(DbgGetDebugEngine())
{
case DebugEngineGleeBug:
fullPath += "GleeBug\\TitanEngine.dll";
break;
case DebugEngineStaticEngine:
fullPath += "StaticEngine\\TitanEngine.dll";
break;
case DebugEngineTitanEngine:
default:
return 0;
}
auto hModule = LoadLibraryW(StringUtils::Utf8ToUtf16(fullPath).c_str());
if(hModule)
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Successfully loaded %s!\n"), fullPath.c_str());
}
else
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to load %s, falling back to regular TitanEngine.dll"), fullPath.c_str());
}
return (FARPROC)hModule;
}
return 0;
}
// Visual Studio 2015 Update 3 made this const per default
// https://dev.to/yumetodo/list-of-mscver-and-mscfullver-8nd
#if _MSC_FULL_VER >= 190024210
const
#endif // _MSC_FULL_VER
PfnDliHook __pfnDliNotifyHook2 = delayHook;
extern "C" DLL_EXPORT const char* _dbg_dbginit()
{
if(!EngineCheckStructAlignment(UE_STRUCT_TITAN_ENGINE_CONTEXT, sizeof(TITAN_ENGINE_CONTEXT_t)))

View File

@ -388,7 +388,6 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>LLVMDemangle\LLVMDemangle_x86.lib;ntdll\ntdll_x86.lib;lz4\lz4_x86.lib;jansson\jansson_x86.lib;DeviceNameResolver\DeviceNameResolver_x86.lib;XEDParse\XEDParse_x86.lib;dbghelp\dbghelp_x86.lib;TitanEngine\TitanEngine_x86.lib;ws2_32.lib;psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<DelayLoadDLLs>TitanEngine.dll</DelayLoadDLLs>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@ -438,7 +437,6 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>LLVMDemangle\LLVMDemangle_x64.lib;ntdll\ntdll_x64.lib;lz4\lz4_x64.lib;jansson\jansson_x64.lib;DeviceNameResolver\DeviceNameResolver_x64.lib;XEDParse\XEDParse_x64.lib;dbghelp\dbghelp_x64.lib;TitanEngine\TitanEngine_x64.lib;ws2_32.lib;psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<DelayLoadDLLs>TitanEngine.dll</DelayLoadDLLs>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">

View File

@ -7,6 +7,7 @@
#include <exception>
#include <signal.h>
#include "crashdump.h"
#include "signaturecheck.h"
#define PROCESS_CALLBACK_FILTER_ENABLED 0x1
@ -37,17 +38,14 @@ BOOL
void CrashDumpInitialize()
{
// Get handles to kernel32 and dbghelp
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
HMODULE hDbghelp = LoadLibraryA("dbghelp.dll");
HMODULE hDbghelp = LoadLibraryCheckedW(L"dbghelp.dll", false);
if(hDbghelp)
*(FARPROC*)&MiniDumpWriteDumpPtr = GetProcAddress(hDbghelp, "MiniDumpWriteDump");
if(hKernel32)
{
*(FARPROC*)&SetProcessUserModeExceptionPolicyPtr = GetProcAddress(hKernel32, "SetProcessUserModeExceptionPolicy");
*(FARPROC*)&GetProcessUserModeExceptionPolicyPtr = GetProcAddress(hKernel32, "GetProcessUserModeExceptionPolicy");
}
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
*(FARPROC*)&SetProcessUserModeExceptionPolicyPtr = GetProcAddress(hKernel32, "SetProcessUserModeExceptionPolicy");
*(FARPROC*)&GetProcessUserModeExceptionPolicyPtr = GetProcAddress(hKernel32, "GetProcessUserModeExceptionPolicy");
if(MiniDumpWriteDumpPtr)
SetUnhandledExceptionFilter(CrashDumpExceptionHandler);

View File

@ -1,21 +1,57 @@
#include <Windows.h>
#include "signaturecheck.h"
#include <delayimp.h>
#include <Softpub.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <string>
#include <vector>
static wchar_t szModulePath[MAX_PATH];
static wchar_t szApplicationDir[MAX_PATH];
static bool bPerformSignatureChecks = false;
//#define DEBUG_SIGNATURE_CHECKS
#ifdef DEBUG_SIGNATURE_CHECKS
static const wchar_t* gCheckedList[4096];
static size_t gCheckedListSize = 0;
static void debugMessage(const wchar_t* szMessage)
{
wchar_t finalMessage[512] = L"[signaturecheck] ";
wcsncat_s(finalMessage, szMessage, _TRUNCATE);
OutputDebugStringW(finalMessage);
}
#endif // DEBUG_SIGNATURE_CHECKS
// TODO: look at hijacking of wintrust (also old vulnerabilities)
// - https://www.bleepingcomputer.com/news/security/microsoft-code-sign-check-bypassed-to-drop-zloader-malware/
// - https://www.trustedsec.com/blog/object-overloading/
#pragma comment (lib, "wintrust")
// MSASN1.dll
// CRYPTSP.dll
// CRYPTBASE.dll
// other:
// cryptnet.dll
// iphlpapi.dll
// profapi.dll
// wininet.dll
// winmm.dll
// opengl32.dll
// glu32.dll
// dnsapi.dll
// mpr.dll
// wldp.dll
// wtsapi32.dll
// XP: rsaenh.dll
// XP: psapi.dll
// XP: WS2_32.dll
// XP: WS2HELP.dll
// XP: DNSAPI.dll
#pragma comment(lib, "wintrust")
// Source: https://learn.microsoft.com/en-us/windows/win32/seccrypto/example-c-program--verifying-the-signature-of-a-pe-file
bool VerifyEmbeddedSignature(LPCWSTR pwszSourceFile, bool checkRevocation = true)
static bool VerifyEmbeddedSignature(LPCWSTR pwszSourceFile, bool checkRevocation)
{
LONG lStatus;
DWORD dwLastError;
@ -184,11 +220,12 @@ bool VerifyEmbeddedSignature(LPCWSTR pwszSourceFile, bool checkRevocation = true
// Any hWVTStateData must be released by a call with close.
WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE;
lStatus = WinVerifyTrust(
NULL,
&WVTPolicyGUID,
&WinTrustData);
auto lStatusClean = WinVerifyTrust(
NULL,
&WVTPolicyGUID,
&WinTrustData);
SetLastError(lStatus);
return validSignature;
}
@ -207,32 +244,77 @@ static std::wstring Utf8ToUtf16(const char* str)
return convertedString;
}
static bool FileExists(const wchar_t* szFullPath)
{
DWORD attrib = GetFileAttributesW(szFullPath);
return (attrib != INVALID_FILE_ATTRIBUTES && !(attrib & FILE_ATTRIBUTE_DIRECTORY));
}
SIGNATURE_EXPORT HMODULE LoadLibraryCheckedW(const wchar_t* szDll, bool allowFailure)
{
std::wstring fullDllPath = szApplicationDir;
fullDllPath += szDll;
#ifdef DEBUG_SIGNATURE_CHECKS
debugMessage(L"LoadLibraryCheckedW");
debugMessage(fullDllPath.c_str());
auto checkedPath = (wchar_t*)malloc(sizeof(wchar_t) * (fullDllPath.size() + 1));
wcscpy_s(checkedPath, fullDllPath.size() + 1, fullDllPath.c_str());
gCheckedList[gCheckedListSize++] = checkedPath;
#endif // DEBUG_SIGNATURE_CHECKS
if(allowFailure && !FileExists(fullDllPath.c_str()))
{
#ifdef DEBUG_SIGNATURE_CHECKS
debugMessage(L"^^^ DLL NOT FOUND ^^^");
#endif // DEBUG_SIGNATURE_CHECKS
SetLastError(ERROR_MOD_NOT_FOUND);
return 0;
}
if(bPerformSignatureChecks)
{
auto validSignature = VerifyEmbeddedSignature(fullDllPath.c_str(), false);
if(!validSignature)
{
#ifdef DEBUG_SIGNATURE_CHECKS
wchar_t msg[128] = L"";
wsprintfW(msg, L"^^^ INVALID SIGNATURE ^^^ -> %08X", GetLastError());
debugMessage(msg);
#else
MessageBoxW(nullptr, fullDllPath.c_str(), L"DLL does not have a valid signature", MB_ICONERROR | MB_SYSTEMMODAL);
ExitProcess(TRUST_E_NOSIGNATURE);
#endif // DEBUG_SIGNATURE_CHECKS
}
}
auto hModule = LoadLibraryW(fullDllPath.c_str());
auto lastError = GetLastError();
if(!allowFailure && !hModule)
{
MessageBoxW(nullptr, fullDllPath.c_str(), L"DLL not found!", MB_ICONERROR | MB_SYSTEMMODAL);
ExitProcess(lastError);
}
return hModule;
}
SIGNATURE_EXPORT HMODULE LoadLibraryCheckedA(const char* szDll, bool allowFailure)
{
return LoadLibraryCheckedW(Utf8ToUtf16(szDll).c_str(), allowFailure);
}
// https://devblogs.microsoft.com/oldnewthing/20170126-00/?p=95265
static FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
// TODO: pre-parse the imports required and make sure there isn't a fake DLL being loaded
// TODO: pre-parse the imports required and make sure there isn't a fake signed DLL being loaded
if(dliNotify == dliNotePreLoadLibrary)
{
std::wstring fullDllPath = szModulePath;
fullDllPath += L'\\';
fullDllPath += Utf8ToUtf16(pdli->szDll);
MessageBoxW(nullptr, fullDllPath.c_str(), L"dliNotePreLoadLibrary", MB_ICONERROR);
if(bPerformSignatureChecks)
if(_stricmp(pdli->szDll, "wintrust.dll") == 0 || _stricmp(pdli->szDll, "user32.dll") == 0)
{
auto validSignature = VerifyEmbeddedSignature(fullDllPath.c_str());
if(!validSignature)
{
MessageBoxW(nullptr, fullDllPath.c_str(), L"DLL does not have a valid signature", MB_ICONERROR | MB_SYSTEMMODAL);
ExitProcess(TRUST_E_NOSIGNATURE);
}
return 0;
}
auto hModule = ::LoadLibraryW(fullDllPath.c_str());
if(!hModule)
{
MessageBoxW(nullptr, fullDllPath.c_str(), L"DLL not found!", MB_ICONERROR | MB_SYSTEMMODAL);
ExitProcess(ERROR_MOD_NOT_FOUND);
}
return (FARPROC)hModule;
return (FARPROC)LoadLibraryCheckedA(pdli->szDll, false);
}
return 0;
}
@ -244,17 +326,206 @@ const
#endif // _MSC_FULL_VER
PfnDliHook __pfnDliNotifyHook2 = delayHook;
#ifdef DEBUG_SIGNATURE_CHECKS
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA
{
ULONG Flags; //Reserved.
PUNICODE_STRING FullDllName; //The full path name of the DLL module.
PUNICODE_STRING BaseDllName; //The base file name of the DLL module.
PVOID DllBase; //A pointer to the base address for the DLL in memory.
ULONG SizeOfImage; //The size of the DLL image, in bytes.
} LDR_DLL_UNLOADED_NOTIFICATION_DATA, * PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA
{
ULONG Flags; //Reserved.
PUNICODE_STRING FullDllName; //The full path name of the DLL module.
PUNICODE_STRING BaseDllName; //The base file name of the DLL module.
PVOID DllBase; //A pointer to the base address for the DLL in memory.
ULONG SizeOfImage; //The size of the DLL image, in bytes.
} LDR_DLL_LOADED_NOTIFICATION_DATA, * PLDR_DLL_LOADED_NOTIFICATION_DATA;
typedef union _LDR_DLL_NOTIFICATION_DATA
{
LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
} LDR_DLL_NOTIFICATION_DATA, * PLDR_DLL_NOTIFICATION_DATA, * const PCLDR_DLL_NOTIFICATION_DATA;
#define LDR_DLL_NOTIFICATION_REASON_LOADED 1
#define LDR_DLL_NOTIFICATION_REASON_UNLOADED 2
VOID CALLBACK LdrDllNotification(
_In_ ULONG NotificationReason,
_In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData,
_In_opt_ PVOID Context
);
using PLDR_DLL_NOTIFICATION_FUNCTION = decltype(&LdrDllNotification);
NTSTATUS NTAPI LdrRegisterDllNotification(
_In_ ULONG Flags,
_In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
_In_opt_ PVOID Context,
_Out_ PVOID* Cookie
);
using PLDR_REGISTER_DLL_NOTIFICATION_FUNCTION = decltype(&LdrRegisterDllNotification);
PLDR_REGISTER_DLL_NOTIFICATION_FUNCTION pLdrRegisterDllNotification;
static VOID CALLBACK MyLdrDllNotification(
_In_ ULONG NotificationReason,
_In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData,
_In_opt_ PVOID Context
)
{
if(NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED)
{
auto buffer = NotificationData->Loaded.FullDllName->Buffer;
auto length = NotificationData->Loaded.FullDllName->Length;
if(memcmp(buffer, szApplicationDir, wcslen(szApplicationDir)) == 0)
{
auto alreadyChecked = false;
for(size_t i = 0; i < gCheckedListSize; i++)
{
auto checkedPath = gCheckedList[i];
auto checkedLen = wcslen(checkedPath);
if(length / 2 == checkedLen && memcmp(checkedPath, buffer, length) == 0)
{
alreadyChecked = true;
break;
}
}
if(alreadyChecked)
{
debugMessage(L"DllLoadNotification (checked)");
}
else
{
if(wcsstr(buffer, L"\\plugins\\"))
debugMessage(L"DllLoadNotification(plugin)");
else
debugMessage(L"=== UNCHECKED === DllLoadNotification");
}
debugMessage(buffer);
}
}
}
#endif // DEBUG_SIGNATURE_CHECKS
#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
#define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200
#define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400
#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
typedef BOOL(WINAPI* pfnSetDefaultDllDirectories)(DWORD DirectoryFlags);
typedef BOOL(WINAPI* pfnSetDllDirectoryW)(LPCWSTR lpPathName);
typedef BOOL(WINAPI* pfnAddDllDirectory)(LPCWSTR lpPathName);
static pfnSetDefaultDllDirectories pSetDefaultDllDirectories;
static pfnSetDllDirectoryW pSetDllDirectoryW;
static pfnAddDllDirectory pAddDllDirectory;
bool InitializeSignatureCheck()
{
if(!GetModuleFileNameW(GetModuleHandleW(nullptr), szModulePath, _countof(szModulePath)))
if(!GetModuleFileNameW(GetModuleHandleW(nullptr), szApplicationDir, _countof(szApplicationDir)))
return false;
bPerformSignatureChecks = VerifyEmbeddedSignature(szModulePath);
bPerformSignatureChecks = true; // TODO: remove this
auto ptr = wcsrchr(szModulePath, L'\\');
std::wstring executablePath = szApplicationDir;
auto ptr = wcsrchr(szApplicationDir, L'\\');
if(ptr == nullptr)
return false;
*ptr = L'\0';
// TODO: check signature
// https://learn.microsoft.com/en-us/windows/win32/seccrypto/example-c-program--verifying-the-signature-of-a-pe-file
ptr[1] = L'\0';
// Get system directory
wchar_t szSystemDir[MAX_PATH] = L"";
GetSystemDirectoryW(szSystemDir, _countof(szSystemDir));
// This is generically fixing DLL hijacking by using the following search order
// - System directory
// - Application directory
// References:
// - https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories
// - https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-adddlldirectory
// - https://medium.com/@1ndahous3/safe-code-pitfalls-dll-side-loading-winapi-and-c-73baaf48bdf5
HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll");
pSetDefaultDllDirectories = (pfnSetDefaultDllDirectories)GetProcAddress(hKernel32, "SetDefaultDllDirectories");
pSetDllDirectoryW = (pfnSetDllDirectoryW)GetProcAddress(hKernel32, "SetDllDirectoryW");
pAddDllDirectory = (pfnAddDllDirectory)GetProcAddress(hKernel32, "AddDllDirectory");
if(pSetDefaultDllDirectories && pSetDllDirectoryW && pAddDllDirectory)
{
if(!pSetDllDirectoryW(L""))
return false;
// Thanks to daax for finding this order
// > If more than one directory has been added, the order in which those directories are searched is unspecified.
// Reversing ntdll shows that it is using a singly-linked list, so the order is the opposite in which you add it
// Wine: https://github.com/wine-mirror/wine/blob/e796002ee61bf5dfb2718e8f4fb8fa928ccdc236/dlls/ntdll/loader.c#L4423-L4462
if(!pAddDllDirectory(szApplicationDir))
return false;
if(!pAddDllDirectory(szSystemDir))
return false;
if(!pSetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS))
return false;
}
else
{
auto loadSystemDll = [&szSystemDir](const wchar_t* szName)
{
std::wstring fullDllPath;
fullDllPath = szSystemDir;
fullDllPath += L'\\';
fullDllPath += szName;
LoadLibraryW(fullDllPath.c_str());
};
// At least prevent DLL hijacking for wintrust.dll on XP/Old 7
loadSystemDll(L"msasn1.dll");
loadSystemDll(L"cryptsp.dll");
loadSystemDll(L"cryptbase.dll");
loadSystemDll(L"wintrust.dll");
loadSystemDll(L"rsaenh.dll");
loadSystemDll(L"psapi.dll");
}
bPerformSignatureChecks = VerifyEmbeddedSignature(executablePath.c_str(), false);
#ifdef DEBUG_SIGNATURE_CHECKS
bPerformSignatureChecks = true;
pLdrRegisterDllNotification = (PLDR_REGISTER_DLL_NOTIFICATION_FUNCTION)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "LdrRegisterDllNotification");
if(pLdrRegisterDllNotification == nullptr)
return true;
PVOID Cookie;
auto status = pLdrRegisterDllNotification(0, MyLdrDllNotification, nullptr, &Cookie);
if(status != 0)
return false;
#endif // DEBUG_SIGNATURE_CHECKS
if(bPerformSignatureChecks)
{
// Safely load the MSVC runtime DLLs (since they cannot be delay loaded)
auto loadRuntimeDll = [](const wchar_t* szDll)
{
std::wstring fullDllPath = szApplicationDir;
fullDllPath += L'\\';
fullDllPath += szDll;
if(FileExists(fullDllPath.c_str()))
LoadLibraryCheckedW(szDll, true);
else
LoadLibraryW(szDll);
};
loadRuntimeDll(L"msvcr120.dll");
loadRuntimeDll(L"msvcp120.dll");
}
return true;
}

View File

@ -1,3 +1,9 @@
#pragma once
bool InitializeSignatureCheck();
#include <Windows.h>
#define SIGNATURE_EXPORT extern "C" __declspec(dllexport)
bool InitializeSignatureCheck();
SIGNATURE_EXPORT HMODULE LoadLibraryCheckedW(const wchar_t* szDll, bool allowFailure);
SIGNATURE_EXPORT HMODULE LoadLibraryCheckedA(const char* szDll, bool allowFailure);

View File

@ -140,7 +140,7 @@
<AdditionalManifestDependencies>
</AdditionalManifestDependencies>
<LargeAddressAware>true</LargeAddressAware>
<DelayLoadDLLs>x32bridge.dll</DelayLoadDLLs>
<DelayLoadDLLs>user32.dll;wintrust.dll;x32bridge.dll</DelayLoadDLLs>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@ -176,7 +176,7 @@
<ProgramDatabaseFile>$(TargetDir)$(TargetName)_exe.pdb</ProgramDatabaseFile>
<AdditionalManifestDependencies>
</AdditionalManifestDependencies>
<DelayLoadDLLs>x64bridge.dll</DelayLoadDLLs>
<DelayLoadDLLs>user32.dll;wintrust.dll;x64bridge.dll</DelayLoadDLLs>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">

View File

@ -292,7 +292,7 @@ static void AddDBFileTypeIcon(TCHAR* sz32Path, TCHAR* sz64Path)
return;
}
static TCHAR szModulePath[MAX_PATH] = TEXT("");
static TCHAR szApplicationDir[MAX_PATH] = TEXT("");
static TCHAR szCurrentDir[MAX_PATH] = TEXT("");
static TCHAR sz32Path[MAX_PATH] = TEXT("");
static TCHAR sz32Dir[MAX_PATH] = TEXT("");
@ -306,7 +306,7 @@ static void restartInstall()
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
auto operation = osvi.dwMajorVersion >= 6 ? TEXT("runas") : TEXT("open");
ShellExecute(nullptr, operation, szModulePath, TEXT("::install"), szCurrentDir, SW_SHOWNORMAL);
ShellExecute(nullptr, operation, szApplicationDir, TEXT("::install"), szCurrentDir, SW_SHOWNORMAL);
}
static BOOL CALLBACK DlgLauncher(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
@ -429,14 +429,14 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
CoInitialize(nullptr);
//Get INI file path
if(!GetModuleFileName(nullptr, szModulePath, MAX_PATH))
if(!GetModuleFileName(nullptr, szApplicationDir, MAX_PATH))
{
MessageBox(nullptr, LoadResString(IDS_ERRORGETTINGMODULEPATH), LoadResString(IDS_ERROR), MB_ICONERROR | MB_SYSTEMMODAL);
return 0;
}
TCHAR szIniPath[MAX_PATH] = TEXT("");
_tcscpy_s(szIniPath, szModulePath);
_tcscpy_s(szCurrentDir, szModulePath);
_tcscpy_s(szIniPath, szApplicationDir);
_tcscpy_s(szCurrentDir, szApplicationDir);
auto len = int(_tcslen(szCurrentDir));
while(szCurrentDir[len] != TEXT('\\') && len)
len--;
@ -585,9 +585,9 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
if(MessageBox(nullptr, LoadResString(IDS_ASKSHELLEXT), LoadResString(IDS_QUESTION), MB_YESNO | MB_ICONQUESTION) == IDYES)
{
TCHAR szLauncherCommand[MAX_PATH] = TEXT("");
_stprintf_s(szLauncherCommand, _countof(szLauncherCommand), TEXT("\"%s\" \"%%1\""), szModulePath);
_stprintf_s(szLauncherCommand, _countof(szLauncherCommand), TEXT("\"%s\" \"%%1\""), szApplicationDir);
TCHAR szIconCommand[MAX_PATH] = TEXT("");
_stprintf_s(szIconCommand, _countof(szIconCommand), TEXT("\"%s\",0"), szModulePath);
_stprintf_s(szIconCommand, _countof(szIconCommand), TEXT("\"%s\",0"), szApplicationDir);
if(RegisterShellExtension(SHELLEXT_EXE_KEY, szLauncherCommand))
AddShellIcon(SHELLEXT_ICON_EXE_KEY, szIconCommand, LoadResString(IDS_SHELLEXTDBG));
if(RegisterShellExtension(SHELLEXT_DLL_KEY, szLauncherCommand))