1 #include <windows.h>
2 #include <psapi.h>
3 #include <inttypes.h>
4 #include "graphics-hook.h"
5 #include "../graphics-hook-ver.h"
6 #include "../obfuscate.h"
7 
8 #define DEBUG_OUTPUT
9 
10 #ifdef DEBUG_OUTPUT
11 #define DbgOut(x) OutputDebugStringA(x)
12 #else
13 #define DbgOut(x)
14 #endif
15 
16 struct thread_data {
17 	CRITICAL_SECTION mutexes[NUM_BUFFERS];
18 	CRITICAL_SECTION data_mutex;
19 	void *volatile cur_data;
20 	uint8_t *shmem_textures[2];
21 	HANDLE copy_thread;
22 	HANDLE copy_event;
23 	HANDLE stop_event;
24 	volatile int cur_tex;
25 	unsigned int pitch;
26 	unsigned int cy;
27 	volatile bool locked_textures[NUM_BUFFERS];
28 };
29 
30 ipc_pipe_client_t pipe = {0};
31 HANDLE signal_restart = NULL;
32 HANDLE signal_stop = NULL;
33 HANDLE signal_ready = NULL;
34 HANDLE signal_exit = NULL;
35 static HANDLE signal_init = NULL;
36 HANDLE tex_mutexes[2] = {NULL, NULL};
37 static HANDLE filemap_hook_info = NULL;
38 
39 static HINSTANCE dll_inst = NULL;
40 static volatile bool stop_loop = false;
41 static HANDLE dup_hook_mutex = NULL;
42 static HANDLE capture_thread = NULL;
43 char system_path[MAX_PATH] = {0};
44 char process_name[MAX_PATH] = {0};
45 wchar_t keepalive_name[64] = {0};
46 HWND dummy_window = NULL;
47 
48 static unsigned int shmem_id_counter = 0;
49 static void *shmem_info = NULL;
50 static HANDLE shmem_file_handle = 0;
51 
52 static struct thread_data thread_data = {0};
53 
54 volatile bool active = false;
55 struct hook_info *global_hook_info = NULL;
56 
wait_for_dll_main_finish(HANDLE thread_handle)57 static inline void wait_for_dll_main_finish(HANDLE thread_handle)
58 {
59 	if (thread_handle) {
60 		WaitForSingleObject(thread_handle, 100);
61 		CloseHandle(thread_handle);
62 	}
63 }
64 
init_pipe(void)65 bool init_pipe(void)
66 {
67 	char new_name[64];
68 	sprintf(new_name, "%s%lu", PIPE_NAME, GetCurrentProcessId());
69 
70 	const bool success = ipc_pipe_client_open(&pipe, new_name);
71 	if (!success) {
72 		DbgOut("[OBS] Failed to open pipe\n");
73 	}
74 
75 	return success;
76 }
77 
init_event(const wchar_t * name,DWORD pid)78 static HANDLE init_event(const wchar_t *name, DWORD pid)
79 {
80 	HANDLE handle = create_event_plus_id(name, pid);
81 	if (!handle)
82 		hlog("Failed to get event '%s': %lu", name, GetLastError());
83 	return handle;
84 }
85 
init_mutex(const wchar_t * name,DWORD pid)86 static HANDLE init_mutex(const wchar_t *name, DWORD pid)
87 {
88 	HANDLE handle = create_mutex_plus_id(name, pid);
89 	if (!handle)
90 		hlog("Failed to open mutex '%s': %lu", name, GetLastError());
91 	return handle;
92 }
93 
init_signals(void)94 static inline bool init_signals(void)
95 {
96 	DWORD pid = GetCurrentProcessId();
97 
98 	signal_restart = init_event(EVENT_CAPTURE_RESTART, pid);
99 	if (!signal_restart) {
100 		return false;
101 	}
102 
103 	signal_stop = init_event(EVENT_CAPTURE_STOP, pid);
104 	if (!signal_stop) {
105 		return false;
106 	}
107 
108 	signal_ready = init_event(EVENT_HOOK_READY, pid);
109 	if (!signal_ready) {
110 		return false;
111 	}
112 
113 	signal_exit = init_event(EVENT_HOOK_EXIT, pid);
114 	if (!signal_exit) {
115 		return false;
116 	}
117 
118 	signal_init = init_event(EVENT_HOOK_INIT, pid);
119 	if (!signal_init) {
120 		return false;
121 	}
122 
123 	return true;
124 }
125 
init_mutexes(void)126 static inline bool init_mutexes(void)
127 {
128 	DWORD pid = GetCurrentProcessId();
129 
130 	tex_mutexes[0] = init_mutex(MUTEX_TEXTURE1, pid);
131 	if (!tex_mutexes[0]) {
132 		return false;
133 	}
134 
135 	tex_mutexes[1] = init_mutex(MUTEX_TEXTURE2, pid);
136 	if (!tex_mutexes[1]) {
137 		return false;
138 	}
139 
140 	return true;
141 }
142 
init_system_path(void)143 static inline bool init_system_path(void)
144 {
145 	UINT ret = GetSystemDirectoryA(system_path, MAX_PATH);
146 	if (!ret) {
147 		hlog("Failed to get windows system path: %lu", GetLastError());
148 		return false;
149 	}
150 
151 	return true;
152 }
153 
log_current_process(void)154 static inline void log_current_process(void)
155 {
156 	DWORD len = GetModuleBaseNameA(GetCurrentProcess(), NULL, process_name,
157 				       MAX_PATH);
158 	if (len > 0) {
159 		process_name[len] = 0;
160 		hlog("graphics-hook.dll loaded against process: %s",
161 		     process_name);
162 	} else {
163 		hlog("graphics-hook.dll loaded");
164 	}
165 
166 	hlog("(half life scientist) everything..  seems to be in order");
167 }
168 
init_hook_info(void)169 static inline bool init_hook_info(void)
170 {
171 	filemap_hook_info = create_hook_info(GetCurrentProcessId());
172 	if (!filemap_hook_info) {
173 		hlog("Failed to create hook info file mapping: %lu",
174 		     GetLastError());
175 		return false;
176 	}
177 
178 	global_hook_info = MapViewOfFile(filemap_hook_info, FILE_MAP_ALL_ACCESS,
179 					 0, 0, sizeof(struct hook_info));
180 	if (!global_hook_info) {
181 		hlog("Failed to map the hook info file mapping: %lu",
182 		     GetLastError());
183 		return false;
184 	}
185 
186 	return true;
187 }
188 
189 #define DEF_FLAGS (WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS)
190 
dummy_window_thread(LPVOID * unused)191 static DWORD WINAPI dummy_window_thread(LPVOID *unused)
192 {
193 	static const wchar_t dummy_window_class[] = L"temp_d3d_window_4039785";
194 	WNDCLASSW wc;
195 	MSG msg;
196 
197 	memset(&wc, 0, sizeof(wc));
198 	wc.style = CS_OWNDC;
199 	wc.hInstance = dll_inst;
200 	wc.lpfnWndProc = (WNDPROC)DefWindowProc;
201 	wc.lpszClassName = dummy_window_class;
202 
203 	if (!RegisterClass(&wc)) {
204 		hlog("Failed to create temp D3D window class: %lu",
205 		     GetLastError());
206 		return 0;
207 	}
208 
209 	dummy_window = CreateWindowExW(0, dummy_window_class, L"Temp Window",
210 				       DEF_FLAGS, 0, 0, 1, 1, NULL, NULL,
211 				       dll_inst, NULL);
212 	if (!dummy_window) {
213 		hlog("Failed to create temp D3D window: %lu", GetLastError());
214 		return 0;
215 	}
216 
217 	while (GetMessage(&msg, NULL, 0, 0)) {
218 		TranslateMessage(&msg);
219 		DispatchMessage(&msg);
220 	}
221 
222 	(void)unused;
223 	return 0;
224 }
225 
init_dummy_window_thread(void)226 static inline void init_dummy_window_thread(void)
227 {
228 	HANDLE thread =
229 		CreateThread(NULL, 0, dummy_window_thread, NULL, 0, NULL);
230 	if (!thread) {
231 		hlog("Failed to create temp D3D window thread: %lu",
232 		     GetLastError());
233 		return;
234 	}
235 
236 	CloseHandle(thread);
237 }
238 
init_hook(HANDLE thread_handle)239 static inline bool init_hook(HANDLE thread_handle)
240 {
241 	wait_for_dll_main_finish(thread_handle);
242 
243 	_snwprintf(keepalive_name, sizeof(keepalive_name) / sizeof(wchar_t),
244 		   L"%s%lu", WINDOW_HOOK_KEEPALIVE, GetCurrentProcessId());
245 
246 	init_pipe();
247 
248 	init_dummy_window_thread();
249 	log_current_process();
250 
251 	SetEvent(signal_restart);
252 	return true;
253 }
254 
close_handle(HANDLE * handle)255 static inline void close_handle(HANDLE *handle)
256 {
257 	if (*handle) {
258 		CloseHandle(*handle);
259 		*handle = NULL;
260 	}
261 }
262 
free_hook(void)263 static void free_hook(void)
264 {
265 	if (filemap_hook_info) {
266 		CloseHandle(filemap_hook_info);
267 		filemap_hook_info = NULL;
268 	}
269 	if (global_hook_info) {
270 		UnmapViewOfFile(global_hook_info);
271 		global_hook_info = NULL;
272 	}
273 
274 	close_handle(&tex_mutexes[1]);
275 	close_handle(&tex_mutexes[0]);
276 	close_handle(&signal_exit);
277 	close_handle(&signal_ready);
278 	close_handle(&signal_stop);
279 	close_handle(&signal_restart);
280 	close_handle(&dup_hook_mutex);
281 	ipc_pipe_client_free(&pipe);
282 }
283 
d3d8_hookable(void)284 static inline bool d3d8_hookable(void)
285 {
286 	return !!global_hook_info->offsets.d3d8.present;
287 }
288 
ddraw_hookable(void)289 static inline bool ddraw_hookable(void)
290 {
291 	return !!global_hook_info->offsets.ddraw.surface_create &&
292 	       !!global_hook_info->offsets.ddraw.surface_restore &&
293 	       !!global_hook_info->offsets.ddraw.surface_release &&
294 	       !!global_hook_info->offsets.ddraw.surface_unlock &&
295 	       !!global_hook_info->offsets.ddraw.surface_blt &&
296 	       !!global_hook_info->offsets.ddraw.surface_flip &&
297 	       !!global_hook_info->offsets.ddraw.surface_set_palette &&
298 	       !!global_hook_info->offsets.ddraw.palette_set_entries;
299 }
300 
d3d9_hookable(void)301 static inline bool d3d9_hookable(void)
302 {
303 	return !!global_hook_info->offsets.d3d9.present &&
304 	       !!global_hook_info->offsets.d3d9.present_ex &&
305 	       !!global_hook_info->offsets.d3d9.present_swap;
306 }
307 
dxgi_hookable(void)308 static inline bool dxgi_hookable(void)
309 {
310 	return !!global_hook_info->offsets.dxgi.present &&
311 	       !!global_hook_info->offsets.dxgi.resize;
312 }
313 
attempt_hook(void)314 static inline bool attempt_hook(void)
315 {
316 	//static bool ddraw_hooked = false;
317 	static bool d3d8_hooked = false;
318 	static bool d3d9_hooked = false;
319 	static bool d3d12_hooked = false;
320 	static bool dxgi_hooked = false;
321 	static bool gl_hooked = false;
322 #if COMPILE_VULKAN_HOOK
323 	static bool vulkan_hooked = false;
324 	if (!vulkan_hooked) {
325 		vulkan_hooked = hook_vulkan();
326 		if (vulkan_hooked) {
327 			return true;
328 		}
329 	}
330 #endif //COMPILE_VULKAN_HOOK
331 
332 #if COMPILE_D3D12_HOOK
333 	if (!d3d12_hooked) {
334 		d3d12_hooked = hook_d3d12();
335 	}
336 #endif
337 
338 	if (!d3d9_hooked) {
339 		if (!d3d9_hookable()) {
340 			DbgOut("[OBS] no D3D9 hook address found!\n");
341 			d3d9_hooked = true;
342 		} else {
343 			d3d9_hooked = hook_d3d9();
344 			if (d3d9_hooked) {
345 				return true;
346 			}
347 		}
348 	}
349 
350 	if (!dxgi_hooked) {
351 		if (!dxgi_hookable()) {
352 			DbgOut("[OBS] no DXGI hook address found!\n");
353 			dxgi_hooked = true;
354 		} else {
355 			dxgi_hooked = hook_dxgi();
356 			if (dxgi_hooked) {
357 				return true;
358 			}
359 		}
360 	}
361 
362 	if (!gl_hooked) {
363 		gl_hooked = hook_gl();
364 		if (gl_hooked) {
365 			return true;
366 		}
367 		/*} else {
368 		rehook_gl();*/
369 	}
370 
371 	if (!d3d8_hooked) {
372 		if (!d3d8_hookable()) {
373 			d3d8_hooked = true;
374 		} else {
375 			d3d8_hooked = hook_d3d8();
376 			if (d3d8_hooked) {
377 				return true;
378 			}
379 		}
380 	}
381 
382 	/*if (!ddraw_hooked) {
383 		if (!ddraw_hookable()) {
384 			ddraw_hooked = true;
385 		} else {
386 			ddraw_hooked = hook_ddraw();
387 			if (ddraw_hooked) {
388 				return true;
389 			}
390 		}
391 	}*/
392 
393 #if HOOK_VERBOSE_LOGGING
394 	DbgOut("[OBS] Attempt hook: D3D8=");
395 	DbgOut(d3d8_hooked ? "1" : "0");
396 	DbgOut(", D3D9=");
397 	DbgOut(d3d9_hooked ? "1" : "0");
398 	DbgOut(", D3D12=");
399 	DbgOut(d3d12_hooked ? "1" : "0");
400 	DbgOut(", DXGI=");
401 	DbgOut(dxgi_hooked ? "1" : "0");
402 	DbgOut(", GL=");
403 	DbgOut(gl_hooked ? "1" : "0");
404 #if COMPILE_VULKAN_HOOK
405 	DbgOut(", VK=");
406 	DbgOut(vulkan_hooked ? "1" : "0");
407 #endif
408 	DbgOut("\n");
409 #endif
410 
411 	return false;
412 }
413 
capture_loop(void)414 static inline void capture_loop(void)
415 {
416 	WaitForSingleObject(signal_init, INFINITE);
417 
418 	while (!attempt_hook())
419 		Sleep(40);
420 
421 	for (size_t n = 0; !stop_loop; n++) {
422 		/* this causes it to check every 4 seconds, but still with
423 		 * a small sleep interval in case the thread needs to stop */
424 		if (n % 100 == 0)
425 			attempt_hook();
426 		Sleep(40);
427 	}
428 }
429 
main_capture_thread(HANDLE thread_handle)430 static DWORD WINAPI main_capture_thread(HANDLE thread_handle)
431 {
432 	if (!init_hook(thread_handle)) {
433 		DbgOut("[OBS] Failed to init hook\n");
434 		free_hook();
435 		return 0;
436 	}
437 
438 	capture_loop();
439 	return 0;
440 }
441 
hlogv(const char * format,va_list args)442 static inline void hlogv(const char *format, va_list args)
443 {
444 	char message[1024] = "";
445 	int num = _vsprintf_p(message, 1024, format, args);
446 	if (num > 0) {
447 		if (!ipc_pipe_client_write(&pipe, message, (size_t)num + 1)) {
448 			ipc_pipe_client_free(&pipe);
449 		}
450 		DbgOut("[OBS] ");
451 		DbgOut(message);
452 		DbgOut("\n");
453 	}
454 }
455 
hlog(const char * format,...)456 void hlog(const char *format, ...)
457 {
458 	va_list args;
459 
460 	va_start(args, format);
461 	hlogv(format, args);
462 	va_end(args);
463 }
464 
hlog_hr(const char * text,HRESULT hr)465 void hlog_hr(const char *text, HRESULT hr)
466 {
467 	LPSTR buffer = NULL;
468 
469 	FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
470 			       FORMAT_MESSAGE_ALLOCATE_BUFFER |
471 			       FORMAT_MESSAGE_IGNORE_INSERTS,
472 		       NULL, hr, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
473 		       (LPSTR)&buffer, 0, NULL);
474 
475 	if (buffer) {
476 		hlog("%s (0x%08lX): %s", text, hr, buffer);
477 		LocalFree(buffer);
478 	} else {
479 		hlog("%s (0x%08lX)", text, hr);
480 	}
481 }
482 
get_clockfreq(void)483 static inline uint64_t get_clockfreq(void)
484 {
485 	static bool have_clockfreq = false;
486 	static LARGE_INTEGER clock_freq;
487 
488 	if (!have_clockfreq) {
489 		QueryPerformanceFrequency(&clock_freq);
490 		have_clockfreq = true;
491 	}
492 
493 	return clock_freq.QuadPart;
494 }
495 
os_gettime_ns(void)496 uint64_t os_gettime_ns(void)
497 {
498 	LARGE_INTEGER current_time;
499 	double time_val;
500 
501 	QueryPerformanceCounter(&current_time);
502 	time_val = (double)current_time.QuadPart;
503 	time_val *= 1000000000.0;
504 	time_val /= (double)get_clockfreq();
505 
506 	return (uint64_t)time_val;
507 }
508 
try_lock_shmem_tex(int id)509 static inline int try_lock_shmem_tex(int id)
510 {
511 	int next = id == 0 ? 1 : 0;
512 	DWORD wait_result = WAIT_FAILED;
513 
514 	wait_result = WaitForSingleObject(tex_mutexes[id], 0);
515 	if (wait_result == WAIT_OBJECT_0 || wait_result == WAIT_ABANDONED) {
516 		return id;
517 	}
518 
519 	wait_result = WaitForSingleObject(tex_mutexes[next], 0);
520 	if (wait_result == WAIT_OBJECT_0 || wait_result == WAIT_ABANDONED) {
521 		return next;
522 	}
523 
524 	return -1;
525 }
526 
unlock_shmem_tex(int id)527 static inline void unlock_shmem_tex(int id)
528 {
529 	if (id != -1) {
530 		ReleaseMutex(tex_mutexes[id]);
531 	}
532 }
533 
init_shared_info(size_t size,HWND window)534 static inline bool init_shared_info(size_t size, HWND window)
535 {
536 	wchar_t name[64];
537 	HWND top = GetAncestor(window, GA_ROOT);
538 
539 	swprintf(name, 64, SHMEM_TEXTURE "_%" PRIu64 "_%u",
540 		 (uint64_t)(uintptr_t)top, ++shmem_id_counter);
541 
542 	shmem_file_handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
543 					       PAGE_READWRITE, 0, (DWORD)size,
544 					       name);
545 	if (!shmem_file_handle) {
546 		hlog("init_shared_info: Failed to create shared memory: %d",
547 		     GetLastError());
548 		return false;
549 	}
550 
551 	shmem_info = MapViewOfFile(shmem_file_handle, FILE_MAP_ALL_ACCESS, 0, 0,
552 				   size);
553 	if (!shmem_info) {
554 		hlog("init_shared_info: Failed to map shared memory: %d",
555 		     GetLastError());
556 		return false;
557 	}
558 
559 	return true;
560 }
561 
capture_init_shtex(struct shtex_data ** data,HWND window,uint32_t cx,uint32_t cy,uint32_t format,bool flip,uintptr_t handle)562 bool capture_init_shtex(struct shtex_data **data, HWND window, uint32_t cx,
563 			uint32_t cy, uint32_t format, bool flip,
564 			uintptr_t handle)
565 {
566 	if (!init_shared_info(sizeof(struct shtex_data), window)) {
567 		hlog("capture_init_shtex: Failed to initialize memory");
568 		return false;
569 	}
570 
571 	*data = shmem_info;
572 	(*data)->tex_handle = (uint32_t)handle;
573 
574 	global_hook_info->hook_ver_major = HOOK_VER_MAJOR;
575 	global_hook_info->hook_ver_minor = HOOK_VER_MINOR;
576 	global_hook_info->window = (uint32_t)(uintptr_t)window;
577 	global_hook_info->type = CAPTURE_TYPE_TEXTURE;
578 	global_hook_info->format = format;
579 	global_hook_info->flip = flip;
580 	global_hook_info->map_id = shmem_id_counter;
581 	global_hook_info->map_size = sizeof(struct shtex_data);
582 	global_hook_info->cx = cx;
583 	global_hook_info->cy = cy;
584 	global_hook_info->UNUSED_base_cx = cx;
585 	global_hook_info->UNUSED_base_cy = cy;
586 
587 	if (!SetEvent(signal_ready)) {
588 		hlog("capture_init_shtex: Failed to signal ready: %d",
589 		     GetLastError());
590 		return false;
591 	}
592 
593 	active = true;
594 	return true;
595 }
596 
copy_thread(LPVOID unused)597 static DWORD CALLBACK copy_thread(LPVOID unused)
598 {
599 	uint32_t pitch = thread_data.pitch;
600 	uint32_t cy = thread_data.cy;
601 	HANDLE events[2] = {NULL, NULL};
602 	int shmem_id = 0;
603 
604 	if (!duplicate_handle(&events[0], thread_data.copy_event)) {
605 		hlog_hr("copy_thread: Failed to duplicate copy event: %d",
606 			GetLastError());
607 		return 0;
608 	}
609 
610 	if (!duplicate_handle(&events[1], thread_data.stop_event)) {
611 		hlog_hr("copy_thread: Failed to duplicate stop event: %d",
612 			GetLastError());
613 		goto finish;
614 	}
615 
616 	for (;;) {
617 		int copy_tex;
618 		void *cur_data;
619 
620 		DWORD ret = WaitForMultipleObjects(2, events, false, INFINITE);
621 		if (ret != WAIT_OBJECT_0) {
622 			break;
623 		}
624 
625 		EnterCriticalSection(&thread_data.data_mutex);
626 		copy_tex = thread_data.cur_tex;
627 		cur_data = thread_data.cur_data;
628 		LeaveCriticalSection(&thread_data.data_mutex);
629 
630 		if (copy_tex < NUM_BUFFERS && !!cur_data) {
631 			EnterCriticalSection(&thread_data.mutexes[copy_tex]);
632 
633 			int lock_id = try_lock_shmem_tex(shmem_id);
634 			if (lock_id != -1) {
635 				memcpy(thread_data.shmem_textures[lock_id],
636 				       cur_data, (size_t)pitch * (size_t)cy);
637 
638 				unlock_shmem_tex(lock_id);
639 				((struct shmem_data *)shmem_info)->last_tex =
640 					lock_id;
641 
642 				shmem_id = lock_id == 0 ? 1 : 0;
643 			}
644 
645 			LeaveCriticalSection(&thread_data.mutexes[copy_tex]);
646 		}
647 	}
648 
649 finish:
650 	for (size_t i = 0; i < 2; i++) {
651 		if (events[i]) {
652 			CloseHandle(events[i]);
653 		}
654 	}
655 
656 	(void)unused;
657 	return 0;
658 }
659 
shmem_copy_data(size_t idx,void * volatile data)660 void shmem_copy_data(size_t idx, void *volatile data)
661 {
662 	EnterCriticalSection(&thread_data.data_mutex);
663 	thread_data.cur_tex = (int)idx;
664 	thread_data.cur_data = data;
665 	thread_data.locked_textures[idx] = true;
666 	LeaveCriticalSection(&thread_data.data_mutex);
667 
668 	SetEvent(thread_data.copy_event);
669 }
670 
shmem_texture_data_lock(int idx)671 bool shmem_texture_data_lock(int idx)
672 {
673 	bool locked;
674 
675 	EnterCriticalSection(&thread_data.data_mutex);
676 	locked = thread_data.locked_textures[idx];
677 	LeaveCriticalSection(&thread_data.data_mutex);
678 
679 	if (locked) {
680 		EnterCriticalSection(&thread_data.mutexes[idx]);
681 		return true;
682 	}
683 
684 	return false;
685 }
686 
shmem_texture_data_unlock(int idx)687 void shmem_texture_data_unlock(int idx)
688 {
689 	EnterCriticalSection(&thread_data.data_mutex);
690 	thread_data.locked_textures[idx] = false;
691 	LeaveCriticalSection(&thread_data.data_mutex);
692 
693 	LeaveCriticalSection(&thread_data.mutexes[idx]);
694 }
695 
init_shmem_thread(uint32_t pitch,uint32_t cy)696 static inline bool init_shmem_thread(uint32_t pitch, uint32_t cy)
697 {
698 	struct shmem_data *data = shmem_info;
699 
700 	thread_data.pitch = pitch;
701 	thread_data.cy = cy;
702 	thread_data.shmem_textures[0] = (uint8_t *)data + data->tex1_offset;
703 	thread_data.shmem_textures[1] = (uint8_t *)data + data->tex2_offset;
704 
705 	thread_data.copy_event = CreateEvent(NULL, false, false, NULL);
706 	if (!thread_data.copy_event) {
707 		hlog("init_shmem_thread: Failed to create copy event: %d",
708 		     GetLastError());
709 		return false;
710 	}
711 
712 	thread_data.stop_event = CreateEvent(NULL, true, false, NULL);
713 	if (!thread_data.stop_event) {
714 		hlog("init_shmem_thread: Failed to create stop event: %d",
715 		     GetLastError());
716 		return false;
717 	}
718 
719 	for (size_t i = 0; i < NUM_BUFFERS; i++) {
720 		InitializeCriticalSection(&thread_data.mutexes[i]);
721 	}
722 
723 	InitializeCriticalSection(&thread_data.data_mutex);
724 
725 	thread_data.copy_thread =
726 		CreateThread(NULL, 0, copy_thread, NULL, 0, NULL);
727 	if (!thread_data.copy_thread) {
728 		hlog("init_shmem_thread: Failed to create thread: %d",
729 		     GetLastError());
730 		return false;
731 	}
732 	return true;
733 }
734 
735 #ifndef ALIGN
736 #define ALIGN(bytes, align) (((bytes) + ((align)-1)) & ~((align)-1))
737 #endif
738 
capture_init_shmem(struct shmem_data ** data,HWND window,uint32_t cx,uint32_t cy,uint32_t pitch,uint32_t format,bool flip)739 bool capture_init_shmem(struct shmem_data **data, HWND window, uint32_t cx,
740 			uint32_t cy, uint32_t pitch, uint32_t format, bool flip)
741 {
742 	uint32_t tex_size = cy * pitch;
743 	uint32_t aligned_header = ALIGN(sizeof(struct shmem_data), 32);
744 	uint32_t aligned_tex = ALIGN(tex_size, 32);
745 	uint32_t total_size = aligned_header + aligned_tex * 2 + 32;
746 	uintptr_t align_pos;
747 
748 	if (!init_shared_info(total_size, window)) {
749 		hlog("capture_init_shmem: Failed to initialize memory");
750 		return false;
751 	}
752 
753 	*data = shmem_info;
754 
755 	/* to ensure fast copy rate, align texture data to 256bit addresses */
756 	align_pos = (uintptr_t)shmem_info;
757 	align_pos += aligned_header;
758 	align_pos &= ~(32 - 1);
759 	align_pos -= (uintptr_t)shmem_info;
760 
761 	if (align_pos < sizeof(struct shmem_data))
762 		align_pos += 32;
763 
764 	(*data)->last_tex = -1;
765 	(*data)->tex1_offset = (uint32_t)align_pos;
766 	(*data)->tex2_offset = (*data)->tex1_offset + aligned_tex;
767 
768 	global_hook_info->hook_ver_major = HOOK_VER_MAJOR;
769 	global_hook_info->hook_ver_minor = HOOK_VER_MINOR;
770 	global_hook_info->window = (uint32_t)(uintptr_t)window;
771 	global_hook_info->type = CAPTURE_TYPE_MEMORY;
772 	global_hook_info->format = format;
773 	global_hook_info->flip = flip;
774 	global_hook_info->map_id = shmem_id_counter;
775 	global_hook_info->map_size = total_size;
776 	global_hook_info->pitch = pitch;
777 	global_hook_info->cx = cx;
778 	global_hook_info->cy = cy;
779 	global_hook_info->UNUSED_base_cx = cx;
780 	global_hook_info->UNUSED_base_cy = cy;
781 
782 	if (!init_shmem_thread(pitch, cy)) {
783 		return false;
784 	}
785 
786 	if (!SetEvent(signal_ready)) {
787 		hlog("capture_init_shmem: Failed to signal ready: %d",
788 		     GetLastError());
789 		return false;
790 	}
791 
792 	active = true;
793 	return true;
794 }
795 
thread_data_free(void)796 static inline void thread_data_free(void)
797 {
798 	if (thread_data.copy_thread) {
799 		DWORD ret;
800 
801 		SetEvent(thread_data.stop_event);
802 		ret = WaitForSingleObject(thread_data.copy_thread, 500);
803 		if (ret != WAIT_OBJECT_0)
804 			TerminateThread(thread_data.copy_thread, (DWORD)-1);
805 
806 		CloseHandle(thread_data.copy_thread);
807 	}
808 	if (thread_data.stop_event)
809 		CloseHandle(thread_data.stop_event);
810 	if (thread_data.copy_event)
811 		CloseHandle(thread_data.copy_event);
812 	for (size_t i = 0; i < NUM_BUFFERS; i++)
813 		DeleteCriticalSection(&thread_data.mutexes[i]);
814 
815 	DeleteCriticalSection(&thread_data.data_mutex);
816 
817 	memset(&thread_data, 0, sizeof(thread_data));
818 }
819 
capture_free(void)820 void capture_free(void)
821 {
822 	thread_data_free();
823 
824 	if (shmem_info) {
825 		UnmapViewOfFile(shmem_info);
826 		shmem_info = NULL;
827 	}
828 
829 	close_handle(&shmem_file_handle);
830 
831 	SetEvent(signal_restart);
832 	active = false;
833 }
834 
835 #define HOOK_NAME L"graphics_hook_dup_mutex"
836 
open_mutex_plus_id(const wchar_t * name,DWORD id)837 static inline HANDLE open_mutex_plus_id(const wchar_t *name, DWORD id)
838 {
839 	wchar_t new_name[64];
840 	_snwprintf(new_name, 64, L"%s%lu", name, id);
841 	return open_mutex(new_name);
842 }
843 
init_dll(void)844 static bool init_dll(void)
845 {
846 	DWORD pid = GetCurrentProcessId();
847 	HANDLE h;
848 
849 	h = open_mutex_plus_id(HOOK_NAME, pid);
850 	if (h) {
851 		CloseHandle(h);
852 		return false;
853 	}
854 
855 	dup_hook_mutex = create_mutex_plus_id(HOOK_NAME, pid);
856 	return !!dup_hook_mutex;
857 }
858 
DllMain(HINSTANCE hinst,DWORD reason,LPVOID unused1)859 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID unused1)
860 {
861 	if (reason == DLL_PROCESS_ATTACH) {
862 		wchar_t name[MAX_PATH];
863 
864 		dll_inst = hinst;
865 
866 		if (!init_dll()) {
867 			DbgOut("[OBS] Duplicate hook library");
868 			return false;
869 		}
870 
871 		HANDLE cur_thread;
872 		bool success = DuplicateHandle(GetCurrentProcess(),
873 					       GetCurrentThread(),
874 					       GetCurrentProcess(), &cur_thread,
875 					       SYNCHRONIZE, false, 0);
876 
877 		if (!success)
878 			DbgOut("[OBS] Failed to get current thread handle");
879 
880 		if (!init_signals()) {
881 			return false;
882 		}
883 		if (!init_system_path()) {
884 			return false;
885 		}
886 		if (!init_hook_info()) {
887 			return false;
888 		}
889 		if (!init_mutexes()) {
890 			return false;
891 		}
892 
893 		/* this prevents the library from being automatically unloaded
894 		 * by the next FreeLibrary call */
895 		GetModuleFileNameW(hinst, name, MAX_PATH);
896 		LoadLibraryW(name);
897 
898 		capture_thread = CreateThread(
899 			NULL, 0, (LPTHREAD_START_ROUTINE)main_capture_thread,
900 			(LPVOID)cur_thread, 0, 0);
901 		if (!capture_thread) {
902 			CloseHandle(cur_thread);
903 			return false;
904 		}
905 
906 	} else if (reason == DLL_PROCESS_DETACH) {
907 		if (!dup_hook_mutex) {
908 			return true;
909 		}
910 
911 		if (capture_thread) {
912 			stop_loop = true;
913 			WaitForSingleObject(capture_thread, 300);
914 			CloseHandle(capture_thread);
915 		}
916 
917 		free_hook();
918 	}
919 
920 	(void)unused1;
921 	return true;
922 }
923 
924 __declspec(dllexport) LRESULT CALLBACK
dummy_debug_proc(int code,WPARAM wparam,LPARAM lparam)925 	dummy_debug_proc(int code, WPARAM wparam, LPARAM lparam)
926 {
927 	static bool hooking = true;
928 	MSG *msg = (MSG *)lparam;
929 
930 	if (hooking && msg->message == (WM_USER + 432)) {
931 		HMODULE user32 = GetModuleHandleW(L"USER32");
932 		BOOL(WINAPI * unhook_windows_hook_ex)(HHOOK) = NULL;
933 
934 		unhook_windows_hook_ex = get_obfuscated_func(
935 			user32, "VojeleY`bdgxvM`hhDz", 0x7F55F80C9EE3A213ULL);
936 
937 		if (unhook_windows_hook_ex)
938 			unhook_windows_hook_ex((HHOOK)msg->lParam);
939 		hooking = false;
940 	}
941 
942 	return CallNextHookEx(0, code, wparam, lparam);
943 }
944