diff 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
line wrap: on
line diff
--- a/dep/animone/src/fd/win32.cc	Wed Jun 19 14:02:11 2024 -0400
+++ b/dep/animone/src/fd/win32.cc	Wed Jun 19 23:13:55 2024 -0400
@@ -32,15 +32,10 @@
 #include <winternl.h>
 
 /* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for
- * 32-bit PIDs, unlike SystemHandleInformation
- */
+ * 32-bit PIDs, unlike SystemHandleInformation */
 static constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x40);
 static constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL;
 
-/* this is filled in at runtime because it's not guaranteed to be (and isn't)
- * constant between different versions of Windows */
-static unsigned short file_type_index = 0;
-
 struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
 	PVOID Object;
 	ULONG_PTR UniqueProcessId;
@@ -136,22 +131,58 @@
 static std::wstring GetFinalPathNameByHandle(HANDLE handle) {
 	std::wstring buffer;
 
-	DWORD size = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+	DWORD size = ::GetFinalPathNameByHandleW(handle, nullptr, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
 	buffer.resize(size);
 	::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
 
 	return buffer;
 }
 
+/* ------------------------------------------------------------------- */
+
+static bool GetSystemDirectory(std::wstring& str) {
+	PWSTR path_wch;
+
+	if (FAILED(::SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, SHGFP_TYPE_CURRENT, &path_wch)))
+		return false;
+
+	str.assign(path_wch);
+
+	::CoTaskMemFree(path_wch);
+	return true;
+}
+
+static bool IsSystemDirectory(const std::string& path) {
+	return IsSystemDirectory(ToWstring(path));
+}
+
+static bool IsSystemDirectory(std::wstring path) {
+	std::wstring windir;
+	if (!GetSystemDirectory(windir))
+		return false;
+
+	::CharUpperBuffW(&path.front(), path.length());
+	::CharUpperBuffW(&windir.front(), windir.length());
+
+	// XXX wtf is 4?
+	return path.find(windir) == 4;
+}
+
 static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
-	if (file_type_index)
-		return object_type_index == file_type_index;
-	else if (!handle)
+	/* this is filled in at runtime because it's not guaranteed to be (and isn't)
+	 * constant between different versions of Windows */
+	static std::optional<unsigned short> file_type_index;
+
+	if (file_type_index.has_value()) {
+		return object_type_index == file_type_index.value();
+	} else if (!handle) {
+		/* XXX what? */
 		return true;
-	else if (GetHandleType(handle) == L"File") {
-		file_type_index = object_type_index;
+	} else if (GetHandleType(handle) == L"File") {
+		file_type_index.reset(object_type_index);
 		return true;
 	}
+
 	return false;
 }
 
@@ -179,13 +210,72 @@
 	return true;
 }
 
+/* ------------------------------------------------------------------- */
+
+static std::string GetProcessPath(DWORD process_id) {
+	// If we try to open a SYSTEM process, this function fails and the last error
+	// code is ERROR_ACCESS_DENIED.
+	//
+	// Note that if we requested PROCESS_QUERY_INFORMATION access right instead
+	// of PROCESS_QUERY_LIMITED_INFORMATION, this function would fail when used
+	// to open an elevated process.
+	Handle process_handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id));
+
+	if (!process_handle)
+		return std::wstring();
+
+	std::wstring buffer(MAX_PATH, L'\0');
+	DWORD buf_size = buffer.length();
+
+	// Note that this function requires Windows Vista or above. You may use
+	// GetProcessImageFileName or GetModuleFileNameEx on earlier versions.
+	if (!::QueryFullProcessImageNameW(process_handle.get(), 0, &buffer.front(), &buf_size))
+		return std::wstring();
+
+	buffer.resize(buf_size);
+	return ToUtf8String(buffer);
+}
+
+static std::string GetFilenameFromPath(const std::string& path) {
+	const auto pos = path.find_last_of(L"/\\");
+	return pos != std::wstring::npos ? path.substr(pos + 1) : path;
+}
+
+static bool VerifyProcessPath(const std::string& path) {
+	return !path.empty() && !IsSystemDirectory(path);
+}
+
+static bool VerifyProcessFilename(const std::string& name) {
+	static const std::set<std::string> invalid_names = {
+	    // System files
+	    "explorer.exe",   // Windows Explorer
+	    "taskeng.exe",    // Task Scheduler Engine
+	    "taskhost.exe",   // Host Process for Windows Tasks
+	    "taskhostex.exe", // Host Process for Windows Tasks
+	    "taskmgr.exe",    // Task Manager
+	    "services.exe",   // Service Control Manager
+	};
+
+	if (name.empty())
+		return false;
+
+	for (const auto& invalid_name : invalid_names)
+		if (util::EqualStrings(name, invalid_name))
+			return false;
+
+	return true;
+}
+
+/* ------------------------------------------------------------------- */
+/* extern functions */
+
 bool GetProcessName(pid_t pid, std::string& name) {
 	std::string path = GetProcessPath(pid);
 	if (path.empty() || !VerifyProcessPath(path))
 		return false;
 
-	name = GetFileNameFromPath(path);
-	if (!VerifyProcessFileName(name))
+	name = GetFilenameFromPath(path);
+	if (!VerifyProcessFilename(name))
 		return false;
 
 	return true;
@@ -214,7 +304,6 @@
 	return true;
 }
 
-/* this could be changed to being a callback, but... I'm too lazy right now :) */
 bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
 	if (!open_file_proc)
 		return false;
@@ -247,7 +336,7 @@
 		if (handle.get() == INVALID_HANDLE_VALUE)
 			continue;
 
-		if (GetFileType(handle.get()) != FILE_TYPE_DISK)
+		if (::GetFileType(handle.get()) != FILE_TYPE_DISK)
 			continue;
 
 		const std::wstring path = GetFinalPathNameByHandle(handle.get());