NativeMethods.cpp
  1  #include "pch.h"
  2  #include "NativeMethods.h"
  3  #include "FileLocksmith.h"
  4  #include "../FileLocksmithLib/Constants.h"
  5  
  6  namespace winrt::PowerToys::FileLocksmithLib::Interop::implementation
  7  {
  8  
  9  #pragma region HelperMethods
 10      std::wstring executable_path()
 11      {
 12          return pid_to_full_path(GetCurrentProcessId());
 13      }
 14      std::wstring paths_file()
 15      {
 16          std::wstring path{ PowerToys::Interop::Constants::AppDataPath() };
 17          path += L"\\";
 18          path += constants::nonlocalizable::PowerToyName;
 19          path += L"\\";
 20          path += constants::nonlocalizable::LastRunPath;
 21          return path;
 22      }
 23  
 24  #pragma endregion
 25  
 26      com_array<winrt::PowerToys::FileLocksmithLib::Interop::ProcessResult> NativeMethods::FindProcessesRecursive(array_view<hstring const> paths)
 27      {
 28          std::vector<std::wstring> paths_cpp{ paths.begin(), paths.end() };
 29  
 30          auto result_cpp = find_processes_recursive(paths_cpp);
 31          const auto result_size = static_cast<int>(result_cpp.size());
 32  
 33          std::vector<ProcessResult> result;
 34  
 35          if (result_size == 0)
 36          {
 37              return com_array<ProcessResult>();
 38          }
 39  
 40          for (int i = 0; i < result_size; i++)
 41          {
 42              result.push_back(ProcessResult
 43              {
 44                  hstring { result_cpp[i].name },
 45                  result_cpp[i].pid,
 46                  hstring{ result_cpp[i].user },
 47                      winrt::com_array<hstring>
 48                  {
 49                      result_cpp[i].files.begin(), result_cpp[i].files.end()
 50                  }
 51              });
 52          }
 53  
 54          return com_array<ProcessResult>{ result.begin(), result.end() };
 55      }
 56  
 57      hstring NativeMethods::PidToFullPath(uint32_t pid)
 58      {
 59          return hstring{ pid_to_full_path(pid) };
 60      }
 61  
 62      com_array<hstring> NativeMethods::ReadPathsFromFile()
 63      {
 64          std::ifstream stream(paths_file());
 65  
 66          std::vector<std::wstring> result_cpp;
 67          std::wstring line;
 68  
 69          bool finished = false;
 70  
 71          while (!finished)
 72          {
 73              WCHAR ch{};
 74              // We have to read data like this
 75              if (!stream.read(reinterpret_cast<char*>(&ch), 2))
 76              {
 77                  finished = true;
 78              }
 79              else if (ch == L'\n')
 80              {
 81                  if (line.empty())
 82                  {
 83                      finished = true;
 84                  }
 85                  else
 86                  {
 87                      result_cpp.push_back(line);
 88                      line = {};
 89                  }
 90              }
 91              else
 92              {
 93                  line += ch;
 94              }
 95          }
 96          return com_array<hstring>{ result_cpp.begin(), result_cpp.end() };
 97      }
 98   
 99      bool NativeMethods::StartAsElevated(array_view<hstring const> paths)
100      {
101          std::ofstream stream(paths_file());
102          const WCHAR newline = L'\n';
103  
104          for (uint32_t i = 0; i < paths.size(); i++)
105          {
106              std::wstring path_cpp{ paths[i] };
107              stream.write(reinterpret_cast<const char*>(path_cpp.c_str()), path_cpp.size() * sizeof(WCHAR));
108              stream.write(reinterpret_cast<const char*>(&newline), sizeof(WCHAR));
109          }
110  
111          stream.write(reinterpret_cast<const char*>(&newline), sizeof(WCHAR));
112  
113          if (!stream)
114          {
115              return false;
116          }
117  
118          stream.close();
119  
120          auto exec_path = executable_path();
121  
122          SHELLEXECUTEINFOW exec_info{};
123          exec_info.cbSize = sizeof(exec_info);
124          exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
125          exec_info.hwnd = NULL;
126          exec_info.lpVerb = L"runas";
127          exec_info.lpFile = exec_path.c_str();
128          exec_info.lpParameters = L"--elevated";
129          exec_info.lpDirectory = NULL;
130          exec_info.nShow = SW_SHOW;
131          exec_info.hInstApp = NULL;
132  
133          if (ShellExecuteExW(&exec_info))
134          {
135              CloseHandle(exec_info.hProcess);
136              return true;
137          }
138  
139          return false;
140      }
141  
142      /* Adapted from "https://learn.microsoft.com/windows/win32/secauthz/enabling-and-disabling-privileges-in-c--" */
143      bool NativeMethods::SetDebugPrivilege()
144      {
145          HANDLE hToken;
146          TOKEN_PRIVILEGES tp{};
147          LUID luid;
148  
149          if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) != 0)
150          {
151              if (!LookupPrivilegeValue(
152                      NULL, // lookup privilege on local system
153                      SE_DEBUG_NAME, // privilege to lookup
154                      &luid)) // receives LUID of privilege
155              {
156                  CloseHandle(hToken);
157                  return false;
158              }
159              tp.PrivilegeCount = 1;
160              tp.Privileges[0].Luid = luid;
161              tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
162  
163              if (!AdjustTokenPrivileges(
164                      hToken,
165                      FALSE,
166                      &tp,
167                      sizeof(TOKEN_PRIVILEGES),
168                      NULL,
169                      NULL))
170              {
171                  CloseHandle(hToken);
172                  return false;
173              }
174  
175              if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
176              {
177                  CloseHandle(hToken);
178                  return false;
179              }
180  
181              CloseHandle(hToken);
182              return true;
183          }
184          return false;
185      }
186   
187      // adapted from common/utils/elevation.h. No need to bring all dependencies to this project, though.
188      // TODO: Make elevation.h lighter so that this function can be used without bringing dependencies like spdlog in.
189      bool NativeMethods::IsProcessElevated()
190      {
191          HANDLE token = nullptr;
192          bool elevated = false;
193          if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
194          {
195              TOKEN_ELEVATION elevation{};
196              DWORD size;
197              if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
198              {
199                  elevated = (elevation.TokenIsElevated != 0);
200              }
201          }
202          if (token)
203          {
204              CloseHandle(token);
205          }
206          return elevated;
207      }
208  }