comparison dep/animia/src/platform/win32/fd.cc @ 137:69db40272acd

dep/animia: [WIP] huge refactor this WILL NOT compile, because lots of code has been changed and every API in the original codebase has been removed. note that this api setup is not exactly permanent...
author Paper <mrpapersonic@gmail.com>
date Fri, 10 Nov 2023 13:52:47 -0500
parents
children
comparison
equal deleted inserted replaced
136:7d3ad9529c4c 137:69db40272acd
1 /**
2 * win32.cpp
3 * - provides support for Windows clients
4 *
5 **/
6 #include "win32.h"
7 #include <fileapi.h>
8 #include <handleapi.h>
9 #include <iostream>
10 #include <libloaderapi.h>
11 #include <ntdef.h>
12 #include <psapi.h>
13 #include <shlobj.h>
14 #include <stdexcept>
15 #include <string>
16 #include <stringapiset.h>
17 #include <tlhelp32.h>
18 #include <unordered_map>
19 #include <vector>
20 #include <windows.h>
21 #include <winternl.h>
22
23 /* This file is noticably more complex than Unix and Linux, and that's because
24 there is no "simple" way to get the paths of a file. In fact, this thing requires
25 you to use *internal functions* that can't even be linked to, hence why we have to
26 use GetProcAddress and such. What a mess. */
27
28 #define SystemExtendedHandleInformation ((SYSTEM_INFORMATION_CLASS)0x40)
29 constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL;
30 constexpr NTSTATUS STATUS_SUCCESS = 0x00000000UL;
31
32 static unsigned short file_type_index = 0;
33
34 struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
35 PVOID Object;
36 ULONG_PTR UniqueProcessId;
37 HANDLE HandleValue;
38 ACCESS_MASK GrantedAccess;
39 USHORT CreatorBackTraceIndex;
40 USHORT ObjectTypeIndex;
41 ULONG HandleAttributes;
42 ULONG Reserved;
43 };
44
45 struct SYSTEM_HANDLE_INFORMATION_EX {
46 ULONG_PTR NumberOfHandles;
47 ULONG_PTR Reserved;
48 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
49 };
50
51 namespace Animia { namespace Windows {
52
53 /* All of this BS is required on Windows. Why? */
54
55 HANDLE DuplicateHandle(HANDLE process_handle, HANDLE handle) {
56 HANDLE dup_handle = nullptr;
57 const bool result =
58 ::DuplicateHandle(process_handle, handle, ::GetCurrentProcess(), &dup_handle, 0, false, DUPLICATE_SAME_ACCESS);
59 return result ? dup_handle : nullptr;
60 }
61
62 PVOID GetNTDLLAddress(LPCSTR proc_name) {
63 return reinterpret_cast<PVOID>(::GetProcAddress(::GetModuleHandleA("ntdll.dll"), proc_name));
64 }
65
66 NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS cls, PVOID sysinfo, ULONG len, PULONG retlen) {
67 static const auto func =
68 reinterpret_cast<decltype(::NtQuerySystemInformation)*>(GetNTDLLAddress("NtQuerySystemInformation"));
69 return func(cls, sysinfo, len, retlen);
70 }
71
72 NTSTATUS QueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS cls, PVOID objinf, ULONG objinflen, PULONG retlen) {
73 static const auto func = reinterpret_cast<decltype(::NtQueryObject)*>(GetNTDLLAddress("NtQueryObject"));
74 return func(handle, cls, objinf, objinflen, retlen);
75 }
76
77 std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleInformation() {
78 std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res;
79 ULONG cb = 1 << 19;
80 NTSTATUS status = STATUS_SUCCESS;
81 SYSTEM_HANDLE_INFORMATION_EX* info;
82
83 do {
84 status = STATUS_NO_MEMORY;
85
86 if (!(info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2)))
87 continue;
88
89 res.reserve(cb);
90
91 if (0 <= (status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb))) {
92 if (ULONG_PTR handles = info->NumberOfHandles) {
93 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles;
94 do {
95 if (entry)
96 res.push_back(*entry);
97 } while (entry++, --handles);
98 }
99 }
100 free(info);
101 } while (status == STATUS_INFO_LENGTH_MISMATCH);
102
103 return res;
104 }
105
106 OBJECT_TYPE_INFORMATION QueryObjectTypeInfo(HANDLE handle) {
107 OBJECT_TYPE_INFORMATION info;
108 QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL);
109 return info;
110 }
111
112 /* we're using UTF-8. originally, I had used just the ANSI versions of functions, but that
113 sucks massive dick. this way we get unicode in the way every single other OS does it */
114 std::string UnicodeStringToUtf8(std::wstring string) {
115 unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0, NULL, NULL);
116 std::string ret = std::string(size, '\0');
117 ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length(), NULL, NULL);
118 return ret;
119 }
120
121 std::string UnicodeStringToUtf8(UNICODE_STRING string) {
122 unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, NULL, 0, NULL, NULL);
123 std::string ret = std::string(size, '\0');
124 ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, &ret.front(), ret.length(), NULL, NULL);
125 return ret;
126 }
127
128 std::wstring Utf8StringToUnicode(std::string string) {
129 unsigned long size = ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0);
130 std::wstring ret = std::wstring(size, L'\0');
131 ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length());
132 return ret;
133 }
134
135 std::string GetHandleType(HANDLE handle) {
136 OBJECT_TYPE_INFORMATION info = QueryObjectTypeInfo(handle);
137 return UnicodeStringToUtf8(info.TypeName);
138 }
139
140 std::string GetFinalPathNameByHandle(HANDLE handle) {
141 std::wstring buffer;
142
143 int result = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
144 buffer.resize(result);
145 ::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
146 buffer.resize(buffer.find('\0'));
147
148 return UnicodeStringToUtf8(buffer);
149 }
150
151 bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
152 if (file_type_index)
153 return object_type_index == file_type_index;
154 else if (!handle)
155 return true;
156 else if (GetHandleType(handle) == "File") {
157 file_type_index = object_type_index;
158 return true;
159 }
160 return false;
161 }
162
163 bool IsFileMaskOk(ACCESS_MASK access_mask) {
164 if (!(access_mask & FILE_READ_DATA))
165 return false;
166
167 if ((access_mask & FILE_APPEND_DATA) || (access_mask & FILE_WRITE_EA) || (access_mask & FILE_WRITE_ATTRIBUTES))
168 return false;
169
170 return true;
171 }
172
173 bool IsFilePathOk(const std::string& path) {
174 if (path.empty())
175 return false;
176
177 const auto file_attributes = GetFileAttributesA(path.c_str());
178 if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY))
179 return false;
180
181 return true;
182 }
183
184 std::string GetSystemDirectory() {
185 PWSTR path_wch;
186 SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &path_wch);
187 std::wstring path_wstr(path_wch);
188 CoTaskMemFree(path_wch);
189 return UnicodeStringToUtf8(path_wstr);
190 }
191
192 bool IsSystemFile(const std::string& path) {
193 std::wstring path_w = Utf8StringToUnicode(path);
194 CharUpperBuffW(&path_w.front(), path_w.length());
195 std::wstring windir_w = Utf8StringToUnicode(GetSystemDirectory());
196 CharUpperBuffW(&windir_w.front(), windir_w.length());
197 return path_w.find(windir_w) == 4;
198 }
199
200 std::vector<int> get_all_pids() {
201 std::vector<int> ret;
202 HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
203 PROCESSENTRY32 pe32;
204 pe32.dwSize = sizeof(PROCESSENTRY32);
205
206 if (hProcessSnap == INVALID_HANDLE_VALUE)
207 return std::vector<int>();
208
209 if (!Process32First(hProcessSnap, &pe32))
210 return std::vector<int>();
211
212 ret.push_back(pe32.th32ProcessID);
213 while (Process32Next(hProcessSnap, &pe32)) {
214 ret.push_back(pe32.th32ProcessID);
215 }
216 // clean the snapshot object
217 CloseHandle(hProcessSnap);
218
219 return ret;
220 }
221
222 std::string get_process_name(int pid) {
223 unsigned long size = 256, ret_size = 0;
224 HANDLE handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
225 if (!handle)
226 return "";
227
228 std::wstring ret(size, L'\0');
229 while (size < 32768) {
230 ret.resize(size, L'\0');
231
232 if (!(ret_size = ::GetModuleBaseNameW(handle, 0, &ret.front(), ret.length())))
233 ret = L"";
234 else if (size > ret_size)
235 ret.resize(ret.find('\0'));
236
237 size *= 2;
238 }
239
240 CloseHandle(handle);
241
242 return UnicodeStringToUtf8(ret);
243 }
244
245 std::vector<std::string> get_open_files(int pid) {
246 std::vector<std::string> ret;
247 std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> info = GetSystemHandleInformation();
248 for (auto& h : info) {
249 if (h.UniqueProcessId != pid)
250 continue;
251
252 if (!IsFileHandle(nullptr, h.ObjectTypeIndex))
253 continue;
254 if (!IsFileMaskOk(h.GrantedAccess))
255 continue;
256
257 const HANDLE proc = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid);
258 HANDLE handle = DuplicateHandle(proc, h.HandleValue);
259 if (!handle)
260 continue;
261
262 if (GetFileType(handle) != FILE_TYPE_DISK)
263 continue;
264
265 std::string path = GetFinalPathNameByHandle(handle);
266 if (!IsFilePathOk(path))
267 continue;
268
269 ret.push_back(path);
270 }
271 return ret;
272 }
273
274 std::vector<std::string> filter_system_files(const std::vector<std::string>& source) {
275 std::vector<std::string> ret;
276 for (const std::string& s : source) {
277 if (!IsSystemFile(s))
278 ret.push_back(s);
279 }
280 return ret;
281 }
282
283 std::unordered_map<int, std::vector<std::string>> get_all_open_files() {
284 std::unordered_map<int, std::vector<std::string>> map;
285 std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> info = GetSystemHandleInformation();
286 for (auto& h : info) {
287 int pid = h.UniqueProcessId;
288
289 if (!IsFileHandle(nullptr, h.ObjectTypeIndex))
290 continue;
291 if (!IsFileMaskOk(h.GrantedAccess))
292 continue;
293
294 const HANDLE proc = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid);
295 HANDLE handle = DuplicateHandle(proc, h.HandleValue);
296 if (!handle)
297 continue;
298
299 if (GetFileType(handle) != FILE_TYPE_DISK)
300 continue;
301
302 std::string path = GetFinalPathNameByHandle(handle);
303 if (!IsFilePathOk(path))
304 continue;
305
306 if (map.find(pid) == map.end())
307 map[pid] = {};
308 map[pid].push_back(path);
309 }
310 return map;
311 }
312
313 } // namespace Windows
314 } // namespace Animia