123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- #include <obs.h>
- #include <util/dstr.h>
- #include <dwmapi.h>
- #include <psapi.h>
- #include <windows.h>
- #include "window-helpers.h"
- #include "obfuscate.h"
- static inline void encode_dstr(struct dstr *str)
- {
- dstr_replace(str, "#", "#22");
- dstr_replace(str, ":", "#3A");
- }
- static inline char *decode_str(const char *src)
- {
- struct dstr str = {0};
- dstr_copy(&str, src);
- dstr_replace(&str, "#3A", ":");
- dstr_replace(&str, "#22", "#");
- return str.array;
- }
- extern void build_window_strings(const char *str, char **class, char **title,
- char **exe)
- {
- char **strlist;
- *class = NULL;
- *title = NULL;
- *exe = NULL;
- if (!str) {
- return;
- }
- strlist = strlist_split(str, ':', true);
- if (strlist && strlist[0] && strlist[1] && strlist[2]) {
- *title = decode_str(strlist[0]);
- *class = decode_str(strlist[1]);
- *exe = decode_str(strlist[2]);
- }
- strlist_free(strlist);
- }
- static HMODULE kernel32(void)
- {
- static HMODULE kernel32_handle = NULL;
- if (!kernel32_handle)
- kernel32_handle = GetModuleHandleA("kernel32");
- return kernel32_handle;
- }
- static inline HANDLE open_process(DWORD desired_access, bool inherit_handle,
- DWORD process_id)
- {
- typedef HANDLE(WINAPI * PFN_OpenProcess)(DWORD, BOOL, DWORD);
- static PFN_OpenProcess open_process_proc = NULL;
- if (!open_process_proc)
- open_process_proc = (PFN_OpenProcess)get_obfuscated_func(
- kernel32(), "B}caZyah`~q", 0x2D5BEBAF6DDULL);
- return open_process_proc(desired_access, inherit_handle, process_id);
- }
- bool get_window_exe(struct dstr *name, HWND window)
- {
- wchar_t wname[MAX_PATH];
- struct dstr temp = {0};
- bool success = false;
- HANDLE process = NULL;
- char *slash;
- DWORD id;
- GetWindowThreadProcessId(window, &id);
- if (id == GetCurrentProcessId())
- return false;
- process = open_process(PROCESS_QUERY_LIMITED_INFORMATION, false, id);
- if (!process)
- goto fail;
- if (!GetProcessImageFileNameW(process, wname, MAX_PATH))
- goto fail;
- dstr_from_wcs(&temp, wname);
- slash = strrchr(temp.array, '\\');
- if (!slash)
- goto fail;
- dstr_copy(name, slash + 1);
- success = true;
- fail:
- if (!success)
- dstr_copy(name, "unknown");
- dstr_free(&temp);
- CloseHandle(process);
- return true;
- }
- void get_window_title(struct dstr *name, HWND hwnd)
- {
- int len;
- len = GetWindowTextLengthW(hwnd);
- if (!len)
- return;
- if (len > 1024) {
- wchar_t *temp;
- temp = malloc(sizeof(wchar_t) * (len + 1));
- if (!temp)
- return;
- if (GetWindowTextW(hwnd, temp, len + 1))
- dstr_from_wcs(name, temp);
- free(temp);
- } else {
- wchar_t temp[1024 + 1];
- if (GetWindowTextW(hwnd, temp, len + 1))
- dstr_from_wcs(name, temp);
- }
- }
- void get_window_class(struct dstr *class, HWND hwnd)
- {
- wchar_t temp[256];
- temp[0] = 0;
- if (GetClassNameW(hwnd, temp, sizeof(temp) / sizeof(wchar_t)))
- dstr_from_wcs(class, temp);
- }
- /* not capturable or internal windows, exact executable names */
- static const char *internal_microsoft_exes_exact[] = {
- "startmenuexperiencehost.exe",
- "applicationframehost.exe",
- "peopleexperiencehost.exe",
- "shellexperiencehost.exe",
- "microsoft.notes.exe",
- "systemsettings.exe",
- "textinputhost.exe",
- "searchapp.exe",
- "video.ui.exe",
- "searchui.exe",
- "lockapp.exe",
- "cortana.exe",
- "gamebar.exe",
- "tabtip.exe",
- "time.exe",
- NULL,
- };
- /* partial matches start from the beginning of the executable name */
- static const char *internal_microsoft_exes_partial[] = {
- "windowsinternal",
- NULL,
- };
- static bool is_microsoft_internal_window_exe(const char *exe)
- {
- if (!exe)
- return false;
- for (const char **vals = internal_microsoft_exes_exact; *vals; vals++) {
- if (astrcmpi(exe, *vals) == 0)
- return true;
- }
- for (const char **vals = internal_microsoft_exes_partial; *vals;
- vals++) {
- if (astrcmpi_n(exe, *vals, strlen(*vals)) == 0)
- return true;
- }
- return false;
- }
- static void add_window(obs_property_t *p, HWND hwnd, add_window_cb callback)
- {
- struct dstr class = {0};
- struct dstr title = {0};
- struct dstr exe = {0};
- struct dstr encoded = {0};
- struct dstr desc = {0};
- if (!get_window_exe(&exe, hwnd))
- return;
- if (is_microsoft_internal_window_exe(exe.array)) {
- dstr_free(&exe);
- return;
- }
- get_window_title(&title, hwnd);
- if (dstr_cmp(&exe, "explorer.exe") == 0 && dstr_is_empty(&title)) {
- dstr_free(&exe);
- dstr_free(&title);
- return;
- }
- get_window_class(&class, hwnd);
- if (callback && !callback(title.array, class.array, exe.array)) {
- dstr_free(&title);
- dstr_free(&class);
- dstr_free(&exe);
- return;
- }
- dstr_printf(&desc, "[%s]: %s", exe.array, title.array);
- encode_dstr(&title);
- encode_dstr(&class);
- encode_dstr(&exe);
- dstr_cat_dstr(&encoded, &title);
- dstr_cat(&encoded, ":");
- dstr_cat_dstr(&encoded, &class);
- dstr_cat(&encoded, ":");
- dstr_cat_dstr(&encoded, &exe);
- obs_property_list_add_string(p, desc.array, encoded.array);
- dstr_free(&encoded);
- dstr_free(&desc);
- dstr_free(&class);
- dstr_free(&title);
- dstr_free(&exe);
- }
- static inline bool IsWindowCloaked(HWND window)
- {
- DWORD cloaked;
- HRESULT hr = DwmGetWindowAttribute(window, DWMWA_CLOAKED, &cloaked,
- sizeof(cloaked));
- return SUCCEEDED(hr) && cloaked;
- }
- static bool check_window_valid(HWND window, enum window_search_mode mode)
- {
- DWORD styles, ex_styles;
- RECT rect;
- if (!IsWindowVisible(window) ||
- (mode == EXCLUDE_MINIMIZED &&
- (IsIconic(window) || IsWindowCloaked(window))))
- return false;
- GetClientRect(window, &rect);
- styles = (DWORD)GetWindowLongPtr(window, GWL_STYLE);
- ex_styles = (DWORD)GetWindowLongPtr(window, GWL_EXSTYLE);
- if (ex_styles & WS_EX_TOOLWINDOW)
- return false;
- if (styles & WS_CHILD)
- return false;
- if (mode == EXCLUDE_MINIMIZED && (rect.bottom == 0 || rect.right == 0))
- return false;
- return true;
- }
- bool is_uwp_window(HWND hwnd)
- {
- wchar_t name[256];
- name[0] = 0;
- if (!GetClassNameW(hwnd, name, sizeof(name) / sizeof(wchar_t)))
- return false;
- return wcscmp(name, L"ApplicationFrameWindow") == 0;
- }
- HWND get_uwp_actual_window(HWND parent)
- {
- DWORD parent_id = 0;
- HWND child;
- GetWindowThreadProcessId(parent, &parent_id);
- child = FindWindowEx(parent, NULL, NULL, NULL);
- while (child) {
- DWORD child_id = 0;
- GetWindowThreadProcessId(child, &child_id);
- if (child_id != parent_id)
- return child;
- child = FindWindowEx(parent, child, NULL, NULL);
- }
- return NULL;
- }
- static HWND next_window(HWND window, enum window_search_mode mode, HWND *parent,
- bool use_findwindowex)
- {
- if (*parent) {
- window = *parent;
- *parent = NULL;
- }
- while (true) {
- if (use_findwindowex)
- window = FindWindowEx(GetDesktopWindow(), window, NULL,
- NULL);
- else
- window = GetNextWindow(window, GW_HWNDNEXT);
- if (!window || check_window_valid(window, mode))
- break;
- }
- if (is_uwp_window(window)) {
- HWND child = get_uwp_actual_window(window);
- if (child) {
- *parent = window;
- return child;
- }
- }
- return window;
- }
- static HWND first_window(enum window_search_mode mode, HWND *parent,
- bool *use_findwindowex)
- {
- HWND window = FindWindowEx(GetDesktopWindow(), NULL, NULL, NULL);
- if (!window) {
- *use_findwindowex = false;
- window = GetWindow(GetDesktopWindow(), GW_CHILD);
- } else {
- *use_findwindowex = true;
- }
- *parent = NULL;
- if (!check_window_valid(window, mode)) {
- window = next_window(window, mode, parent, *use_findwindowex);
- if (!window && *use_findwindowex) {
- *use_findwindowex = false;
- window = GetWindow(GetDesktopWindow(), GW_CHILD);
- if (!check_window_valid(window, mode))
- window = next_window(window, mode, parent,
- *use_findwindowex);
- }
- }
- if (is_uwp_window(window)) {
- HWND child = get_uwp_actual_window(window);
- if (child) {
- *parent = window;
- return child;
- }
- }
- return window;
- }
- void fill_window_list(obs_property_t *p, enum window_search_mode mode,
- add_window_cb callback)
- {
- HWND parent;
- bool use_findwindowex = false;
- HWND window = first_window(mode, &parent, &use_findwindowex);
- while (window) {
- add_window(p, window, callback);
- window = next_window(window, mode, &parent, use_findwindowex);
- }
- }
- static int window_rating(HWND window, enum window_priority priority,
- const char *class, const char *title, const char *exe,
- bool generic_class)
- {
- struct dstr cur_class = {0};
- struct dstr cur_title = {0};
- struct dstr cur_exe = {0};
- int val = 0x7FFFFFFF;
- if (!get_window_exe(&cur_exe, window))
- return 0x7FFFFFFF;
- get_window_title(&cur_title, window);
- get_window_class(&cur_class, window);
- bool class_matches = dstr_cmpi(&cur_class, class) == 0;
- bool exe_matches = dstr_cmpi(&cur_exe, exe) == 0;
- int title_val = abs(dstr_cmpi(&cur_title, title));
- /* always match by name if class is generic */
- if (generic_class) {
- if (priority == WINDOW_PRIORITY_EXE && !exe_matches)
- val = 0x7FFFFFFF;
- else
- val = title_val == 0 ? 0 : 0x7FFFFFFF;
- } else if (priority == WINDOW_PRIORITY_CLASS) {
- val = class_matches ? title_val : 0x7FFFFFFF;
- if (val != 0x7FFFFFFF && !exe_matches)
- val += 0x1000;
- } else if (priority == WINDOW_PRIORITY_TITLE) {
- val = title_val == 0 ? 0 : 0x7FFFFFFF;
- } else if (priority == WINDOW_PRIORITY_EXE) {
- val = exe_matches ? title_val : 0x7FFFFFFF;
- }
- dstr_free(&cur_class);
- dstr_free(&cur_title);
- dstr_free(&cur_exe);
- return val;
- }
- static const char *generic_class_substrings[] = {
- "Chrome",
- NULL,
- };
- static const char *generic_classes[] = {
- "Windows.UI.Core.CoreWindow",
- NULL,
- };
- static bool is_generic_class(const char *current_class)
- {
- const char **class = generic_class_substrings;
- while (*class) {
- if (astrstri(current_class, *class) != NULL) {
- return true;
- }
- class ++;
- }
- class = generic_classes;
- while (*class) {
- if (astrcmpi(current_class, *class) == 0) {
- return true;
- }
- class ++;
- }
- return false;
- }
- HWND find_window(enum window_search_mode mode, enum window_priority priority,
- const char *class, const char *title, const char *exe)
- {
- HWND parent;
- bool use_findwindowex = false;
- HWND window = first_window(mode, &parent, &use_findwindowex);
- HWND best_window = NULL;
- int best_rating = 0x7FFFFFFF;
- if (!class)
- return NULL;
- bool generic_class = is_generic_class(class);
- while (window) {
- int rating = window_rating(window, priority, class, title, exe,
- generic_class);
- if (rating < best_rating) {
- best_rating = rating;
- best_window = window;
- if (rating == 0)
- break;
- }
- window = next_window(window, mode, &parent, use_findwindowex);
- }
- return best_window;
- }
- struct top_level_enum_data {
- enum window_search_mode mode;
- enum window_priority priority;
- const char *class;
- const char *title;
- const char *exe;
- bool generic_class;
- HWND best_window;
- int best_rating;
- };
- BOOL CALLBACK enum_windows_proc(HWND window, LPARAM lParam)
- {
- struct top_level_enum_data *data = (struct top_level_enum_data *)lParam;
- if (!check_window_valid(window, data->mode))
- return TRUE;
- if (IsWindowCloaked(window))
- return TRUE;
- const int rating = window_rating(window, data->priority, data->class,
- data->title, data->exe,
- data->generic_class);
- if (rating < data->best_rating) {
- data->best_rating = rating;
- data->best_window = window;
- }
- return rating > 0;
- }
- HWND find_window_top_level(enum window_search_mode mode,
- enum window_priority priority, const char *class,
- const char *title, const char *exe)
- {
- if (!class)
- return NULL;
- struct top_level_enum_data data;
- data.mode = mode;
- data.priority = priority;
- data.class = class;
- data.title = title;
- data.exe = exe;
- data.generic_class = is_generic_class(class);
- data.best_window = NULL;
- data.best_rating = 0x7FFFFFFF;
- EnumWindows(enum_windows_proc, (LPARAM)&data);
- return data.best_window;
- }
|