game-capture-file-init.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. #include <windows.h>
  2. #include <strsafe.h>
  3. #include <shlobj.h>
  4. #include <aclapi.h>
  5. #include <sddl.h>
  6. #include <obs-module.h>
  7. #include <util/windows/win-version.h>
  8. #include <util/platform.h>
  9. #include <util/c99defs.h>
  10. #include <util/base.h>
  11. /* ------------------------------------------------------------------------- */
  12. /* helper funcs */
  13. static bool has_elevation_internal()
  14. {
  15. SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
  16. PSID sid = NULL;
  17. BOOL elevated = false;
  18. BOOL success;
  19. success = AllocateAndInitializeSid(&sia, 2, SECURITY_BUILTIN_DOMAIN_RID,
  20. DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0,
  21. 0, 0, &sid);
  22. if (success && sid) {
  23. CheckTokenMembership(NULL, sid, &elevated);
  24. FreeSid(sid);
  25. }
  26. return elevated;
  27. }
  28. static bool has_elevation()
  29. {
  30. static bool elevated = false;
  31. static bool initialized = false;
  32. if (!initialized) {
  33. elevated = has_elevation_internal();
  34. initialized = true;
  35. }
  36. return elevated;
  37. }
  38. static bool add_aap_perms(const wchar_t *dir)
  39. {
  40. PSECURITY_DESCRIPTOR sd = NULL;
  41. SID *aap_sid = NULL;
  42. SID *bu_sid = NULL;
  43. PACL new_dacl1 = NULL;
  44. PACL new_dacl2 = NULL;
  45. bool success = false;
  46. PACL dacl;
  47. if (GetNamedSecurityInfoW(dir, SE_FILE_OBJECT,
  48. DACL_SECURITY_INFORMATION, NULL, NULL, &dacl,
  49. NULL, &sd) != ERROR_SUCCESS) {
  50. goto fail;
  51. }
  52. EXPLICIT_ACCESSW ea = {0};
  53. ea.grfAccessPermissions = GENERIC_READ | GENERIC_EXECUTE;
  54. ea.grfAccessMode = GRANT_ACCESS;
  55. ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  56. ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
  57. /* ALL_APP_PACKAGES */
  58. ConvertStringSidToSidW(L"S-1-15-2-1", &aap_sid);
  59. ea.Trustee.ptstrName = (wchar_t *)aap_sid;
  60. if (SetEntriesInAclW(1, &ea, dacl, &new_dacl1) != ERROR_SUCCESS) {
  61. goto fail;
  62. }
  63. ea.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE |
  64. GENERIC_EXECUTE;
  65. /* BUILTIN_USERS */
  66. ConvertStringSidToSidW(L"S-1-5-32-545", &bu_sid);
  67. ea.Trustee.ptstrName = (wchar_t *)bu_sid;
  68. DWORD s = SetEntriesInAclW(1, &ea, new_dacl1, &new_dacl2);
  69. if (s != ERROR_SUCCESS) {
  70. goto fail;
  71. }
  72. if (SetNamedSecurityInfoW((wchar_t *)dir, SE_FILE_OBJECT,
  73. DACL_SECURITY_INFORMATION, NULL, NULL,
  74. new_dacl2, NULL) != ERROR_SUCCESS) {
  75. goto fail;
  76. }
  77. success = true;
  78. fail:
  79. if (sd)
  80. LocalFree(sd);
  81. if (new_dacl1)
  82. LocalFree(new_dacl1);
  83. if (new_dacl2)
  84. LocalFree(new_dacl2);
  85. if (aap_sid)
  86. LocalFree(aap_sid);
  87. if (bu_sid)
  88. LocalFree(bu_sid);
  89. return success;
  90. }
  91. static inline bool file_exists(const wchar_t *path)
  92. {
  93. WIN32_FIND_DATAW wfd;
  94. HANDLE h = FindFirstFileW(path, &wfd);
  95. if (h == INVALID_HANDLE_VALUE)
  96. return false;
  97. FindClose(h);
  98. return true;
  99. }
  100. static LSTATUS get_reg(HKEY hkey, LPCWSTR sub_key, LPCWSTR value_name, bool b64)
  101. {
  102. HKEY key;
  103. LSTATUS status;
  104. DWORD flags = b64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
  105. DWORD size = sizeof(DWORD);
  106. DWORD val;
  107. status = RegOpenKeyEx(hkey, sub_key, 0, KEY_READ | flags, &key);
  108. if (status == ERROR_SUCCESS) {
  109. status = RegQueryValueExW(key, value_name, NULL, NULL,
  110. (LPBYTE)&val, &size);
  111. RegCloseKey(key);
  112. }
  113. return status;
  114. }
  115. #define get_programdata_path(path, subpath) \
  116. do { \
  117. SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, \
  118. SHGFP_TYPE_CURRENT, path); \
  119. StringCbCatW(path, sizeof(path), L"\\"); \
  120. StringCbCatW(path, sizeof(path), subpath); \
  121. } while (false)
  122. #define make_filename(str, name, ext) \
  123. do { \
  124. StringCbCatW(str, sizeof(str), name); \
  125. StringCbCatW(str, sizeof(str), b64 ? L"64" : L"32"); \
  126. StringCbCatW(str, sizeof(str), ext); \
  127. } while (false)
  128. /* ------------------------------------------------------------------------- */
  129. /* function to get the path to the hook */
  130. static bool programdata64_hook_exists = false;
  131. static bool programdata32_hook_exists = false;
  132. char *get_hook_path(bool b64)
  133. {
  134. wchar_t path[MAX_PATH];
  135. get_programdata_path(path, L"obs-studio-hook\\");
  136. make_filename(path, L"graphics-hook", L".dll");
  137. if ((b64 && programdata64_hook_exists) ||
  138. (!b64 && programdata32_hook_exists)) {
  139. char *path_utf8 = NULL;
  140. os_wcs_to_utf8_ptr(path, 0, &path_utf8);
  141. return path_utf8;
  142. }
  143. return obs_module_file(b64 ? "graphics-hook64.dll"
  144. : "graphics-hook32.dll");
  145. }
  146. /* ------------------------------------------------------------------------- */
  147. /* initialization */
  148. #define IMPLICIT_LAYERS L"SOFTWARE\\Khronos\\Vulkan\\ImplicitLayers"
  149. static bool update_hook_file(bool b64)
  150. {
  151. wchar_t temp[MAX_PATH];
  152. wchar_t src[MAX_PATH];
  153. wchar_t dst[MAX_PATH];
  154. wchar_t src_json[MAX_PATH];
  155. wchar_t dst_json[MAX_PATH];
  156. StringCbCopyW(temp, sizeof(temp),
  157. L"..\\..\\data\\obs-plugins\\"
  158. L"win-capture\\");
  159. make_filename(temp, L"obs-vulkan", L".json");
  160. if (_wfullpath(src_json, temp, MAX_PATH) == NULL)
  161. return false;
  162. StringCbCopyW(temp, sizeof(temp),
  163. L"..\\..\\data\\obs-plugins\\"
  164. L"win-capture\\");
  165. make_filename(temp, L"graphics-hook", L".dll");
  166. if (_wfullpath(src, temp, MAX_PATH) == NULL)
  167. return false;
  168. get_programdata_path(temp, L"obs-studio-hook\\");
  169. StringCbCopyW(dst_json, sizeof(dst_json), temp);
  170. StringCbCopyW(dst, sizeof(dst), temp);
  171. make_filename(dst_json, L"obs-vulkan", L".json");
  172. make_filename(dst, L"graphics-hook", L".dll");
  173. if (!file_exists(src)) {
  174. return false;
  175. }
  176. if (!file_exists(src_json)) {
  177. return false;
  178. }
  179. if (!file_exists(dst) || !file_exists(dst_json)) {
  180. CreateDirectoryW(temp, NULL);
  181. if (has_elevation())
  182. add_aap_perms(temp);
  183. if (!CopyFileW(src_json, dst_json, false))
  184. return false;
  185. if (!CopyFileW(src, dst, false))
  186. return false;
  187. return true;
  188. }
  189. if (has_elevation())
  190. add_aap_perms(temp);
  191. struct win_version_info ver_src = {0};
  192. struct win_version_info ver_dst = {0};
  193. if (!get_dll_ver(src, &ver_src))
  194. return false;
  195. #ifndef _DEBUG
  196. if (!get_dll_ver(dst, &ver_dst))
  197. return false;
  198. #endif
  199. /* if source is greater than dst, overwrite new file */
  200. while (win_version_compare(&ver_dst, &ver_src) < 0) {
  201. if (!CopyFileW(src_json, dst_json, false))
  202. return false;
  203. if (!CopyFileW(src, dst, false))
  204. return false;
  205. if (!get_dll_ver(dst, &ver_dst))
  206. return false;
  207. }
  208. /* do not use if major version incremented in target compared to
  209. * ours */
  210. if (ver_dst.major > ver_src.major) {
  211. return false;
  212. }
  213. return true;
  214. }
  215. #define warn(format, ...) \
  216. blog(LOG_WARNING, "%s: " format, "[Vulkan Capture Init]", ##__VA_ARGS__)
  217. /* Sets vulkan layer registry if it doesn't already exist */
  218. static void init_vulkan_registry(bool b64)
  219. {
  220. DWORD flags = b64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
  221. HKEY key = NULL;
  222. LSTATUS s;
  223. wchar_t path[MAX_PATH];
  224. get_programdata_path(path, L"obs-studio-hook\\");
  225. make_filename(path, L"obs-vulkan", L".json");
  226. s = get_reg(HKEY_LOCAL_MACHINE, IMPLICIT_LAYERS, path, b64);
  227. if (s == ERROR_FILE_NOT_FOUND) {
  228. s = get_reg(HKEY_CURRENT_USER, IMPLICIT_LAYERS, path, b64);
  229. if (s != ERROR_FILE_NOT_FOUND && s != ERROR_SUCCESS) {
  230. warn("Failed to query registry keys: %d", (int)s);
  231. goto finish;
  232. }
  233. if (s == ERROR_SUCCESS && has_elevation()) {
  234. s = RegOpenKeyEx(HKEY_CURRENT_USER, IMPLICIT_LAYERS, 0,
  235. KEY_WRITE | flags, &key);
  236. if (s == ERROR_SUCCESS) {
  237. RegDeleteValueW(key, path);
  238. RegCloseKey(key);
  239. s = -1;
  240. key = NULL;
  241. }
  242. }
  243. } else if (s != ERROR_SUCCESS) {
  244. warn("Failed to query registry keys: %d", (int)s);
  245. goto finish;
  246. }
  247. if (s == ERROR_SUCCESS) {
  248. goto finish;
  249. }
  250. HKEY type = has_elevation() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  251. DWORD temp;
  252. s = RegCreateKeyExW(type, IMPLICIT_LAYERS, 0, NULL, 0,
  253. KEY_WRITE | flags, NULL, &key, &temp);
  254. if (s != ERROR_SUCCESS) {
  255. warn("Failed to create registry key");
  256. goto finish;
  257. }
  258. DWORD zero = 0;
  259. s = RegSetValueExW(key, path, 0, REG_DWORD, (const BYTE *)&zero,
  260. sizeof(zero));
  261. if (s != ERROR_SUCCESS) {
  262. warn("Failed to set registry value");
  263. }
  264. finish:
  265. if (key)
  266. RegCloseKey(key);
  267. }
  268. void init_hook_files()
  269. {
  270. if (update_hook_file(true)) {
  271. programdata64_hook_exists = true;
  272. init_vulkan_registry(true);
  273. }
  274. if (update_hook_file(false)) {
  275. programdata32_hook_exists = true;
  276. init_vulkan_registry(false);
  277. }
  278. }