aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--LICENSE.txt21
-rw-r--r--error.h26
-rw-r--r--handle.h64
-rw-r--r--main.cpp183
-rw-r--r--main.rcbin0 -> 5844 bytes
-rw-r--r--os.h30
-rw-r--r--process.h40
-rw-r--r--resource_ids.hbin0 -> 1528 bytes
-rw-r--r--sid.h68
-rw-r--r--token.h175
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.
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..2ea7f85
--- /dev/null
+++ b/error.h
@@ -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));
+}
diff --git a/main.rc b/main.rc
new file mode 100644
index 0000000..5468c6e
--- /dev/null
+++ b/main.rc
Binary files differ
diff --git a/os.h b/os.h
new file mode 100644
index 0000000..e4ba71c
--- /dev/null
+++ b/os.h
@@ -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
new file mode 100644
index 0000000..54718a8
--- /dev/null
+++ b/resource_ids.h
Binary files differ
diff --git a/sid.h b/sid.h
new file mode 100644
index 0000000..5da8da7
--- /dev/null
+++ b/sid.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();
+ }
+}
diff --git a/token.h b/token.h
new file mode 100644
index 0000000..66dac28
--- /dev/null
+++ b/token.h
@@ -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;
+ }
+}