Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FileLocksmith]Query system processes if elevated #21688

Merged
merged 7 commits into from Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -103,6 +103,7 @@ std::vector<ProcessResult> find_processes_recursive(const std::vector<std::wstri
{
process_info.name,
process_info.pid,
process_info.user,
std::vector(it->second.begin(), it->second.end())
});
}
Expand All @@ -111,50 +112,6 @@ std::vector<ProcessResult> find_processes_recursive(const std::vector<std::wstri
return result;
}

std::wstring pid_to_user(DWORD pid)
{
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);

if (process == NULL)
{
return {};
}

std::wstring user = L"";
std::wstring domain = L"";

HANDLE token = NULL;

if (OpenProcessToken(process, TOKEN_QUERY, &token))
{
DWORD token_size = 0;
GetTokenInformation(token, TokenUser, NULL, 0, &token_size);

if (token_size > 0)
{
std::vector<BYTE> token_buffer(token_size);
GetTokenInformation(token, TokenUser, token_buffer.data(), token_size, &token_size);
TOKEN_USER* user_ptr = (TOKEN_USER*)token_buffer.data();
PSID psid = user_ptr->User.Sid;
DWORD user_size = 0;
DWORD domain_size = 0;
SID_NAME_USE sid_name;
LookupAccountSidW(NULL, psid, NULL, &user_size, NULL, &domain_size, &sid_name);
user.resize(user_size + 1);
domain.resize(domain_size + 1);
LookupAccountSidW(NULL, psid, user.data(), &user_size, domain.data(), &domain_size, &sid_name);
user[user_size] = L'\0';
domain[domain_size] = L'\0';
}

CloseHandle(token);
}

CloseHandle(process);

return user;
}

constexpr size_t LongMaxPathSize = 65536;

std::wstring pid_to_full_path(DWORD pid)
Expand Down
Expand Up @@ -6,14 +6,12 @@ struct ProcessResult
{
std::wstring name;
DWORD pid;
std::wstring user;
std::vector<std::wstring> files;
};

// Second version, checks handles towards files and all subfiles and folders of given dirs, if any.
std::vector<ProcessResult> find_processes_recursive(const std::vector<std::wstring>& paths);

// Gives the user name of the account running this process
std::wstring pid_to_user(DWORD pid);

// Gives the full path of the executable, given the process id
std::wstring pid_to_full_path(DWORD pid);
Expand Up @@ -128,46 +128,6 @@
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\FileLocksmith\</OutDir>
<TargetName>PowerToys.FileLocksmithLib.Interop</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;FILELOCKSMITHLIBINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsManaged>NetCore</CompileAsManaged>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<AdditionalOptions>/Zc:twoPhase-</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;FILELOCKSMITHLIBINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsManaged>NetCore</CompileAsManaged>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/Zc:twoPhase-</AdditionalOptions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
Expand Down
Expand Up @@ -238,6 +238,57 @@ std::vector<NtdllExtensions::HandleInfo> NtdllExtensions::handles() noexcept
// Returns the list of all processes.
// On failure, returns an empty vector.

std::wstring NtdllExtensions::pid_to_user(DWORD pid)
{
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
std::wstring user;
std::wstring domain;

if (process == nullptr)
{
return user;
}

HANDLE token = nullptr;

if (!OpenProcessToken(process, TOKEN_QUERY, &token))
{
return user;
}

DWORD token_size = 0;
GetTokenInformation(token, TokenUser, nullptr, 0, &token_size);

if (token_size < 0)
{
return user;
}

std::vector<BYTE> token_buffer(token_size);
GetTokenInformation(token, TokenUser, token_buffer.data(), token_size, &token_size);
TOKEN_USER* user_ptr = (TOKEN_USER*)token_buffer.data();
PSID psid = user_ptr->User.Sid;
DWORD user_buf_size = 0;
DWORD domain_buf_size = 0;
SID_NAME_USE sid_name;
LookupAccountSidW(nullptr, psid, nullptr, &user_buf_size, nullptr, &domain_buf_size, &sid_name);
if (!user_buf_size || !domain_buf_size)
{
return user;
}

user.resize(user_buf_size);
domain.resize(domain_buf_size);
LookupAccountSidW(nullptr, psid, user.data(), &user_buf_size, domain.data(), &domain_buf_size, &sid_name);
user.resize(user.size() - 1);
domain.resize(domain.size() - 1);
CloseHandle(token);
CloseHandle(process);

return user;
}


std::vector<NtdllExtensions::ProcessInfo> NtdllExtensions::processes() noexcept
{
auto get_info_result = NtQuerySystemInformationMemoryLoop(SystemProcessInformation);
Expand All @@ -258,6 +309,7 @@ std::vector<NtdllExtensions::ProcessInfo> NtdllExtensions::processes() noexcept
item.name = unicode_to_str(info_ptr->ImageName);
item.pid = (DWORD)(uintptr_t)info_ptr->UniqueProcessId;
item.modules = process_modules(item.pid);
item.user = pid_to_user(item.pid);

result.push_back(item);
}
Expand Down
Expand Up @@ -29,6 +29,7 @@ class NtdllExtensions : protected Ntdll
{
DWORD pid;
std::wstring name;
std::wstring user;
std::vector<std::wstring> modules;
};

Expand All @@ -44,6 +45,9 @@ class NtdllExtensions : protected Ntdll

std::wstring path_to_kernel_name(LPCWSTR path);

// Gives the user name of the account running this process
std::wstring pid_to_user(DWORD pid);

std::vector<HandleInfo> handles() noexcept;

// Returns the list of all processes.
Expand Down
75 changes: 69 additions & 6 deletions src/modules/FileLocksmith/FileLocksmithLibInterop/interop.cpp
Expand Up @@ -10,6 +10,7 @@ namespace FileLocksmith::Interop
{
System::String^ name;
System::UInt32 pid;
System::String^ user;
array<System::String^>^ files;
};

Expand Down Expand Up @@ -69,6 +70,7 @@ namespace FileLocksmith::Interop

item->name = from_wstring_view(result_cpp[i].name);
item->pid = result_cpp[i].pid;
item->user = from_wstring_view(result_cpp[i].user);

const int n_files = static_cast<int>(result_cpp[i].files.size());
item->files = gcnew array<System::String ^>(n_files);
Expand All @@ -83,12 +85,6 @@ namespace FileLocksmith::Interop
return result;
}

static System::String^ PidToUser(System::UInt32 pid)
{
auto user_cpp = pid_to_user(pid);
return from_wstring_view(user_cpp);
}

static System::String^ PidToFullPath(System::UInt32 pid)
{
auto path_cpp = pid_to_full_path(pid);
Expand Down Expand Up @@ -180,5 +176,72 @@ namespace FileLocksmith::Interop

return false;
}

/* Adapted from "https://learn.microsoft.com/en-us/windows/win32/secauthz/enabling-and-disabling-privileges-in-c--" */
static System::Boolean SetDebugPrivilege()
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;

if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) != 0)
{
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
SE_DEBUG_NAME, // privilege to lookup
&luid)) // receives LUID of privilege
{
CloseHandle(hToken);
return false;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
CloseHandle(hToken);
return false;
}

if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
CloseHandle(hToken);
return false;
}

CloseHandle(hToken);
return true;
}
return false;
}

// adapted from common/utils/elevation.h. No need to bring all dependencies to this project, though.
// TODO: Make elevation.h lighter so that this function can be used without bringing dependencies like spdlog in.
static System::Boolean IsProcessElevated()
{
HANDLE token = nullptr;
bool elevated = false;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
{
TOKEN_ELEVATION elevation;
DWORD size;
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
{
elevated = (elevation.TokenIsElevated != 0);
}
}
if (token)
{
CloseHandle(token);
}
return elevated;
}
};
}
12 changes: 11 additions & 1 deletion src/modules/FileLocksmith/FileLocksmithUI/App.xaml.cs
Expand Up @@ -40,7 +40,17 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
return;
}

_window = new MainWindow(Environment.GetCommandLineArgs().Contains("--elevated"));
bool isElevated = FileLocksmith.Interop.NativeMethods.IsProcessElevated();

if (isElevated)
{
if (!FileLocksmith.Interop.NativeMethods.SetDebugPrivilege())
{
Logger.LogWarning("Couldn't set debug privileges to see system processes.");
}
}

_window = new MainWindow(isElevated);
_window.Activate();
}

Expand Down
Expand Up @@ -5,19 +5,29 @@
namespace PowerToys.FileLocksmithUI.Converters
{
using System;
using System.Globalization;
using FileLocksmith.Interop;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;

public sealed class PidToUserConverter : IValueConverter
public sealed class UserToSystemWarningVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return NativeMethods.PidToUser((uint)value);
string user = ((string)value).ToUpperInvariant().Trim();
if (user.Equals("SYSTEM", StringComparison.Ordinal) || user.Equals("LOCALSYSTEM", StringComparison.Ordinal))
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value;
throw new NotSupportedException();
}
}
}
Expand Up @@ -136,6 +136,9 @@
<value>Click to see the entire list of paths.</value>
<comment>Paths as in file paths that were selected for the utility to check.</comment>
</data>
<data name="ProcessIsSystemUserWarning.Text" xml:space="preserve">
<value>The process belongs to a system user. Closing it may cause the system to malfunction.</value>
</data>
<data name="SelectedFilesListDialog.Title" xml:space="preserve">
<value>Selected file paths</value>
<comment>Paths as in file paths that were selected for the utility to check.</comment>
Expand Down