mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-06-07 09:28:03 +08:00
[FileLocksmith]Query system processes if elevated (#21688)
* [FileLocksmith]Query system processes if elevated * Show warning if user is a system user * Make text in the file list selectable * Update src/modules/FileLocksmith/FileLocksmithLibInterop/NtdllExtensions.cpp Co-authored-by: Andrey Nekrasov <yuyoyuppe@users.noreply.github.com> * Trim \0 no longer required * Correct elevation detection logic * Use theme approppriate colors Co-authored-by: Andrey Nekrasov <yuyoyuppe@users.noreply.github.com>
This commit is contained in:
parent
462a107c7c
commit
66754afd76
@ -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())
|
||||
});
|
||||
}
|
||||
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
{
|
||||
DWORD pid;
|
||||
std::wstring name;
|
||||
std::wstring user;
|
||||
std::vector<std::wstring> modules;
|
||||
};
|
||||
|
||||
@ -44,6 +45,9 @@ public:
|
||||
|
||||
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.
|
||||
|
@ -10,6 +10,7 @@ namespace FileLocksmith::Interop
|
||||
{
|
||||
System::String^ name;
|
||||
System::UInt32 pid;
|
||||
System::String^ user;
|
||||
array<System::String^>^ files;
|
||||
};
|
||||
|
||||
@ -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);
|
||||
@ -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);
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -40,7 +40,17 @@ namespace FileLocksmithUI
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -25,7 +25,7 @@
|
||||
TrueValue="Collapsed" />
|
||||
<converters:FileCountConverter x:Key="fileCountConverter" />
|
||||
<converters:PidToIconConverter x:Key="pidToIconConverter" />
|
||||
<converters:PidToUserConverter x:Key="pidToUserConverter" />
|
||||
<converters:UserToSystemWarningVisibilityConverter x:Key="userToSystemWarningVisibilityConverter" />
|
||||
<converters:FileListToDescriptionConverter x:Key="fileListToDescriptionConverter" />
|
||||
</Page.Resources>
|
||||
|
||||
@ -116,6 +116,17 @@
|
||||
</StackPanel>
|
||||
</labs:SettingsExpander.Header>
|
||||
<labs:SettingsExpander.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon
|
||||
Margin="0,0,8,0"
|
||||
Glyph=""
|
||||
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
|
||||
Visibility="{x:Bind user, Mode=OneTime, Converter={StaticResource userToSystemWarningVisibilityConverter}}"
|
||||
>
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock TextWrapping="Wrap" x:Uid="ProcessIsSystemUserWarning" />
|
||||
</ToolTipService.ToolTip>
|
||||
</FontIcon>
|
||||
<Button Command="{Binding Path=DataContext.EndTaskCommand, ElementName=ProcessesListView}" CommandParameter="{Binding}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<FontIcon
|
||||
@ -125,6 +136,7 @@
|
||||
<TextBlock x:Uid="EndTask" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</labs:SettingsExpander.Content>
|
||||
<labs:SettingsExpander.Items>
|
||||
<labs:SettingsCard x:Uid="ProcessID">
|
||||
@ -137,7 +149,7 @@
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind pid, Converter={StaticResource pidToUserConverter}}" />
|
||||
Text="{x:Bind user}" />
|
||||
</labs:SettingsCard>
|
||||
<labs:SettingsCard ContentAlignment="Vertical">
|
||||
<labs:SettingsCard.Header>
|
||||
@ -199,7 +211,7 @@
|
||||
x:Uid="SelectedFilesListDialog"
|
||||
>
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" HorizontalScrollMode="Auto" VerticalScrollBarVisibility="Auto" VerticalScrollMode="Auto">
|
||||
<TextBlock Text="{x:Bind ViewModel.PathsToString, Mode=OneWay}"/>
|
||||
<TextBlock IsTextSelectionEnabled="True" Text="{x:Bind ViewModel.PathsToString, Mode=OneWay}"/>
|
||||
</ScrollViewer>
|
||||
</ContentDialog>
|
||||
</Grid>
|
||||
|
Loading…
Reference in New Issue
Block a user