Mercurial > minori
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 |