diff options
-rw-r--r-- | CMakeLists.txt | 10 | ||||
-rw-r--r-- | LICENSE.txt | 21 | ||||
-rw-r--r-- | error.h | 26 | ||||
-rw-r--r-- | handle.h | 64 | ||||
-rw-r--r-- | main.cpp | 183 | ||||
-rw-r--r-- | main.rc | bin | 0 -> 5844 bytes | |||
-rw-r--r-- | os.h | 30 | ||||
-rw-r--r-- | process.h | 40 | ||||
-rw-r--r-- | resource_ids.h | bin | 0 -> 1528 bytes | |||
-rw-r--r-- | sid.h | 68 | ||||
-rw-r--r-- | token.h | 175 |
11 files changed, 617 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..aff5426 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +project(privilege_test) + +file(GLOB ${PROJECT_NAME}_source_files "*.cpp") +file(GLOB ${PROJECT_NAME}_header_files "*.h") +file(GLOB ${PROJECT_NAME}_resource_files "*.rc") +add_executable(${PROJECT_NAME} WIN32 + ${${PROJECT_NAME}_source_files} + ${${PROJECT_NAME}_header_files} + ${${PROJECT_NAME}_resource_files}) +target_compile_definitions(${PROJECT_NAME} PRIVATE _UNICODE) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..80c5e0a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. @@ -0,0 +1,26 @@ +#pragma once + +#include <Windows.h> + +#include <system_error> + +typedef std::system_error Error; + +namespace error +{ + inline void raise(const char* function_name) + { + const auto ec = GetLastError(); + throw std::system_error(ec, std::system_category(), function_name); + } + + void report(const Error& e) + { + MessageBoxA(NULL, e.what(), NULL, MB_OK); + } + + int get_code(const Error& e) + { + return e.code().value(); + } +} diff --git a/handle.h b/handle.h new file mode 100644 index 0000000..5f83f24 --- /dev/null +++ b/handle.h @@ -0,0 +1,64 @@ +#pragma once + +#include <Windows.h> + +#include <cassert> + +#include <memory> +#include <utility> + +class Handle +{ +public: + Handle() = default; + + explicit Handle(HANDLE raw) + : impl{raw} + { } + + Handle(Handle&& other) noexcept + { + swap(other); + } + + Handle& operator=(Handle other) noexcept + { + swap(other); + return *this; + } + + void swap(Handle& other) noexcept + { + using std::swap; + swap(impl, other.impl); + } + + operator HANDLE() const + { + return impl.get(); + } + +private: + struct Close + { + void operator()(HANDLE raw) const + { + if (raw == NULL || raw == INVALID_HANDLE_VALUE) + return; + const auto ret = CloseHandle(raw); + assert(ret); + } + }; + + std::unique_ptr<void, Close> impl; + + Handle(const Handle&) = delete; +}; + +namespace std +{ + void swap(Handle& a, Handle& b) noexcept + { + a.swap(b); + } +} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..322626c --- /dev/null +++ b/main.cpp @@ -0,0 +1,183 @@ +#include "error.h" +#include "os.h" +#include "process.h" +#include "resource_ids.h" +#include "sid.h" +#include "token.h" + +#include <Windows.h> +#include <CommCtrl.h> +#include <windowsx.h> + +#include <string> + +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +bool is_user_in_administrators() +{ + const auto token = token::impersonate( + token::get_linked( + token::open_for_current_process(token::permissions::query() | token::permissions::duplicate()))); + + return token::belongs(token, sid::builtin_administrators()); +} + +bool is_run_as_administrator() +{ + return token::belongs(token::dumb(), sid::builtin_administrators()); +} + +bool is_elevated() +{ + return token::is_elevated(token::open_for_current_process()); +} + +void set_label(HWND root, int id, bool val) +{ + const auto label = GetDlgItem(root, id); + SetWindowTextW(label, val ? L"True" : L"False"); +} + +void set_label(HWND root, int id, const std::wstring& s) +{ + const auto label = GetDlgItem(root, id); + SetWindowTextW(label, s.c_str()); +} + +BOOL on_init_dialog(HWND wnd, HWND, LPARAM) +{ + try + { + set_label(wnd, IDC_STATIC_IS_ADMIN, is_user_in_administrators()); + } + catch (const Error& e) + { + set_label(wnd, IDC_STATIC_IS_ADMIN, L"N/A"); + error::report(e); + } + + try + { + set_label(wnd, IDC_STATIC_IS_RUN_AS_ADMIN, is_run_as_administrator()); + } + catch (const Error& e) + { + set_label(wnd, IDC_STATIC_IS_RUN_AS_ADMIN, L"N/A"); + error::report(e); + } + + if (os::is_vista_or_later()) + { + try + { + const auto elevated = is_elevated(); + set_label(wnd, IDC_STATIC_IS_ELEVATED, elevated); + + const auto elevate_button = GetDlgItem(wnd, IDC_BUTTON_ELEVATE); + Button_SetElevationRequiredState(elevate_button, !elevated); + } + catch (const Error& e) + { + set_label(wnd, IDC_STATIC_IS_ELEVATED, L"N/A"); + error::report(e); + } + + try + { + set_label(wnd, IDC_STATIC_INTEGRITY_LEVEL, token::integrity_level_to_string( + token::query_integrity_level(token::open_for_current_process()))); + } + catch (const Error& e) + { + set_label(wnd, IDC_STATIC_INTEGRITY_LEVEL, L"N/A"); + error::report(e); + } + } + else + { + set_label(wnd, IDC_STATIC_IS_ELEVATED, L"N/A"); + set_label(wnd, IDC_STATIC_INTEGRITY_LEVEL, L"N/A"); + } + + return TRUE; +} + +void on_command(HWND wnd, int id, HWND, unsigned int) +{ + switch (id) + { + case IDC_BUTTON_ELEVATE: + { + bool as_admin = false; + try + { + as_admin = is_run_as_administrator(); + } + catch (const Error& e) + { + error::report(e); + break; + } + + if (as_admin) + { + MessageBoxW(wnd, L"Already elevated!", L"Elevation", MB_OK); + break; + } + + try + { + process::runas(process::get_executable_path()); + } + catch (const Error& e) + { + if (error::get_code(e) != ERROR_CANCELLED) + error::report(e); + break; + } + + EndDialog(wnd, 1); + break; + } + + case IDOK: + case IDCANCEL: + EndDialog(wnd, 0); + break; + } +} + +void on_close(HWND wnd) +{ + EndDialog(wnd, 0); +} + +INT_PTR CALLBACK dialog_main( + HWND wnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) +{ + switch (msg) + { + HANDLE_MSG(wnd, WM_INITDIALOG, on_init_dialog); + HANDLE_MSG(wnd, WM_COMMAND, on_command); + HANDLE_MSG(wnd, WM_CLOSE, on_close); + + default: + return FALSE; + } +} + +int APIENTRY wWinMain( + HINSTANCE instance, + HINSTANCE, + wchar_t*, + int) +{ + return static_cast<int>(DialogBox( + instance, + MAKEINTRESOURCE(IDD_MAINDIALOG), + NULL, + dialog_main)); +} Binary files differ@@ -0,0 +1,30 @@ +#pragma once + +#include "error.h" + +#include <Windows.h> + +namespace os +{ + OSVERSIONINFOW get_version_info() + { + OSVERSIONINFOW info; + ZeroMemory(&info, sizeof(info)); + info.dwOSVersionInfoSize = sizeof(info); + + if (!GetVersionExW(&info)) + error::raise("GetVersionExW"); + + return info; + } + + bool is_vista_or_later(const OSVERSIONINFOW& info) + { + return info.dwMajorVersion >= 6; + } + + bool is_vista_or_later() + { + return is_vista_or_later(get_version_info()); + } +} diff --git a/process.h b/process.h new file mode 100644 index 0000000..9aaf319 --- /dev/null +++ b/process.h @@ -0,0 +1,40 @@ +#pragma once + +#include "error.h" + +#include <Windows.h> +#include <shellapi.h> + +#include <array> +#include <string> + +namespace process +{ + std::wstring get_executable_path() + { + static constexpr DWORD max_path = MAX_PATH; + + std::array<wchar_t, max_path> buf; + + const auto ret = GetModuleFileNameW(NULL, buf.data(), max_path); + + if (GetLastError() != ERROR_SUCCESS) + error::raise("GetModuleFileNameW"); + + return buf.data(); + } + + void runas(const std::wstring& exe_path, HWND hwnd = NULL, int nShow = SW_NORMAL) + { + SHELLEXECUTEINFOW info; + ZeroMemory(&info, sizeof(info)); + info.cbSize = sizeof(info); + info.lpVerb = L"runas"; + info.lpFile = exe_path.c_str(); + info.hwnd = hwnd; + info.nShow = nShow; + + if (!ShellExecuteExW(&info)) + error::raise("ShellExecuteExW"); + } +} diff --git a/resource_ids.h b/resource_ids.h Binary files differnew file mode 100644 index 0000000..54718a8 --- /dev/null +++ b/resource_ids.h @@ -0,0 +1,68 @@ +#pragma once + +#include "error.h" + +#include <Windows.h> +#include <sddl.h> + +#include <array> +#include <memory> +#include <string> + +constexpr DWORD max_sid_size = SECURITY_MAX_SID_SIZE; +typedef std::array<unsigned char, max_sid_size> SidBuffer; + +namespace sid +{ + SidBuffer well_known(WELL_KNOWN_SID_TYPE type) + { + SidBuffer buffer; + DWORD cb = static_cast<DWORD>(buffer.size()); + + if (!CreateWellKnownSid(type, NULL, buffer.data(), &cb)) + error::raise("CreateWellKnownSid"); + + return buffer; + } + + SidBuffer builtin_administrators() + { + /* + void* sid = nullptr; + SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_AUTHORITY; + + if (!AllocateAndInitializeSid( + &authority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &sid)) + { + error::raise("AllocateAndInitializeSid"); + } + + return std::unique_ptr<void, FreeSid>{sid}; + */ + + return well_known(WinBuiltinAdministratorsSid); + } + + struct DeleteSidString + { + void operator()(wchar_t* s) const + { + LocalFree(s); + } + }; + + std::wstring to_string(const SidBuffer& sid) + { + wchar_t* s = nullptr; + + if (!ConvertSidToStringSidW(const_cast<unsigned char*>(sid.data()), &s)) + error::raise("ConvertSidToStringSidW"); + + return std::unique_ptr<wchar_t, DeleteSidString>{s}.get(); + } +} @@ -0,0 +1,175 @@ +#pragma once + +#include "error.h" +#include "handle.h" +#include "os.h" +#include "sid.h" + +#include <Windows.h> + +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +namespace token +{ + namespace permissions + { + constexpr DWORD query() { return TOKEN_QUERY; } + + constexpr DWORD duplicate() { return TOKEN_DUPLICATE; } + } + + constexpr DWORD default_permissions = permissions::query(); + + // Current thread's primary impersonation token. + Handle dumb() { return Handle{NULL}; } + + Handle open_for_process( + const Handle& process, + DWORD permissions = default_permissions) + { + HANDLE raw; + + if (!OpenProcessToken(process, permissions, &raw)) + error::raise("OpenProcessToken"); + + return Handle{raw}; + } + + Handle open_for_current_process( + DWORD permissions = default_permissions) + { + return open_for_process(Handle{GetCurrentProcess()}, permissions); + } + + Handle get_linked(Handle&& token) + { + if (!os::is_vista_or_later()) + return std::move(token); + + auto type = TokenElevationTypeDefault; + DWORD cb = 0; + + if (!GetTokenInformation(token, TokenElevationType, &type, sizeof(type), &cb)) + error::raise("GetTokenInformation"); + + if (type != TokenElevationTypeLimited) + return std::move(token); + + HANDLE raw; + + if (!GetTokenInformation(token, TokenLinkedToken, &raw, sizeof(raw), &cb)) + error::raise("GetTokenInformation"); + + return Handle{raw}; + } + + Handle impersonate(const Handle& token) + { + HANDLE raw; + + if (!DuplicateToken(token, SecurityIdentification, &raw)) + error::raise("DuplicateToken"); + + return Handle{raw}; + } + + // + // FUNCTION: query_integrity_level() + // + // RETURN VALUE: Returns the integrity level of the current process. It is + // usually one of these values: + // + // SECURITY_MANDATORY_UNTRUSTED_RID (SID: S-1-16-0x0) + // Means untrusted level. It is used by processes started by the + // Anonymous group. Blocks most write access. + // + // SECURITY_MANDATORY_LOW_RID (SID: S-1-16-0x1000) + // Means low integrity level. It is used by Protected Mode Internet + // Explorer. Blocks write acess to most objects (such as files and + // registry keys) on the system. + // + // SECURITY_MANDATORY_MEDIUM_RID (SID: S-1-16-0x2000) + // Means medium integrity level. It is used by normal applications + // being launched while UAC is enabled. + // + // SECURITY_MANDATORY_HIGH_RID (SID: S-1-16-0x3000) + // Means high integrity level. It is used by administrative applications + // launched through elevation when UAC is enabled, or normal + // applications if UAC is disabled and the user is an administrator. + // + // SECURITY_MANDATORY_SYSTEM_RID (SID: S-1-16-0x4000) + // Means system integrity level. It is used by services and other + // system-level applications (such as Wininit, Winlogon, Smss, etc.) + // + DWORD query_integrity_level(const Handle& token) + { + DWORD cb = 0; + + if (!GetTokenInformation(token, TokenIntegrityLevel, NULL, 0, &cb)) + { + switch (GetLastError()) + { + case ERROR_INSUFFICIENT_BUFFER: + break; + default: + error::raise("GetTokenInformation"); + } + } + + std::vector<unsigned char> buf(cb); + const auto token_level = reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buf.data()); + + if (!GetTokenInformation(token, TokenIntegrityLevel, token_level, cb, &cb)) + error::raise("GetTokenInformation"); + + // Integrity Level SIDs are in the form of S-1-16-0xXXXX. (e.g. + // S-1-16-0x1000 stands for low integrity level SID). There is one and + // only one subauthority. + return *GetSidSubAuthority(token_level->Label.Sid, 0); + } + + std::wstring integrity_level_to_string(DWORD level) + { + static const std::unordered_map<DWORD, const wchar_t*> names = + { + {SECURITY_MANDATORY_UNTRUSTED_RID, L"Untrusted"}, + {SECURITY_MANDATORY_LOW_RID, L"Low"}, + {SECURITY_MANDATORY_MEDIUM_RID, L"Medium"}, + {SECURITY_MANDATORY_HIGH_RID, L"High"}, + {SECURITY_MANDATORY_SYSTEM_RID, L"System"}, + }; + + static constexpr auto unknown_name = L"Unknown"; + + const auto it = names.find(level); + + if (it == names.cend()) + return unknown_name; + + return it->second; + } + + bool belongs(const Handle& token, const SidBuffer& sid) + { + BOOL b = FALSE; + + if (!CheckTokenMembership(token, const_cast<unsigned char*>(sid.data()), &b)) + error::raise("CheckTokenMembership"); + + return b != FALSE; + } + + bool is_elevated(const Handle& token) + { + TOKEN_ELEVATION elevation; + DWORD cb = 0; + + if (!GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &cb)) + error::raise("GetTokenInformation"); + + return elevation.TokenIsElevated != 0; + } +} |