From 70e869dcdb4f9637b8e08ca0e0d8744c8169e58e Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Sat, 23 May 2020 22:55:44 +0200 Subject: [PATCH] add sudo utility --- .gitignore | 5 ++ FunUtils.sln | 6 ++ sudo/resource.h | 17 +++++ sudo/resource.rc | 117 +++++++++++++++++++++++++++++ sudo/sudo.cpp | 153 ++++++++++++++++++++++++++++++++++++++ sudo/sudo.vcxproj | 96 ++++++++++++++++++++++++ sudo/sudo.vcxproj.filters | 37 +++++++++ sudo/uac.ico | Bin 0 -> 49100 bytes 8 files changed, 431 insertions(+) create mode 100644 sudo/resource.h create mode 100644 sudo/resource.rc create mode 100644 sudo/sudo.cpp create mode 100644 sudo/sudo.vcxproj create mode 100644 sudo/sudo.vcxproj.filters create mode 100644 sudo/uac.ico diff --git a/.gitignore b/.gitignore index 07b26ed..e8259e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ .vs/ bin/ obj/ +Release/ +Debug/ +x64/ +*.aps +*.vcxproj.user diff --git a/FunUtils.sln b/FunUtils.sln index c1166d9..5d29e20 100644 --- a/FunUtils.sln +++ b/FunUtils.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DirBrowser", "DirBrowser\Di EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "separate-console", "separate-console\separate-console.csproj", "{4A112556-38F8-473B-9BA1-A8FA58338FD9}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sudo", "sudo\sudo.vcxproj", "{98578377-F8C7-4851-B746-11F7243AAAFA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {4A112556-38F8-473B-9BA1-A8FA58338FD9}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A112556-38F8-473B-9BA1-A8FA58338FD9}.Release|Any CPU.ActiveCfg = Release|Any CPU {4A112556-38F8-473B-9BA1-A8FA58338FD9}.Release|Any CPU.Build.0 = Release|Any CPU + {98578377-F8C7-4851-B746-11F7243AAAFA}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {98578377-F8C7-4851-B746-11F7243AAAFA}.Debug|Any CPU.Build.0 = Debug|Win32 + {98578377-F8C7-4851-B746-11F7243AAAFA}.Release|Any CPU.ActiveCfg = Release|Win32 + {98578377-F8C7-4851-B746-11F7243AAAFA}.Release|Any CPU.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sudo/resource.h b/sudo/resource.h new file mode 100644 index 0000000..3f027e8 --- /dev/null +++ b/sudo/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resource.rc +// +#define IDI_ICON1 107 +#define IDC_BUTTON3 1003 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1002 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/sudo/resource.rc b/sudo/resource.rc new file mode 100644 index 0000000..1c5890e --- /dev/null +++ b/sudo/resource.rc @@ -0,0 +1,117 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "sudo" + VALUE "FileVersion", "1.0.0.0" + VALUE "LegalCopyright", "mrexodia.re" + VALUE "ProductName", "sudo" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // Neutral resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Neutral (Default) (unknown sub-lang: 0x8) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ZZZ) +LANGUAGE LANG_NEUTRAL, 0x8 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "uac.ico" + +#endif // Neutral (Default) (unknown sub-lang: 0x8) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/sudo/sudo.cpp b/sudo/sudo.cpp new file mode 100644 index 0000000..7029470 --- /dev/null +++ b/sudo/sudo.cpp @@ -0,0 +1,153 @@ +#include +#include +#include + +static bool IsProcessElevated() +{ + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID SecurityIdentifier; + if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &SecurityIdentifier)) + return 0; + + BOOL IsAdminMember; + if (!CheckTokenMembership(NULL, SecurityIdentifier, &IsAdminMember)) + IsAdminMember = FALSE; + + FreeSid(SecurityIdentifier); + return !!IsAdminMember; +} + +class RedirectWow +{ + PVOID oldValue = NULL; + BOOL(WINAPI* wow64DisableRedirection)(PVOID); + BOOL(WINAPI* wow64RevertRedirection)(PVOID); + +public: + RedirectWow() + { + wow64DisableRedirection = (decltype(wow64DisableRedirection))GetProcAddress(GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection"); + wow64RevertRedirection = (decltype(wow64RevertRedirection))GetProcAddress(GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection"); + if (wow64DisableRedirection) + wow64DisableRedirection(&oldValue); + } + + RedirectWow(const RedirectWow&) = delete; + RedirectWow(RedirectWow&&) = delete; + RedirectWow& operator=(const RedirectWow&) = delete; + + ~RedirectWow() + { + if (oldValue) + wow64RevertRedirection(oldValue); + } +}; + +int wmain(int argc, wchar_t* argv[]) +{ + RedirectWow redirectWow; + + // Helper functions for functioning printf + auto printf = [](const char* fmt, auto... args) + { + char msg[1024] = ""; + sprintf_s(msg, fmt, args...); + DWORD w = 0; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msg, strlen(msg), &w, nullptr); + }; + auto puts = [&printf](const char* str) + { + printf("%s\n", str); + }; + + // Get rid of application from command line + auto commandLine = GetCommandLineW(); + { + if (*commandLine == L'\"') + { + commandLine++; + while (*commandLine++ != L'\"'); + commandLine++; + } + else + { + while (*commandLine != L' ' && *commandLine != L'\0') + commandLine++; + } + while (*commandLine == L' ') + commandLine++; + if (!*commandLine) + commandLine = nullptr; + } + + if (!IsProcessElevated()) + { + // Restart sudo elevated + SHELLEXECUTEINFOW ShExecInfo = { 0 }; + ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW); + ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + ShExecInfo.hwnd = GetConsoleWindow(); + ShExecInfo.lpVerb = L"runas"; + ShExecInfo.lpFile = argv[0]; + ShExecInfo.lpParameters = commandLine; + ShExecInfo.lpDirectory = NULL; + ShExecInfo.nShow = SW_HIDE; + ShExecInfo.hInstApp = NULL; + if (!ShellExecuteExW(&ShExecInfo)) + { + auto lastError = GetLastError(); + if (lastError == ERROR_CANCELLED) + puts("Elevation canceled by user"); + else + printf("ShellExecuteExW failed (LastError: %u)\n", GetLastError()); + return EXIT_FAILURE; + } + WaitForSingleObject(ShExecInfo.hProcess, INFINITE); + DWORD ExitCode = 0; + if (!GetExitCodeProcess(ShExecInfo.hProcess, &ExitCode)) + ExitCode = 1; + CloseHandle(ShExecInfo.hProcess); + return ExitCode; + } + else + { + // Get rid of the hidden console and attach to the parent sudo's console + FreeConsole(); + AttachConsole(ATTACH_PARENT_PROCESS); + + // Construct final command line for cmd + std::vector finalCommandLine; + auto append = [&finalCommandLine](const wchar_t* str) + { + auto length = wcslen(str); + for (size_t i = 0; i < length; i++) + finalCommandLine.push_back(str[i]); + }; + if (commandLine) + { + append(L"cmd.exe /C "); + append(commandLine); + } + else + { + append(L"cmd.exe"); + } + finalCommandLine.push_back(L'\0'); + + // Create cmd instance + STARTUPINFOW si = { sizeof(si) }; + PROCESS_INFORMATION pi = { 0 }; + if (!CreateProcessW(nullptr, finalCommandLine.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) + { + printf("CreateProcessW failed (LastError: %u)\n", GetLastError()); + return EXIT_FAILURE; + } + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD ExitCode = 0; + if (!GetExitCodeProcess(pi.hProcess, &ExitCode)) + ExitCode = 1; + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return ExitCode; + } +} \ No newline at end of file diff --git a/sudo/sudo.vcxproj b/sudo/sudo.vcxproj new file mode 100644 index 0000000..7edbc53 --- /dev/null +++ b/sudo/sudo.vcxproj @@ -0,0 +1,96 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + 16.0 + Win32Proj + {98578377-f8c7-4851-b746-11f7243aaafa} + sudo + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sudo/sudo.vcxproj.filters b/sudo/sudo.vcxproj.filters new file mode 100644 index 0000000..5b3fb0d --- /dev/null +++ b/sudo/sudo.vcxproj.filters @@ -0,0 +1,37 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/sudo/uac.ico b/sudo/uac.ico new file mode 100644 index 0000000000000000000000000000000000000000..14e21e18bf606bb2a8d326ef08662e97b09eeca8 GIT binary patch literal 49100 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

pyFgwHxvUAF3D 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
&n@)nTme@ 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