win-capture: Add direct hooking and use by default

I've come to realize that it's probably not wise to deviate from the
original version's functionality due to the fact that the original
version works without issues.  I'm wondering if some of the capture
problems have been due to the fact that the direct hook method (via
CreateRemoteThread) was removed, so I put it back in, made it default,
and added an option to use anti-cheat compatibility just like in the
original version.
This commit is contained in:
jp9000
2015-07-05 12:29:27 -07:00
parent b0570c46c3
commit f4d0da4e04
5 changed files with 181 additions and 69 deletions

View File

@@ -6,6 +6,7 @@
#include <emmintrin.h>
#include <ipc-util/pipe.h>
#include "obfuscate.h"
#include "inject-library.h"
#include "graphics-hook-info.h"
#include "window-helpers.h"
#include "cursor-capture.h"
@@ -29,6 +30,7 @@
#define SETTING_TRANSPARENCY "allow_transparency"
#define SETTING_LIMIT_FRAMERATE "limit_framerate"
#define SETTING_CAPTURE_OVERLAYS "capture_overlays"
#define SETTING_ANTI_CHEAT_HOOK "anti_cheat_hook"
#define TEXT_GAME_CAPTURE obs_module_text("GameCapture")
#define TEXT_ANY_FULLSCREEN obs_module_text("GameCapture.AnyFullscreen")
@@ -44,6 +46,7 @@
#define TEXT_CAPTURE_CURSOR obs_module_text("CaptureCursor")
#define TEXT_LIMIT_FRAMERATE obs_module_text("GameCapture.LimitFramerate")
#define TEXT_CAPTURE_OVERLAYS obs_module_text("GameCapture.CaptureOverlays")
#define TEXT_ANTI_CHEAT_HOOK obs_module_text("GameCapture.AntiCheatHook")
#define DEFAULT_RETRY_INTERVAL 2.0f
#define ERROR_RETRY_INTERVAL 4.0f
@@ -62,6 +65,7 @@ struct game_capture_config {
bool allow_transparency : 1;
bool limit_framerate : 1;
bool capture_overlays : 1;
bool anticheat_hook : 1;
};
struct game_capture {
@@ -249,6 +253,8 @@ static inline void get_config(struct game_capture_config *cfg,
SETTING_LIMIT_FRAMERATE);
cfg->capture_overlays = obs_data_get_bool(settings,
SETTING_CAPTURE_OVERLAYS);
cfg->anticheat_hook = obs_data_get_bool(settings,
SETTING_ANTI_CHEAT_HOOK);
scale_str = obs_data_get_string(settings, SETTING_SCALE_RES);
ret = sscanf(scale_str, "%"PRIu32"x%"PRIu32,
@@ -554,23 +560,77 @@ static inline bool init_pipe(struct game_capture *gc)
return true;
}
static inline bool inject_library(HANDLE process, const wchar_t *dll)
{
return inject_library_obf(process, dll,
"D|hkqkW`kl{k\\osofj", 0xa178ef3655e5ade7,
"[uawaRzbhh{tIdkj~~", 0x561478dbd824387c,
"[fr}pboIe`dlN}", 0x395bfbc9833590fd,
"\\`zs}gmOzhhBq", 0x12897dd89168789a,
"GbfkDaezbp~X", 0x76aff7238788f7db);
}
static inline bool hook_direct(struct game_capture *gc,
const char *hook_path_rel)
{
wchar_t hook_path_abs_w[MAX_PATH];
wchar_t *hook_path_rel_w;
wchar_t *path_ret;
HANDLE process;
int ret;
os_utf8_to_wcs_ptr(hook_path_rel, 0, &hook_path_rel_w);
if (!hook_path_rel_w) {
warn("hook_direct: could not convert string");
return false;
}
path_ret = _wfullpath(hook_path_abs_w, hook_path_rel_w, MAX_PATH);
bfree(hook_path_rel_w);
if (path_ret == NULL) {
warn("hook_direct: could not make absolute path");
return false;
}
process = open_process(PROCESS_ALL_ACCESS, false, gc->process_id);
if (!process) {
warn("hook_direct: could not open process: %s (%lu)",
gc->config.executable, GetLastError());
return false;
}
ret = inject_library(process, hook_path_abs_w);
CloseHandle(process);
if (ret != 0) {
warn("hook_direct: inject failed: %ld", ret);
return false;
}
return true;
}
static inline bool create_inject_process(struct game_capture *gc,
const char *inject_path, const char *hook_path)
const char *inject_path, const char *hook_dll)
{
wchar_t *command_line_w = malloc(4096 * sizeof(wchar_t));
wchar_t *inject_path_w;
wchar_t *hook_path_w;
wchar_t *hook_dll_w;
bool anti_cheat = gc->config.anticheat_hook;
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
bool success = false;
os_utf8_to_wcs_ptr(inject_path, 0, &inject_path_w);
os_utf8_to_wcs_ptr(hook_path, 0, &hook_path_w);
os_utf8_to_wcs_ptr(hook_dll, 0, &hook_dll_w);
si.cb = sizeof(si);
swprintf(command_line_w, 4096, L"\"%s\" \"%s\" %lu",
inject_path_w, hook_path_w, gc->thread_id);
swprintf(command_line_w, 4096, L"\"%s\" \"%s\" %lu %lu",
inject_path_w, hook_dll_w,
(unsigned long)anti_cheat,
anti_cheat ? gc->thread_id : gc->process_id);
success = !!CreateProcessW(inject_path_w, command_line_w, NULL, NULL,
false, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
@@ -584,24 +644,28 @@ static inline bool create_inject_process(struct game_capture *gc,
free(command_line_w);
bfree(inject_path_w);
bfree(hook_path_w);
bfree(hook_dll_w);
return success;
}
static inline bool inject_hook(struct game_capture *gc)
{
bool matching_architecture;
bool success = false;
const char *hook_dll;
char *inject_path;
char *hook_path;
if (gc->process_is_64bit) {
hook_dll = "graphics-hook64.dll";
inject_path = obs_module_file("inject-helper64.exe");
hook_path = obs_module_file("graphics-hook64.dll");
} else {
hook_dll = "graphics-hook32.dll";
inject_path = obs_module_file("inject-helper32.exe");
hook_path = obs_module_file("graphics-hook32.dll");
}
hook_path = obs_module_file(hook_dll);
if (!check_file_integrity(gc, inject_path, "inject helper")) {
goto cleanup;
}
@@ -609,7 +673,20 @@ static inline bool inject_hook(struct game_capture *gc)
goto cleanup;
}
success = create_inject_process(gc, inject_path, hook_path);
#ifdef _WIN64
matching_architecture = gc->process_is_64bit;
#else
matching_architecture = !gc->process_is_64bit;
#endif
if (matching_architecture && !gc->config.anticheat_hook) {
info("using direct hook");
success = hook_direct(gc, hook_path);
} else {
info("using helper (%s hook)", gc->config.anticheat_hook ?
"compatibility" : "direct");
success = create_inject_process(gc, inject_path, hook_dll);
}
cleanup:
bfree(inject_path);
@@ -1173,7 +1250,7 @@ static void game_capture_tick(void *data, float seconds)
close_handle(&gc->injector_process);
if (exit_code != 0) {
warn("inject process failed: %lu", exit_code);
warn("inject process failed: %ld", (long)exit_code);
gc->error_acquiring = true;
} else if (!gc->capturing) {
@@ -1309,6 +1386,7 @@ static void game_capture_defaults(obs_data_t *settings)
obs_data_set_default_string(settings, SETTING_SCALE_RES, "0x0");
obs_data_set_default_bool(settings, SETTING_LIMIT_FRAMERATE, false);
obs_data_set_default_bool(settings, SETTING_CAPTURE_OVERLAYS, false);
obs_data_set_default_bool(settings, SETTING_ANTI_CHEAT_HOOK, false);
}
static bool any_fullscreen_callback(obs_properties_t *ppts,
@@ -1481,11 +1559,14 @@ static obs_properties_t *game_capture_properties(void *data)
obs_properties_add_bool(ppts, SETTING_LIMIT_FRAMERATE,
TEXT_LIMIT_FRAMERATE);
obs_properties_add_bool(ppts, SETTING_CURSOR, TEXT_CAPTURE_CURSOR);
obs_properties_add_bool(ppts, SETTING_ANTI_CHEAT_HOOK,
TEXT_ANTI_CHEAT_HOOK);
obs_properties_add_bool(ppts, SETTING_CAPTURE_OVERLAYS,
TEXT_CAPTURE_OVERLAYS);
obs_properties_add_bool(ppts, SETTING_CURSOR, TEXT_CAPTURE_CURSOR);
UNUSED_PARAMETER(data);
return ppts;
}