]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2007 Brian Dessent | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * A copy of the GNU General Public License can be found at | |
10 | * http://www.gnu.org/ | |
11 | * | |
12 | * Written by Brian Dessent <brian@dessent.net> | |
13 | * | |
14 | */ | |
15 | ||
16 | #include "win32.h" | |
17 | #include <memory> | |
18 | #include <malloc.h> | |
19 | #include "LogFile.h" | |
20 | #include "resource.h" | |
21 | #include "ini.h" | |
22 | #include <sys/stat.h> | |
23 | #include "String++.h" | |
24 | ||
25 | NTSecurity nt_sec; | |
26 | ||
27 | #define ALL_INHERIT_ACE (CONTAINER_INHERIT_ACE \ | |
28 | | OBJECT_INHERIT_ACE \ | |
29 | | INHERIT_ONLY_ACE) | |
30 | #define NO_INHERIT_ACE (0) | |
31 | ||
32 | PSECURITY_DESCRIPTOR | |
33 | NTSecurity::GetPosixPerms (const char *fname, PSID owner_sid, PSID group_sid, | |
34 | mode_t mode, SECURITY_DESCRIPTOR &out_sd, acl_t &acl) | |
35 | { | |
36 | DWORD u_attribute, g_attribute, o_attribute; | |
37 | DWORD offset = 0; | |
38 | ||
39 | /* Initialize out SD */ | |
40 | if (!InitializeSecurityDescriptor (&out_sd, SECURITY_DESCRIPTOR_REVISION)) | |
41 | Log (LOG_TIMESTAMP) << "InitializeSecurityDescriptor(" << fname | |
42 | << ") failed: " << GetLastError () << endLog; | |
43 | out_sd.Control |= SE_DACL_PROTECTED; | |
44 | ||
45 | /* Initialize ACL and fill with almost POSIX-like permissions. | |
46 | Note that the current user always requires write permissions, otherwise | |
47 | creating files in directories with restricted permissions fails. */ | |
48 | if (!InitializeAcl (&acl.acl , sizeof acl, ACL_REVISION)) | |
49 | Log (LOG_TIMESTAMP) << "InitializeAcl(" << fname << ") failed: " | |
50 | << GetLastError () << endLog; | |
51 | /* USER */ | |
52 | /* Default user to current user. */ | |
53 | if (!owner_sid) | |
54 | owner_sid = ownerSID.user.User.Sid; | |
55 | u_attribute = STANDARD_RIGHTS_ALL | FILE_GENERIC_READ | FILE_GENERIC_WRITE; | |
56 | if (mode & 0100) // S_IXUSR | |
57 | u_attribute |= FILE_GENERIC_EXECUTE; | |
58 | if ((mode & 0300) == 0300) // S_IWUSR | S_IXUSR | |
59 | u_attribute |= FILE_DELETE_CHILD; | |
60 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE, | |
61 | u_attribute, owner_sid)) | |
62 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
63 | << ", owner) failed: " << GetLastError () << endLog; | |
64 | else | |
65 | offset++; | |
66 | /* GROUP */ | |
67 | /* Default group to current primary group. */ | |
68 | if (!group_sid) | |
69 | group_sid = groupSID; | |
70 | g_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES; | |
71 | if (mode & 0040) // S_IRGRP | |
72 | g_attribute |= FILE_GENERIC_READ; | |
73 | if (mode & 0020) // S_IWGRP | |
74 | g_attribute |= FILE_GENERIC_WRITE; | |
75 | if (mode & 0010) // S_IXGRP | |
76 | g_attribute |= FILE_GENERIC_EXECUTE; | |
77 | if ((mode & 01030) == 00030) // S_IWGRP | S_IXGRP, !S_ISVTX | |
78 | g_attribute |= FILE_DELETE_CHILD; | |
79 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE, | |
80 | g_attribute, group_sid)) | |
81 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
82 | << ", group) failed: " << GetLastError () << endLog; | |
83 | else | |
84 | offset++; | |
85 | /* OTHER */ | |
86 | o_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES; | |
87 | if (mode & 0004) // S_IROTH | |
88 | o_attribute |= FILE_GENERIC_READ; | |
89 | if (mode & 0002) // S_IWOTH | |
90 | o_attribute |= FILE_GENERIC_WRITE; | |
91 | if (mode & 0001) // S_IXOTH | |
92 | o_attribute |= FILE_GENERIC_EXECUTE; | |
93 | if ((mode & 01003) == 00003) // S_IWOTH | S_IXOTH, !S_ISVTX | |
94 | o_attribute |= FILE_DELETE_CHILD; | |
95 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE, | |
96 | o_attribute, everyOneSID.theSID ())) | |
97 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
98 | << ", everyone) failed: " << GetLastError () << endLog; | |
99 | else | |
100 | offset++; | |
101 | if (mode & 07000) /* At least one of S_ISUID, S_ISGID, S_ISVTX */ | |
102 | { | |
103 | DWORD attribute = 0; | |
104 | if (mode & 04000) // S_ISUID | |
105 | attribute |= FILE_APPEND_DATA; | |
106 | if (mode & 02000) // S_ISGID | |
107 | attribute |= FILE_WRITE_DATA; | |
108 | if (mode & 01000) // S_ISVTX | |
109 | attribute |= FILE_READ_DATA; | |
110 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE, | |
111 | attribute, nullSID.theSID ())) | |
112 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
113 | << ", null) failed: " << GetLastError () << endLog; | |
114 | else | |
115 | offset++; | |
116 | } | |
117 | /* For directories, we also add inherit-only ACEs for CREATOR OWNER, | |
118 | CREATOR GROUP, and EVERYONE (aka OTHER). */ | |
119 | if (mode & S_IFDIR) | |
120 | { | |
121 | if (mode & 01000) // S_ISVTX | |
122 | { | |
123 | /* Don't allow default write permissions for group and other | |
124 | in a S_ISVTX dir. */ | |
125 | /* GROUP */ | |
126 | g_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES; | |
127 | if (mode & 0040) // S_IRGRP | |
128 | g_attribute |= FILE_GENERIC_READ; | |
129 | if (mode & 0010) // S_IXGRP | |
130 | g_attribute |= FILE_GENERIC_EXECUTE; | |
131 | /* OTHER */ | |
132 | o_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES; | |
133 | if (mode & 0004) // S_IROTH | |
134 | o_attribute |= FILE_GENERIC_READ; | |
135 | if (mode & 0001) // S_IXOTH | |
136 | o_attribute |= FILE_GENERIC_EXECUTE; | |
137 | } | |
138 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE, | |
139 | u_attribute, cr_ownerSID.theSID ())) | |
140 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
141 | << ", creator owner) failed: " | |
142 | << GetLastError () << endLog; | |
143 | else | |
144 | offset++; | |
145 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE, | |
146 | g_attribute, cr_groupSID.theSID ())) | |
147 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
148 | << ", creator group) failed: " | |
149 | << GetLastError () << endLog; | |
150 | else | |
151 | offset++; | |
152 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE, | |
153 | o_attribute, everyOneSID.theSID ())) | |
154 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
155 | << ", everyone inherit) failed: " | |
156 | << GetLastError () << endLog; | |
157 | else | |
158 | offset++; | |
159 | } | |
160 | ||
161 | /* Set SD's DACL to just created ACL. */ | |
162 | if (!SetSecurityDescriptorDacl (&out_sd, TRUE, &acl.acl, FALSE)) | |
163 | Log (LOG_TIMESTAMP) << "SetSecurityDescriptorDacl(" << fname | |
164 | << ") failed: " << GetLastError () << endLog; | |
165 | return &out_sd; | |
166 | } | |
167 | ||
168 | void | |
169 | NTSecurity::NoteFailedAPI (const std::string &api) | |
170 | { | |
171 | Log (LOG_TIMESTAMP) << api << "() failed: " << GetLastError () << endLog; | |
172 | } | |
173 | ||
174 | void | |
175 | NTSecurity::initialiseWellKnownSIDs () | |
176 | { | |
177 | SID_IDENTIFIER_AUTHORITY n_sid_auth = { SECURITY_NULL_SID_AUTHORITY }; | |
178 | /* Get the SID for "NULL" S-1-0-0 */ | |
179 | if (!AllocateAndInitializeSid (&n_sid_auth, 1, SECURITY_NULL_RID, | |
180 | 0, 0, 0, 0, 0, 0, 0, &nullSID.theSID ())) | |
181 | return; | |
182 | SID_IDENTIFIER_AUTHORITY e_sid_auth = { SECURITY_WORLD_SID_AUTHORITY }; | |
183 | /* Get the SID for "Everyone" S-1-1-0 */ | |
184 | if (!AllocateAndInitializeSid (&e_sid_auth, 1, SECURITY_WORLD_RID, | |
185 | 0, 0, 0, 0, 0, 0, 0, &everyOneSID.theSID ())) | |
186 | return; | |
187 | SID_IDENTIFIER_AUTHORITY nt_sid_auth = { SECURITY_NT_AUTHORITY }; | |
188 | /* Get the SID for "Administrators" S-1-5-32-544 */ | |
189 | if (!AllocateAndInitializeSid (&nt_sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID, | |
190 | DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, | |
191 | &administratorsSID.theSID ())) | |
192 | return; | |
193 | /* Get the SID for "Users" S-1-5-32-545 */ | |
194 | if (!AllocateAndInitializeSid (&nt_sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID, | |
195 | DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, | |
196 | &usersSID.theSID ())) | |
197 | return; | |
198 | SID_IDENTIFIER_AUTHORITY c_sid_auth = { SECURITY_CREATOR_SID_AUTHORITY }; | |
199 | /* Get the SID for "CREATOR OWNER" S-1-3-0 */ | |
200 | if (!AllocateAndInitializeSid (&c_sid_auth, 1, SECURITY_CREATOR_OWNER_RID, | |
201 | 0, 0, 0, 0, 0, 0, 0, &cr_ownerSID.theSID ())) | |
202 | return; | |
203 | /* Get the SID for "CREATOR GROUP" S-1-3-1 */ | |
204 | if (!AllocateAndInitializeSid (&c_sid_auth, 1, SECURITY_CREATOR_GROUP_RID, | |
205 | 0, 0, 0, 0, 0, 0, 0, &cr_groupSID.theSID ())) | |
206 | return; | |
207 | wellKnownSIDsinitialized (true); | |
208 | } | |
209 | ||
210 | void | |
211 | NTSecurity::setDefaultDACL () | |
212 | { | |
213 | /* To assure that the created files have a useful ACL, the | |
214 | default DACL in the process token is set to full access to | |
215 | everyone. This applies to files and subdirectories created | |
216 | in directories which don't propagate permissions to child | |
217 | objects. | |
218 | To assure that the files group is meaningful, a token primary | |
219 | group of None is changed to Users or Administrators. | |
220 | This is the fallback if real POSIX permissions don't | |
221 | work for some reason. */ | |
222 | ||
223 | /* Create a buffer which has enough room to contain the TOKEN_DEFAULT_DACL | |
224 | structure plus an ACL with one ACE. */ | |
225 | size_t bufferSize = sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE) | |
226 | + GetLengthSid (everyOneSID.theSID ()) - sizeof (DWORD); | |
227 | ||
228 | std::unique_ptr<char[]> buf (new char[bufferSize]); | |
229 | ||
230 | /* First initialize the TOKEN_DEFAULT_DACL structure. */ | |
231 | PACL dacl = (PACL) buf.get (); | |
232 | ||
233 | /* Initialize the ACL for containing one ACE. */ | |
234 | if (!InitializeAcl (dacl, bufferSize, ACL_REVISION)) | |
235 | { | |
236 | NoteFailedAPI ("InitializeAcl"); | |
237 | return; | |
238 | } | |
239 | ||
240 | /* Create the ACE which grants full access to "Everyone" and store it | |
241 | in dacl. */ | |
242 | if (!AddAccessAllowedAceEx (dacl, ACL_REVISION, NO_INHERIT_ACE, | |
243 | GENERIC_ALL, everyOneSID.theSID ())) | |
244 | { | |
245 | NoteFailedAPI ("AddAccessAllowedAceEx"); | |
246 | return; | |
247 | } | |
248 | ||
249 | /* Set the default DACL to the above computed ACL. */ | |
250 | if (!SetTokenInformation (token.theHANDLE(), TokenDefaultDacl, &dacl, | |
251 | bufferSize)) | |
252 | NoteFailedAPI ("SetTokenInformation"); | |
253 | } | |
254 | ||
255 | void | |
256 | NTSecurity::setBackupPrivileges () | |
257 | { | |
258 | LUID backup, restore; | |
259 | if (!LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &backup)) | |
260 | NoteFailedAPI ("LookupPrivilegeValue"); | |
261 | else if (!LookupPrivilegeValue (NULL, SE_RESTORE_NAME, &restore)) | |
262 | NoteFailedAPI ("LookupPrivilegeValue"); | |
263 | else | |
264 | { | |
265 | PTOKEN_PRIVILEGES new_privs; | |
266 | ||
267 | new_privs = (PTOKEN_PRIVILEGES) alloca (sizeof (TOKEN_PRIVILEGES) | |
268 | + sizeof (LUID_AND_ATTRIBUTES)); | |
269 | new_privs->PrivilegeCount = 2; | |
270 | new_privs->Privileges[0].Luid = backup; | |
271 | new_privs->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
272 | new_privs->Privileges[1].Luid = restore; | |
273 | new_privs->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; | |
274 | if (!AdjustTokenPrivileges (token.theHANDLE (), FALSE, new_privs, | |
275 | 0, NULL, NULL)) | |
276 | NoteFailedAPI ("AdjustTokenPrivileges"); | |
277 | else if (GetLastError () == ERROR_NOT_ALL_ASSIGNED) | |
278 | Log (LOG_TIMESTAMP) << "User has NO backup/restore rights" << endLog; | |
279 | else | |
280 | Log (LOG_TIMESTAMP) << "User has backup/restore rights" << endLog; | |
281 | } | |
282 | } | |
283 | ||
284 | void | |
285 | NTSecurity::resetPrimaryGroup () | |
286 | { | |
287 | if (primaryGroupSID.pgrp.PrimaryGroup) | |
288 | { | |
289 | Log (LOG_TIMESTAMP) << "Changing gid back to original" << endLog; | |
290 | if (!SetTokenInformation (token.theHANDLE (), TokenPrimaryGroup, | |
291 | &primaryGroupSID, sizeof primaryGroupSID)) | |
292 | NoteFailedAPI ("SetTokenInformation"); | |
293 | } | |
294 | } | |
295 | ||
296 | void | |
297 | NTSecurity::setAdminGroup () | |
298 | { | |
299 | TOKEN_PRIMARY_GROUP tpg; | |
300 | ||
301 | tpg.PrimaryGroup = administratorsSID.theSID (); | |
302 | Log (LOG_TIMESTAMP) << "Changing gid to Administrators" << endLog; | |
303 | if (!SetTokenInformation (token.theHANDLE (), TokenPrimaryGroup, | |
304 | &tpg, sizeof tpg)) | |
305 | NoteFailedAPI ("SetTokenInformation"); | |
306 | else | |
307 | groupSID = administratorsSID.theSID (); | |
308 | } | |
309 | ||
310 | void | |
311 | NTSecurity::setDefaultSecurity () | |
312 | { | |
313 | /* Get the processes access token. */ | |
314 | if (!OpenProcessToken (GetCurrentProcess (), | |
315 | TOKEN_READ | TOKEN_ADJUST_DEFAULT | |
316 | | TOKEN_ADJUST_PRIVILEGES, &token.theHANDLE ())) | |
317 | { | |
318 | NoteFailedAPI ("OpenProcessToken"); | |
319 | return; | |
320 | } | |
321 | ||
322 | /* Set backup and restore privileges if available. */ | |
323 | setBackupPrivileges (); | |
324 | ||
325 | /* Log if symlink creation privilege is available. */ | |
326 | if (!hasSymlinkCreationRights()) | |
327 | Log (LOG_TIMESTAMP) << "User has NO symlink creation right" << endLog; | |
328 | else | |
329 | Log (LOG_TIMESTAMP) << "User has symlink creation right" << endLog; | |
330 | ||
331 | /* If initializing the well-known SIDs didn't work, we're finished here. */ | |
332 | if (!wellKnownSIDsinitialized ()) | |
333 | return; | |
334 | ||
335 | /* Set the default DACL to all permissions for everyone as a fallback. */ | |
336 | setDefaultDACL (); | |
337 | ||
338 | /* Get the user */ | |
339 | if (!GetTokenInformation (token.theHANDLE (), TokenUser, &ownerSID, | |
340 | sizeof ownerSID, &size)) | |
341 | { | |
342 | NoteFailedAPI ("GetTokenInformation(user)"); | |
343 | return; | |
344 | } | |
345 | /* Make it the owner */ | |
346 | TOKEN_OWNER owner = { ownerSID.user.User.Sid }; | |
347 | if (!SetTokenInformation (token.theHANDLE (), TokenOwner, &owner, | |
348 | sizeof owner)) | |
349 | { | |
350 | NoteFailedAPI ("SetTokenInformation(owner)"); | |
351 | return; | |
352 | } | |
353 | /* Get original primary group */ | |
354 | if (!GetTokenInformation (token.theHANDLE (), TokenPrimaryGroup, | |
355 | &primaryGroupSID, sizeof primaryGroupSID, &size)) | |
356 | { | |
357 | NoteFailedAPI("GetTokenInformation(pgrp)"); | |
358 | primaryGroupSID.pgrp.PrimaryGroup = (PSID) NULL; | |
359 | } | |
360 | groupSID = primaryGroupSID.pgrp.PrimaryGroup; | |
361 | } | |
362 | ||
363 | bool | |
364 | NTSecurity::isRunAsAdmin () | |
365 | { | |
366 | BOOL is_run_as_admin = FALSE; | |
367 | if (!CheckTokenMembership(NULL, administratorsSID.theSID (), &is_run_as_admin)) | |
368 | NoteFailedAPI("CheckTokenMembership(administratorsSID)"); | |
369 | return (is_run_as_admin == TRUE); | |
370 | } | |
371 | ||
372 | bool | |
373 | NTSecurity::hasSymlinkCreationRights () | |
374 | { | |
375 | LUID symlink; | |
376 | if (!LookupPrivilegeValue (NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &symlink)) | |
377 | { | |
378 | NoteFailedAPI ("LookupPrivilegeValue"); | |
379 | return FALSE; | |
380 | } | |
381 | ||
382 | DWORD size; | |
383 | GetTokenInformation (token.theHANDLE (), TokenPrivileges, NULL, 0, &size); | |
384 | /* Will fail ERROR_INSUFFICIENT_BUFFER, but updates size */ | |
385 | ||
386 | TOKEN_PRIVILEGES *privileges = (TOKEN_PRIVILEGES *)alloca(size); | |
387 | if (!GetTokenInformation (token.theHANDLE (), TokenPrivileges, privileges, | |
388 | size, &size)) | |
389 | { | |
390 | NoteFailedAPI ("GetTokenInformation(privileges)"); | |
391 | return FALSE; | |
392 | } | |
393 | ||
394 | unsigned int i; | |
395 | for (i = 0; i < privileges->PrivilegeCount; i++) | |
396 | { | |
397 | if (memcmp(&privileges->Privileges[i].Luid, &symlink, sizeof(LUID)) == 0) | |
398 | { | |
399 | return TRUE; | |
400 | } | |
401 | } | |
402 | ||
403 | return FALSE; | |
404 | } | |
405 | ||
406 | VersionInfo::VersionInfo () | |
407 | { | |
408 | v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); | |
409 | if (GetVersionEx (&v) == 0) | |
410 | { | |
411 | Log (LOG_PLAIN) << "GetVersionEx () failed: " << GetLastError () | |
412 | << endLog; | |
413 | ||
414 | /* If GetVersionEx fails we really should bail with an error of some kind, | |
415 | but for now just assume we're on NT and continue. */ | |
416 | v.dwPlatformId = VER_PLATFORM_WIN32_NT; | |
417 | } | |
418 | } | |
419 | ||
420 | /* This is the Construct on First Use idiom to avoid static initialization | |
421 | order problems. */ | |
422 | VersionInfo& GetVer () | |
423 | { | |
424 | static VersionInfo *vi = new VersionInfo (); | |
425 | return *vi; | |
426 | } | |
427 | ||
428 | /* Identify native machine arch if we are running under WoW */ | |
429 | USHORT | |
430 | WowNativeMachine () | |
431 | { | |
432 | typedef BOOL (WINAPI *PFNISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *); | |
433 | PFNISWOW64PROCESS2 pfnIsWow64Process2 = (PFNISWOW64PROCESS2)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process2"); | |
434 | ||
435 | typedef BOOL (WINAPI *PFNISWOW64PROCESS)(HANDLE, PBOOL); | |
436 | PFNISWOW64PROCESS pfnIsWow64Process = (PFNISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process"); | |
437 | ||
438 | USHORT processMachine, nativeMachine; | |
439 | if ((pfnIsWow64Process2) && | |
440 | (pfnIsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine))) | |
441 | return nativeMachine; | |
442 | else if (pfnIsWow64Process) { | |
443 | #ifdef _X86_ | |
444 | BOOL bIsWow64 = FALSE; | |
445 | if (pfnIsWow64Process(GetCurrentProcess(), &bIsWow64)) | |
446 | return bIsWow64 ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_MACHINE_I386; | |
447 | #endif | |
448 | } | |
449 | ||
450 | #ifdef __x86_64__ | |
451 | return IMAGE_FILE_MACHINE_AMD64; | |
452 | #else | |
453 | return IMAGE_FILE_MACHINE_I386; | |
454 | #endif | |
455 | } | |
456 | ||
457 | /* Identify machine arch for the current process */ | |
458 | USHORT | |
459 | WindowsProcessMachine () | |
460 | { | |
461 | #if defined(__x86_64__) | |
462 | USHORT processMachine = IMAGE_FILE_MACHINE_AMD64; | |
463 | #elif defined(__i386__) | |
464 | USHORT processMachine = IMAGE_FILE_MACHINE_I386; | |
465 | #elif defined(__aarch64__) | |
466 | USHORT processMachine = IMAGE_FILE_MACHINE_ARM64; | |
467 | #else | |
468 | #error "Unknown architecture" | |
469 | #endif | |
470 | return processMachine; | |
471 | } | |
472 | ||
473 | /* Convert a machine arch identifier to a string */ | |
474 | const std::string | |
475 | machine_name(USHORT machine) | |
476 | { | |
477 | switch (machine) | |
478 | { | |
479 | case IMAGE_FILE_MACHINE_I386: | |
480 | return "x86"; | |
481 | break; | |
482 | case IMAGE_FILE_MACHINE_AMD64: | |
483 | return "x86_64"; | |
484 | break; | |
485 | case IMAGE_FILE_MACHINE_ARM64: | |
486 | return "ARM64"; | |
487 | break; | |
488 | default: | |
489 | std::stringstream machine_desc; | |
490 | machine_desc << std::hex << machine; | |
491 | return machine_desc.str(); | |
492 | } | |
493 | } | |
494 | ||
495 | const std::wstring | |
496 | LoadStringWEx(UINT uID, UINT langId) | |
497 | { | |
498 | HINSTANCE hInstance = GetModuleHandle(NULL); | |
499 | ||
500 | // Convert the string ID into a bundle number | |
501 | LPCSTR bundle = MAKEINTRESOURCE(uID / 16 + 1); | |
502 | HRSRC hRes = ::FindResourceEx(hInstance, RT_STRING, bundle, langId); | |
503 | if (hRes) | |
504 | { | |
505 | HGLOBAL h = ::LoadResource(hInstance, hRes); | |
506 | if (h) | |
507 | { | |
508 | HGLOBAL hGlob = ::LockResource(h); | |
509 | ||
510 | // walk string bundle | |
511 | wchar_t *buf = (wchar_t *)hGlob; | |
512 | for (unsigned int i = 0; i < (uID & 15); i++) | |
513 | { | |
514 | buf += 1 + (UINT)*buf; | |
515 | } | |
516 | ||
517 | int len = *buf; | |
518 | return std::wstring(buf + 1, len); | |
519 | } | |
520 | } | |
521 | // N.B.: Due to the way string bundles are encoded, there's no difference | |
522 | // between an absent string resource whose bundle is present, and a string | |
523 | // resource containing the null string. | |
524 | return L""; | |
525 | } | |
526 | ||
527 | const std::wstring | |
528 | LoadStringW(unsigned int uID) | |
529 | { | |
530 | wchar_t *buf; | |
531 | ||
532 | int len = ::LoadStringW(GetModuleHandle(NULL), uID, (LPWSTR)&buf, 0); | |
533 | if (len > 0) | |
534 | return std::wstring(buf, len); | |
535 | ||
536 | // if empty or absent, fallback to the untranslated string | |
537 | return LoadStringWEx(uID, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); | |
538 | } | |
539 | ||
540 | const std::string | |
541 | LoadStringUtf8(unsigned int uID) | |
542 | { | |
543 | return wstring_to_string(LoadStringW(uID)); | |
544 | } | |
545 | ||
546 | bool | |
547 | is_developer_mode(void) | |
548 | { | |
549 | HKEY hKey; | |
550 | LSTATUS err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, KEY_READ, &hKey); | |
551 | if (err != ERROR_SUCCESS) | |
552 | return false; | |
553 | ||
554 | DWORD value; | |
555 | DWORD size = sizeof(DWORD); | |
556 | err = RegQueryValueExW(hKey, L"AllowDevelopmentWithoutDevLicense", NULL, NULL, reinterpret_cast<LPBYTE>(&value), &size); | |
557 | RegCloseKey(hKey); | |
558 | if (err != ERROR_SUCCESS) | |
559 | return false; | |
560 | ||
561 | return value != 0; | |
562 | } |