diff --git a/plugins/win-capture/game-capture.c b/plugins/win-capture/game-capture.c index 2e1a8020a..7fed5fe39 100644 --- a/plugins/win-capture/game-capture.c +++ b/plugins/win-capture/game-capture.c @@ -13,6 +13,7 @@ #include "window-helpers.h" #include "cursor-capture.h" #include "app-helpers.h" +#include "nt-stuff.h" #define do_log(level, format, ...) \ blog(level, "[game-capture: '%s'] " format, \ @@ -984,6 +985,11 @@ static bool is_blacklisted_exe(const char *exe) return false; } +static bool target_suspended(struct game_capture *gc) +{ + return thread_is_suspended(gc->process_id, gc->thread_id); +} + static bool init_events(struct game_capture *gc); static bool init_hook(struct game_capture *gc) @@ -1009,6 +1015,9 @@ static bool init_hook(struct game_capture *gc) if (blacklisted_process) { return false; } + if (target_suspended(gc)) { + return false; + } if (!open_target_process(gc)) { return false; } diff --git a/plugins/win-capture/nt-stuff.h b/plugins/win-capture/nt-stuff.h index 03171bc3d..15f0823d9 100644 --- a/plugins/win-capture/nt-stuff.h +++ b/plugins/win-capture/nt-stuff.h @@ -2,10 +2,46 @@ #include +#define THREAD_STATE_WAITING 5 +#define THREAD_WAIT_REASON_SUSPENDED 5 + +typedef struct _SYSTEM_PROCESS_INFORMATION2 { + ULONG NextEntryOffset; + ULONG ThreadCount; + BYTE Reserved1[48]; + PVOID Reserved2[3]; + HANDLE UniqueProcessId; + PVOID Reserved3; + ULONG HandleCount; + BYTE Reserved4[4]; + PVOID Reserved5[11]; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; + LARGE_INTEGER Reserved6[6]; +} SYSTEM_PROCESS_INFORMATION2; + +typedef struct _SYSTEM_THREAD_INFORMATION { + FILETIME KernelTime; + FILETIME UserTime; + FILETIME CreateTime; + DWORD WaitTime; + PVOID Address; + HANDLE UniqueProcessId; + HANDLE UniqueThreadId; + DWORD Priority; + DWORD BasePriority; + DWORD ContextSwitches; + DWORD ThreadState; + DWORD WaitReason; + DWORD Reserved1; +} SYSTEM_THREAD_INFORMATION; + #ifndef NT_SUCCESS #define NT_SUCCESS(status) ((NTSTATUS)(status) >= 0) #endif +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) + #define init_named_attribs(o, name) \ do { \ (o)->Length = sizeof(*(o)); \ @@ -18,6 +54,8 @@ typedef void (WINAPI *RTLINITUNICODESTRINGFUNC)(PCUNICODE_STRING pstr, const wchar_t *lpstrName); typedef NTSTATUS (WINAPI *NTOPENFUNC)(PHANDLE phandle, ACCESS_MASK access, POBJECT_ATTRIBUTES objattr); +typedef ULONG (WINAPI *RTLNTSTATUSTODOSERRORFUNC)(NTSTATUS status); +typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATIONFUNC)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); static FARPROC get_nt_func(const char *name) { @@ -61,6 +99,79 @@ static void rtl_init_str(UNICODE_STRING *unistr, const wchar_t *str) func(unistr, str); } +static NTSTATUS nt_query_information(SYSTEM_INFORMATION_CLASS info_class, + PVOID info, ULONG info_len, PULONG ret_len) +{ + static bool initialized = false; + static NTQUERYSYSTEMINFORMATIONFUNC func = NULL; + + if (!initialized) { + func = (NTQUERYSYSTEMINFORMATIONFUNC)get_nt_func( + "NtQuerySystemInformation"); + initialized = true; + } + + if (func) + return func(info_class, info, info_len, ret_len); + return (NTSTATUS)-1; +} + +static bool thread_is_suspended(DWORD process_id, DWORD thread_id) +{ + ULONG size = 4096; + bool suspended = false; + void *data = malloc(size); + + for (;;) { + NTSTATUS stat = nt_query_information(SystemProcessInformation, + data, size, &size); + if (NT_SUCCESS(stat)) + break; + + if (stat != STATUS_INFO_LENGTH_MISMATCH) { + goto fail; + } + + free(data); + size += 1024; + data = malloc(size); + } + + SYSTEM_PROCESS_INFORMATION2 *spi = data; + + for (;;) { + if (spi->UniqueProcessId == (HANDLE)process_id) { + break; + } + + ULONG offset = spi->NextEntryOffset; + if (!offset) + goto fail; + + spi = (SYSTEM_PROCESS_INFORMATION2*)((BYTE*)spi + offset); + } + + SYSTEM_THREAD_INFORMATION *sti; + SYSTEM_THREAD_INFORMATION *info = NULL; + sti = (SYSTEM_THREAD_INFORMATION*)((BYTE*)spi + sizeof(*spi)); + + for (ULONG i = 0; i < spi->ThreadCount; i++) { + if (sti[i].UniqueThreadId == (HANDLE)thread_id) { + info = &sti[i]; + break; + } + } + + if (info) { + suspended = info->ThreadState == THREAD_STATE_WAITING && + info->WaitReason == THREAD_WAIT_REASON_SUSPENDED; + } + +fail: + free(data); + return suspended; +} + #define MAKE_NT_OPEN_FUNC(func_name, nt_name, access) \ static HANDLE func_name(const wchar_t *name) \ { \