comparison dep/animone/src/fd/win32.cc @ 340:74e2365326c6

dep/animone: add experimental accessibility strategy I also moved most of the functions out of util/win32.cc, because that file is meant for things that are shared between the different functions, and currently that is only wide string conversion helpers.
author Paper <paper@paper.us.eu.org>
date Wed, 19 Jun 2024 23:13:55 -0400
parents a7d4e5107531
children adb79bdde329
comparison
equal deleted inserted replaced
339:eac06513db86 340:74e2365326c6
30 #include <tlhelp32.h> 30 #include <tlhelp32.h>
31 #include <windows.h> 31 #include <windows.h>
32 #include <winternl.h> 32 #include <winternl.h>
33 33
34 /* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for 34 /* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for
35 * 32-bit PIDs, unlike SystemHandleInformation 35 * 32-bit PIDs, unlike SystemHandleInformation */
36 */
37 static constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x40); 36 static constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x40);
38 static constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL; 37 static constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL;
39
40 /* this is filled in at runtime because it's not guaranteed to be (and isn't)
41 * constant between different versions of Windows */
42 static unsigned short file_type_index = 0;
43 38
44 struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { 39 struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
45 PVOID Object; 40 PVOID Object;
46 ULONG_PTR UniqueProcessId; 41 ULONG_PTR UniqueProcessId;
47 HANDLE HandleValue; 42 HANDLE HandleValue;
134 } 129 }
135 130
136 static std::wstring GetFinalPathNameByHandle(HANDLE handle) { 131 static std::wstring GetFinalPathNameByHandle(HANDLE handle) {
137 std::wstring buffer; 132 std::wstring buffer;
138 133
139 DWORD size = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); 134 DWORD size = ::GetFinalPathNameByHandleW(handle, nullptr, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
140 buffer.resize(size); 135 buffer.resize(size);
141 ::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); 136 ::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
142 137
143 return buffer; 138 return buffer;
144 } 139 }
145 140
141 /* ------------------------------------------------------------------- */
142
143 static bool GetSystemDirectory(std::wstring& str) {
144 PWSTR path_wch;
145
146 if (FAILED(::SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, SHGFP_TYPE_CURRENT, &path_wch)))
147 return false;
148
149 str.assign(path_wch);
150
151 ::CoTaskMemFree(path_wch);
152 return true;
153 }
154
155 static bool IsSystemDirectory(const std::string& path) {
156 return IsSystemDirectory(ToWstring(path));
157 }
158
159 static bool IsSystemDirectory(std::wstring path) {
160 std::wstring windir;
161 if (!GetSystemDirectory(windir))
162 return false;
163
164 ::CharUpperBuffW(&path.front(), path.length());
165 ::CharUpperBuffW(&windir.front(), windir.length());
166
167 // XXX wtf is 4?
168 return path.find(windir) == 4;
169 }
170
146 static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) { 171 static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
147 if (file_type_index) 172 /* this is filled in at runtime because it's not guaranteed to be (and isn't)
148 return object_type_index == file_type_index; 173 * constant between different versions of Windows */
149 else if (!handle) 174 static std::optional<unsigned short> file_type_index;
175
176 if (file_type_index.has_value()) {
177 return object_type_index == file_type_index.value();
178 } else if (!handle) {
179 /* XXX what? */
150 return true; 180 return true;
151 else if (GetHandleType(handle) == L"File") { 181 } else if (GetHandleType(handle) == L"File") {
152 file_type_index = object_type_index; 182 file_type_index.reset(object_type_index);
153 return true; 183 return true;
154 } 184 }
185
155 return false; 186 return false;
156 } 187 }
157 188
158 static bool IsFileMaskOk(ACCESS_MASK access_mask) { 189 static bool IsFileMaskOk(ACCESS_MASK access_mask) {
159 if (!(access_mask & FILE_READ_DATA)) 190 if (!(access_mask & FILE_READ_DATA))
176 if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY)) 207 if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY))
177 return false; 208 return false;
178 209
179 return true; 210 return true;
180 } 211 }
212
213 /* ------------------------------------------------------------------- */
214
215 static std::string GetProcessPath(DWORD process_id) {
216 // If we try to open a SYSTEM process, this function fails and the last error
217 // code is ERROR_ACCESS_DENIED.
218 //
219 // Note that if we requested PROCESS_QUERY_INFORMATION access right instead
220 // of PROCESS_QUERY_LIMITED_INFORMATION, this function would fail when used
221 // to open an elevated process.
222 Handle process_handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id));
223
224 if (!process_handle)
225 return std::wstring();
226
227 std::wstring buffer(MAX_PATH, L'\0');
228 DWORD buf_size = buffer.length();
229
230 // Note that this function requires Windows Vista or above. You may use
231 // GetProcessImageFileName or GetModuleFileNameEx on earlier versions.
232 if (!::QueryFullProcessImageNameW(process_handle.get(), 0, &buffer.front(), &buf_size))
233 return std::wstring();
234
235 buffer.resize(buf_size);
236 return ToUtf8String(buffer);
237 }
238
239 static std::string GetFilenameFromPath(const std::string& path) {
240 const auto pos = path.find_last_of(L"/\\");
241 return pos != std::wstring::npos ? path.substr(pos + 1) : path;
242 }
243
244 static bool VerifyProcessPath(const std::string& path) {
245 return !path.empty() && !IsSystemDirectory(path);
246 }
247
248 static bool VerifyProcessFilename(const std::string& name) {
249 static const std::set<std::string> invalid_names = {
250 // System files
251 "explorer.exe", // Windows Explorer
252 "taskeng.exe", // Task Scheduler Engine
253 "taskhost.exe", // Host Process for Windows Tasks
254 "taskhostex.exe", // Host Process for Windows Tasks
255 "taskmgr.exe", // Task Manager
256 "services.exe", // Service Control Manager
257 };
258
259 if (name.empty())
260 return false;
261
262 for (const auto& invalid_name : invalid_names)
263 if (util::EqualStrings(name, invalid_name))
264 return false;
265
266 return true;
267 }
268
269 /* ------------------------------------------------------------------- */
270 /* extern functions */
181 271
182 bool GetProcessName(pid_t pid, std::string& name) { 272 bool GetProcessName(pid_t pid, std::string& name) {
183 std::string path = GetProcessPath(pid); 273 std::string path = GetProcessPath(pid);
184 if (path.empty() || !VerifyProcessPath(path)) 274 if (path.empty() || !VerifyProcessPath(path))
185 return false; 275 return false;
186 276
187 name = GetFileNameFromPath(path); 277 name = GetFilenameFromPath(path);
188 if (!VerifyProcessFileName(name)) 278 if (!VerifyProcessFilename(name))
189 return false; 279 return false;
190 280
191 return true; 281 return true;
192 } 282 }
193 283
212 } while (::Process32Next(process_snap.get(), &pe32)); 302 } while (::Process32Next(process_snap.get(), &pe32));
213 303
214 return true; 304 return true;
215 } 305 }
216 306
217 /* this could be changed to being a callback, but... I'm too lazy right now :) */
218 bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) { 307 bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
219 if (!open_file_proc) 308 if (!open_file_proc)
220 return false; 309 return false;
221 310
222 std::unordered_map<pid_t, Handle> proc_handles; 311 std::unordered_map<pid_t, Handle> proc_handles;
245 334
246 Handle handle(DuplicateHandle(proc_handles[pid].get(), h.HandleValue)); 335 Handle handle(DuplicateHandle(proc_handles[pid].get(), h.HandleValue));
247 if (handle.get() == INVALID_HANDLE_VALUE) 336 if (handle.get() == INVALID_HANDLE_VALUE)
248 continue; 337 continue;
249 338
250 if (GetFileType(handle.get()) != FILE_TYPE_DISK) 339 if (::GetFileType(handle.get()) != FILE_TYPE_DISK)
251 continue; 340 continue;
252 341
253 const std::wstring path = GetFinalPathNameByHandle(handle.get()); 342 const std::wstring path = GetFinalPathNameByHandle(handle.get());
254 if (!IsFilePathOk(path)) 343 if (!IsFilePathOk(path))
255 continue; 344 continue;