Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ghost5683/06de345a5adbf164fb764942866afc24 to your computer and use it in GitHub Desktop.
Save ghost5683/06de345a5adbf164fb764942866afc24 to your computer and use it in GitHub Desktop.
This C++ code example is part of the White Knight Labs Offensive Development Course materials. A straightforward C++ code snippet demonstrating how to prevent DLL sideloading by validating the calling executable. It uses a whitelist approach to ensure only specified executables can load the DLL.
#include <windows.h>
#include <string>
#include <vector>
#include <algorithm>
// White Knight Labs - Offensive Development Course
// DLL Guardrails Example
// This function extracts the file name from a given path
// It is used later to determine the executable name loading the DLL.
std::string ExtractFileName(const std::string& path) {
size_t lastSlashPos = path.find_last_of("\\/");
return lastSlashPos != std::string::npos ? path.substr(lastSlashPos + 1) : path;
}
// Entry point for the DLL
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved) {
CHAR processPath[MAX_PATH] = { 0 };
// List of executables allowed to load this DLL
std::vector<std::string> allowedExecutables = {
"Word.exe", // Example allowed executable
"Chrome.exe", // Another example allowed executable
"Caller.exe" // Placeholder for additional executables
// Add any other executable names as needed
};
// Retrieve the full path of the process trying to load the DLL
GetModuleFileNameA(NULL, processPath, MAX_PATH);
std::string executableName = ExtractFileName(processPath);
// Determine the reason for calling DllMain and act accordingly
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
// Check if the executable is not in the list of allowed executables
if (std::find(allowedExecutables.begin(), allowedExecutables.end(), executableName) == allowedExecutables.end()) {
// Prevent DLL from being loaded by an unknown or unauthorized executable
return FALSE;
}
// Break is important to prevent fall-through to other cases
break;
case DLL_THREAD_ATTACH:
// Code to run when a new thread is created within the process
break;
case DLL_THREAD_DETACH:
// Code to run when a thread within the process ends cleanly
break;
case DLL_PROCESS_DETACH:
// Code to run when the DLL is unloaded from a process
break;
}
// Allow the DLL to load if no conditions are violated
return TRUE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment