diff --git a/plugins/win-capture/CMakeLists.txt b/plugins/win-capture/CMakeLists.txt index e6f110518..335dd74f4 100644 --- a/plugins/win-capture/CMakeLists.txt +++ b/plugins/win-capture/CMakeLists.txt @@ -30,7 +30,8 @@ set(win-capture_SOURCES load-graphics-offsets.c game-capture-file-init.c duplicator-monitor-capture.c - plugin-main.c) + plugin-main.c + nt-stuff.c) add_library(win-capture MODULE ${win-capture_SOURCES} diff --git a/plugins/win-capture/app-helpers.h b/plugins/win-capture/app-helpers.h index 4824fde44..6906e2d94 100644 --- a/plugins/win-capture/app-helpers.h +++ b/plugins/win-capture/app-helpers.h @@ -4,7 +4,7 @@ extern bool is_app(HANDLE process); extern wchar_t *get_app_sid(HANDLE process); -extern HANDLE create_app_mutex(const wchar_t *sid, const wchar_t *name); -extern HANDLE open_app_mutex(const wchar_t *sid, const wchar_t *name); -extern HANDLE open_app_event(const wchar_t *sid, const wchar_t *name); -extern HANDLE open_app_map(const wchar_t *sid, const wchar_t *name); +HANDLE create_app_mutex(const wchar_t *sid, const wchar_t *name); +HANDLE open_app_mutex(const wchar_t *sid, const wchar_t *name); +HANDLE open_app_event(const wchar_t *sid, const wchar_t *name); +HANDLE open_app_map(const wchar_t *sid, const wchar_t *name); diff --git a/plugins/win-capture/nt-stuff.c b/plugins/win-capture/nt-stuff.c new file mode 100644 index 000000000..d8f81299b --- /dev/null +++ b/plugins/win-capture/nt-stuff.c @@ -0,0 +1,242 @@ +#include +#include +#include + +#define THREAD_STATE_WAITING 5 +#define THREAD_WAIT_REASON_SUSPENDED 5 + +typedef struct _OBS_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]; +} OBS_SYSTEM_PROCESS_INFORMATION2; + +typedef struct _OBS_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; +} OBS_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)); \ + (o)->ObjectName = name; \ + (o)->RootDirectory = NULL; \ + (o)->Attributes = 0; \ + (o)->SecurityDescriptor = NULL; \ + (o)->SecurityQualityOfService = NULL; \ + } while (false) + +typedef void(WINAPI *RTLINITUNICODESTRINGFUNC)(PCUNICODE_STRING pstr, + const wchar_t *lpstrName); +typedef NTSTATUS(WINAPI *NTOPENFUNC)(PHANDLE phandle, ACCESS_MASK access, + POBJECT_ATTRIBUTES objattr); +typedef NTSTATUS(WINAPI *NTCREATEMUTANT)(PHANDLE phandle, ACCESS_MASK access, + POBJECT_ATTRIBUTES objattr, + BOOLEAN isowner); +typedef ULONG(WINAPI *RTLNTSTATUSTODOSERRORFUNC)(NTSTATUS status); +typedef NTSTATUS(WINAPI *NTQUERYSYSTEMINFORMATIONFUNC)(SYSTEM_INFORMATION_CLASS, + PVOID, ULONG, PULONG); + +FARPROC get_nt_func(const char *name) +{ + static bool initialized = false; + static HANDLE ntdll = NULL; + if (!initialized) { + ntdll = GetModuleHandleW(L"ntdll"); + initialized = true; + } + + return GetProcAddress(ntdll, name); +} + +void nt_set_last_error(NTSTATUS status) +{ + static bool initialized = false; + static RTLNTSTATUSTODOSERRORFUNC func = NULL; + + if (!initialized) { + func = (RTLNTSTATUSTODOSERRORFUNC)get_nt_func( + "RtlNtStatusToDosError"); + initialized = true; + } + + if (func) + SetLastError(func(status)); +} + +void rtl_init_str(UNICODE_STRING *unistr, const wchar_t *str) +{ + static bool initialized = false; + static RTLINITUNICODESTRINGFUNC func = NULL; + + if (!initialized) { + func = (RTLINITUNICODESTRINGFUNC)get_nt_func( + "RtlInitUnicodeString"); + initialized = true; + } + + if (func) + func(unistr, str); +} + +#define MAKE_NT_OPEN_FUNC(func_name, nt_name, access) \ + HANDLE func_name(const wchar_t *name) \ + { \ + static bool initialized = false; \ + static NTOPENFUNC open = NULL; \ + HANDLE handle; \ + NTSTATUS status; \ + UNICODE_STRING unistr; \ + OBJECT_ATTRIBUTES attr; \ + \ + if (!initialized) { \ + open = (NTOPENFUNC)get_nt_func(#nt_name); \ + initialized = true; \ + } \ + \ + if (!open) \ + return NULL; \ + \ + rtl_init_str(&unistr, name); \ + init_named_attribs(&attr, &unistr); \ + \ + status = open(&handle, access, &attr); \ + if (NT_SUCCESS(status)) \ + return handle; \ + nt_set_last_error(status); \ + return NULL; \ + } + +MAKE_NT_OPEN_FUNC(nt_open_mutex, NtOpenMutant, SYNCHRONIZE) +MAKE_NT_OPEN_FUNC(nt_open_event, NtOpenEvent, EVENT_MODIFY_STATE | SYNCHRONIZE) +MAKE_NT_OPEN_FUNC(nt_open_map, NtOpenSection, FILE_MAP_READ | FILE_MAP_WRITE) + +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; +} + +bool thread_is_suspended(DWORD process_id, DWORD thread_id) +{ + ULONG size = 4096; + bool suspended = false; + void *data = malloc(size); + if (!data) + return false; + + 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); + if (!data) + return false; + } + + OBS_SYSTEM_PROCESS_INFORMATION2 *spi = data; + + for (;;) { + if (spi->UniqueProcessId == (HANDLE)(DWORD_PTR)process_id) { + break; + } + + ULONG offset = spi->NextEntryOffset; + if (!offset) + goto fail; + + spi = (OBS_SYSTEM_PROCESS_INFORMATION2 *)((BYTE *)spi + offset); + } + + OBS_SYSTEM_THREAD_INFORMATION *sti; + OBS_SYSTEM_THREAD_INFORMATION *info = NULL; + sti = (OBS_SYSTEM_THREAD_INFORMATION *)((BYTE *)spi + sizeof(*spi)); + + for (ULONG i = 0; i < spi->ThreadCount; i++) { + if (sti[i].UniqueThreadId == (HANDLE)(DWORD_PTR)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; +} + +HANDLE nt_create_mutex(const wchar_t *name) +{ + static bool initialized = false; + static NTCREATEMUTANT create = NULL; + HANDLE handle; + NTSTATUS status; + UNICODE_STRING unistr; + OBJECT_ATTRIBUTES attr; + + if (!initialized) { + create = (NTCREATEMUTANT)get_nt_func("NtCreateMutant"); + initialized = true; + } + + if (!create) + return NULL; + + rtl_init_str(&unistr, name); + init_named_attribs(&attr, &unistr); + + status = create(&handle, SYNCHRONIZE, &attr, FALSE); + if (NT_SUCCESS(status)) + return handle; + nt_set_last_error(status); + return NULL; +} diff --git a/plugins/win-capture/nt-stuff.h b/plugins/win-capture/nt-stuff.h index 6d5882072..ce1580a9a 100644 --- a/plugins/win-capture/nt-stuff.h +++ b/plugins/win-capture/nt-stuff.h @@ -1,238 +1,7 @@ #pragma once -#include - -#define THREAD_STATE_WAITING 5 -#define THREAD_WAIT_REASON_SUSPENDED 5 - -typedef struct _OBS_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]; -} OBS_SYSTEM_PROCESS_INFORMATION2; - -typedef struct _OBS_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; -} OBS_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)); \ - (o)->ObjectName = name; \ - (o)->RootDirectory = NULL; \ - (o)->Attributes = 0; \ - (o)->SecurityDescriptor = NULL; \ - (o)->SecurityQualityOfService = NULL; \ - } while (false) - -typedef void(WINAPI *RTLINITUNICODESTRINGFUNC)(PCUNICODE_STRING pstr, - const wchar_t *lpstrName); -typedef NTSTATUS(WINAPI *NTOPENFUNC)(PHANDLE phandle, ACCESS_MASK access, - POBJECT_ATTRIBUTES objattr); -typedef NTSTATUS(WINAPI *NTCREATEMUTANT)(PHANDLE phandle, ACCESS_MASK access, - POBJECT_ATTRIBUTES objattr, - BOOLEAN isowner); -typedef ULONG(WINAPI *RTLNTSTATUSTODOSERRORFUNC)(NTSTATUS status); -typedef NTSTATUS(WINAPI *NTQUERYSYSTEMINFORMATIONFUNC)(SYSTEM_INFORMATION_CLASS, - PVOID, ULONG, PULONG); - -static FARPROC get_nt_func(const char *name) -{ - static bool initialized = false; - static HANDLE ntdll = NULL; - if (!initialized) { - ntdll = GetModuleHandleW(L"ntdll"); - initialized = true; - } - - return GetProcAddress(ntdll, name); -} - -static void nt_set_last_error(NTSTATUS status) -{ - static bool initialized = false; - static RTLNTSTATUSTODOSERRORFUNC func = NULL; - - if (!initialized) { - func = (RTLNTSTATUSTODOSERRORFUNC)get_nt_func( - "RtlNtStatusToDosError"); - initialized = true; - } - - if (func) - SetLastError(func(status)); -} - -static void rtl_init_str(UNICODE_STRING *unistr, const wchar_t *str) -{ - static bool initialized = false; - static RTLINITUNICODESTRINGFUNC func = NULL; - - if (!initialized) { - func = (RTLINITUNICODESTRINGFUNC)get_nt_func( - "RtlInitUnicodeString"); - initialized = true; - } - - if (func) - 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); - } - - OBS_SYSTEM_PROCESS_INFORMATION2 *spi = data; - - for (;;) { - if (spi->UniqueProcessId == (HANDLE)(DWORD_PTR)process_id) { - break; - } - - ULONG offset = spi->NextEntryOffset; - if (!offset) - goto fail; - - spi = (OBS_SYSTEM_PROCESS_INFORMATION2 *)((BYTE *)spi + offset); - } - - OBS_SYSTEM_THREAD_INFORMATION *sti; - OBS_SYSTEM_THREAD_INFORMATION *info = NULL; - sti = (OBS_SYSTEM_THREAD_INFORMATION *)((BYTE *)spi + sizeof(*spi)); - - for (ULONG i = 0; i < spi->ThreadCount; i++) { - if (sti[i].UniqueThreadId == (HANDLE)(DWORD_PTR)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) \ - { \ - static bool initialized = false; \ - static NTOPENFUNC open = NULL; \ - HANDLE handle; \ - NTSTATUS status; \ - UNICODE_STRING unistr; \ - OBJECT_ATTRIBUTES attr; \ - \ - if (!initialized) { \ - open = (NTOPENFUNC)get_nt_func(#nt_name); \ - initialized = true; \ - } \ - \ - if (!open) \ - return NULL; \ - \ - rtl_init_str(&unistr, name); \ - init_named_attribs(&attr, &unistr); \ - \ - status = open(&handle, access, &attr); \ - if (NT_SUCCESS(status)) \ - return handle; \ - nt_set_last_error(status); \ - return NULL; \ - } - -MAKE_NT_OPEN_FUNC(nt_open_mutex, NtOpenMutant, SYNCHRONIZE) -MAKE_NT_OPEN_FUNC(nt_open_event, NtOpenEvent, EVENT_MODIFY_STATE | SYNCHRONIZE) -MAKE_NT_OPEN_FUNC(nt_open_map, NtOpenSection, FILE_MAP_READ | FILE_MAP_WRITE) - -static HANDLE nt_create_mutex(const wchar_t *name) -{ - static bool initialized = false; - static NTCREATEMUTANT create = NULL; - HANDLE handle; - NTSTATUS status; - UNICODE_STRING unistr; - OBJECT_ATTRIBUTES attr; - - if (!initialized) { - create = (NTCREATEMUTANT)get_nt_func("NtCreateMutant"); - initialized = true; - } - - if (!create) - return NULL; - - rtl_init_str(&unistr, name); - init_named_attribs(&attr, &unistr); - - status = create(&handle, SYNCHRONIZE, &attr, FALSE); - if (NT_SUCCESS(status)) - return handle; - nt_set_last_error(status); - return NULL; -} +bool thread_is_suspended(DWORD process_id, DWORD thread_id); +HANDLE nt_create_mutex(const wchar_t *name); +HANDLE nt_open_mutex(const wchar_t *name); +HANDLE nt_open_event(const wchar_t *name); +HANDLE nt_open_map(const wchar_t *name);