258
+ − 1 #include "animone/fd/win32.h"
+ − 2 #include "animone.h"
+ − 3 #include "animone/util/win32.h"
+ − 4
+ − 5 #include <stdexcept>
+ − 6 #include <string>
+ − 7 #include <unordered_map>
+ − 8 #include <vector>
+ − 9
+ − 10 #include <fileapi.h>
+ − 11 #include <handleapi.h>
+ − 12 #include <libloaderapi.h>
+ − 13 #include <ntdef.h>
+ − 14 #include <psapi.h>
+ − 15 #include <shlobj.h>
+ − 16 #include <stringapiset.h>
+ − 17 #include <tlhelp32.h>
+ − 18 #include <windows.h>
+ − 19 #include <winternl.h>
+ − 20
+ − 21 /* This file is noticably more complex than Unix and Linux, and that's because
+ − 22 * there is no "simple" way to get the paths of a file. In fact, this thing requires
+ − 23 * you to use *internal functions* that can't even be linked to, hence why we have to
+ − 24 * use GetProcAddress and such. What a mess.
+ − 25 *
+ − 26 * Speaking of which, because this file uses internal functions of the OS, it is not
+ − 27 * guaranteed to work far into the future. However, it has worked since NT 6.0 (Vista)
+ − 28 * at least, so it's unlikely to be changed much ever.
+ − 29 */
+ − 30
+ − 31 /* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for
+ − 32 * 32-bit PIDs, unlike SystemHandleInformation
+ − 33 *
+ − 34 * TODO: implement SystemHandleInformation for systems older than XP
+ − 35 */
+ − 36 static constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x40);
+ − 37 static constexpr SYSTEM_INFORMATION_CLASS SystemHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x10);
+ − 38 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
+ − 44 struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
+ − 45 PVOID Object;
+ − 46 ULONG_PTR UniqueProcessId;
+ − 47 HANDLE HandleValue;
+ − 48 ACCESS_MASK GrantedAccess;
+ − 49 USHORT CreatorBackTraceIndex;
+ − 50 USHORT ObjectTypeIndex;
+ − 51 ULONG HandleAttributes;
+ − 52 ULONG Reserved;
+ − 53 };
+ − 54
+ − 55 struct SYSTEM_HANDLE_INFORMATION_EX {
+ − 56 ULONG_PTR NumberOfHandles;
+ − 57 ULONG_PTR Reserved;
+ − 58 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
+ − 59 };
+ − 60
+ − 61 namespace animone::internal::win32 {
+ − 62
+ − 63 class Ntdll {
+ − 64 public:
+ − 65 Ntdll() {
+ − 66 ntdll = ::GetModuleHandleW(L"ntdll.dll");
+ − 67 nt_query_system_information = reinterpret_cast<decltype(::NtQuerySystemInformation)*>(
+ − 68 ::GetProcAddress(ntdll, "NtQuerySystemInformation"));
+ − 69 nt_query_object = reinterpret_cast<decltype(::NtQueryObject)*>(::GetProcAddress(ntdll, "NtQueryObject"));
+ − 70 }
+ − 71
+ − 72 NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS cls, PVOID sysinfo, ULONG len,
+ − 73 PULONG retlen){return nt_query_system_information(cls, sysinfo, len, retlen)}
+ − 74
+ − 75 NTSTATUS QueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS cls, PVOID objinf, ULONG objinflen, PULONG retlen) {
+ − 76 return nt_query_object(handle, cls, objinf, objinflen, retlen);
+ − 77 }
+ − 78
+ − 79 private:
+ − 80 HMODULE ntdll;
+ − 81 decltype(::NtQuerySystemInformation)* nt_query_system_information;
+ − 82 decltype(::NtQueryObject)* nt_query_object;
+ − 83
+ − 84 }
+ − 85
+ − 86 Ntdll ntdll;
+ − 87
+ − 88 static HANDLE DuplicateHandle(HANDLE process_handle, HANDLE handle) {
+ − 89 HANDLE dup_handle = nullptr;
+ − 90 const bool result =
+ − 91 ::DuplicateHandle(process_handle, handle, ::GetCurrentProcess(), &dup_handle, 0, false, DUPLICATE_SAME_ACCESS);
+ − 92 return result ? dup_handle : nullptr;
+ − 93 }
+ − 94
+ − 95 static std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleInformation() {
+ − 96 /* we should really put a cap on this */
+ − 97 ULONG cb = 1 << 19;
+ − 98 NTSTATUS status = STATUS_NO_MEMORY;
+ − 99 std::unique_ptr<SYSTEM_HANDLE_INFORMATION_EX> info(malloc(cb));
+ − 100
+ − 101 do {
+ − 102 info.reset(malloc(cb *= 2));
+ − 103 if (!info)
+ − 104 continue;
+ − 105
+ − 106 status = ntdll.QuerySystemInformation(SystemExtendedHandleInformation, info.get(), cb, &cb);
+ − 107 } while (status == STATUS_INFO_LENGTH_MISMATCH);
+ − 108
+ − 109 if (!NT_SUCCESS(status))
+ − 110 return {};
+ − 111
+ − 112 std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res;
+ − 113
+ − 114 ULONG_PTR handles = info->NumberOfHandles;
+ − 115 if (!handles)
+ − 116 return {};
+ − 117
+ − 118 res.reserve(handles);
+ − 119
+ − 120 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles;
+ − 121 do {
+ − 122 if (entry)
+ − 123 res.push_back(*(entry++));
+ − 124 } while (--handles);
+ − 125
+ − 126 return res;
+ − 127 }
+ − 128
+ − 129 static std::wstring GetHandleType(HANDLE handle) {
+ − 130 OBJECT_TYPE_INFORMATION info = {0};
+ − 131 ntdll.QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL);
+ − 132 return std::wstring(info.TypeName.Buffer, info.TypeName.Length);
+ − 133 }
+ − 134
+ − 135 static std::wstring GetFinalPathNameByHandle(HANDLE handle) {
+ − 136 std::wstring buffer;
+ − 137
+ − 138 DWORD size = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+ − 139 buffer.resize(size);
+ − 140 ::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+ − 141
+ − 142 return buffer;
+ − 143 }
+ − 144
+ − 145 static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
+ − 146 if (file_type_index)
+ − 147 return object_type_index == file_type_index;
+ − 148 else if (!handle)
+ − 149 return true;
+ − 150 else if (GetHandleType(handle) == L"File") {
+ − 151 file_type_index = object_type_index;
+ − 152 return true;
+ − 153 }
+ − 154 return false;
+ − 155 }
+ − 156
+ − 157 static bool IsFileMaskOk(ACCESS_MASK access_mask) {
+ − 158 if (!(access_mask & FILE_READ_DATA))
+ − 159 return false;
+ − 160
+ − 161 if ((access_mask & FILE_APPEND_DATA) || (access_mask & FILE_WRITE_EA) || (access_mask & FILE_WRITE_ATTRIBUTES))
+ − 162 return false;
+ − 163
+ − 164 return true;
+ − 165 }
+ − 166
+ − 167 static bool IsFilePathOk(const std::wstring& path) {
+ − 168 if (path.empty())
+ − 169 return false;
+ − 170
+ − 171 if (IsSystemDirectory(path))
+ − 172 return false;
+ − 173
+ − 174 const auto file_attributes = GetFileAttributesW(path.c_str());
+ − 175 if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY))
+ − 176 return false;
+ − 177
+ − 178 return true;
+ − 179 }
+ − 180
+ − 181 bool GetProcessName(pid_t pid, std::string& name) {
+ − 182 std::wstring wname = GetProcessPath();
+ − 183 if (wname.empty())
+ − 184 return false;
+ − 185
+ − 186 return ToUtf8String(GetFileNameWithoutExtension(GetFileNameFromPath(wname)));
+ − 187 }
+ − 188
+ − 189 bool EnumerateOpenProcesses(process_proc_t process_proc) {
+ − 190 HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ − 191 if (hProcessSnap == INVALID_HANDLE_VALUE)
+ − 192 return false;
+ − 193
+ − 194 PROCESSENTRY32 pe32;
+ − 195 pe32.dwSize = sizeof(PROCESSENTRY32);
+ − 196
+ − 197 if (!::Process32First(hProcessSnap, &pe32))
+ − 198 return false;
+ − 199
+ − 200 if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
+ − 201 return false;
+ − 202
+ − 203 while (::Process32Next(hProcessSnap, &pe32))
+ − 204 if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
+ − 205 return false;
+ − 206
+ − 207 ::CloseHandle(hProcessSnap);
+ − 208
+ − 209 return true;
+ − 210 }
+ − 211
+ − 212 /* this could be changed to being a callback, but... I'm too lazy right now :) */
+ − 213 bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+ − 214 if (!open_file_proc)
+ − 215 return false;
+ − 216
+ − 217 std::unordered_map<pid_t, Handle> proc_handles;
+ − 218
+ − 219 for (const pid_t& pid : pids) {
+ − 220 const HANDLE handle = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid);
+ − 221 if (handle != INVALID_HANDLE_VALUE)
+ − 222 proc_handles[pid] = Handle(handle);
+ − 223 }
+ − 224
+ − 225 if (proc_handles.empty())
+ − 226 return false;
+ − 227
+ − 228 std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> info = GetSystemHandleInformation();
+ − 229
+ − 230 for (const auto& h : info) {
+ − 231 const pid_t pid = h.UniqueProcessId;
+ − 232 if (!pids.count(pid))
+ − 233 continue;
+ − 234
+ − 235 if (!IsFileHandle(nullptr, h.ObjectTypeIndex))
+ − 236 continue;
+ − 237
+ − 238 if (!IsFileMaskOk(h.GrantedAccess))
+ − 239 continue;
+ − 240
+ − 241 Handle handle(DuplicateHandle(proc_handles[pid].get(), h.HandleValue));
+ − 242 if (handle.get() == INVALID_HANDLE_VALUE)
+ − 243 continue;
+ − 244
+ − 245 if (GetFileType(handle.get()) != FILE_TYPE_DISK)
+ − 246 continue;
+ − 247
+ − 248 const std::wstring path = GetFinalPathNameByHandle(handle.get());
+ − 249 if (!IsFilePathOk(path))
+ − 250 continue;
+ − 251
+ − 252 if (!open_file_proc({pid, ToUtf8String(path)}))
+ − 253 return false;
+ − 254 }
+ − 255
+ − 256 return true;
+ − 257 }
+ − 258
+ − 259 } // namespace animone::internal::win32