137
|
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
|