From 744a7e6560319b0713ce798e9581dabbee71a450 Mon Sep 17 00:00:00 2001 From: Tau Date: Wed, 15 May 2019 20:10:32 -0400 Subject: [PATCH] hooklib/path.c: Add initial path rewriting hooks --- hooklib/meson.build | 2 + hooklib/path.c | 524 ++++++++++++++++++++++++++++++++++++++++++++ hooklib/path.h | 12 + 3 files changed, 538 insertions(+) create mode 100644 hooklib/path.c create mode 100644 hooklib/path.h diff --git a/hooklib/meson.build b/hooklib/meson.build index a96e907..2ace752 100644 --- a/hooklib/meson.build +++ b/hooklib/meson.build @@ -15,6 +15,8 @@ hooklib_lib = static_library( 'fdshark.h', 'gfx.c', 'gfx.h', + 'path.c', + 'path.h', 'reg.c', 'reg.h', 'setupapi.c', diff --git a/hooklib/path.c b/hooklib/path.c new file mode 100644 index 0000000..9a42f88 --- /dev/null +++ b/hooklib/path.c @@ -0,0 +1,524 @@ +#include + +#include +#include +#include +#include + +#include "hook/hr.h" +#include "hook/table.h" + +#include "hooklib/path.h" + +/* Helpers */ + +static void path_hook_init(void); +static BOOL path_transform_a(char **out, const char *src); +static BOOL path_transform_w(wchar_t **out, const wchar_t *src); + +/* API hooks */ + +static BOOL WINAPI hook_CreateDirectoryA( + const char *lpFileName, + SECURITY_ATTRIBUTES *lpSecurityAttributes); + +static BOOL WINAPI hook_CreateDirectoryW( + const wchar_t *lpFileName, + SECURITY_ATTRIBUTES *lpSecurityAttributes); + +static HANDLE WINAPI hook_CreateFileA( + const char *lpFileName, + uint32_t dwDesiredAccess, + uint32_t dwShareMode, + SECURITY_ATTRIBUTES *lpSecurityAttributes, + uint32_t dwCreationDisposition, + uint32_t dwFlagsAndAttributes, + HANDLE hTemplateFile); + +static HANDLE WINAPI hook_CreateFileW( + const wchar_t *lpFileName, + uint32_t dwDesiredAccess, + uint32_t dwShareMode, + SECURITY_ATTRIBUTES *lpSecurityAttributes, + uint32_t dwCreationDisposition, + uint32_t dwFlagsAndAttributes, + HANDLE hTemplateFile); + +static DWORD WINAPI hook_GetFileAttributesA(const char *lpFileName); + +static DWORD WINAPI hook_GetFileAttributesW(const wchar_t *lpFileName); + +static BOOL WINAPI hook_GetFileAttributesExA( + const char *lpFileName, + GET_FILEEX_INFO_LEVELS fInfoLevelId, + void *lpFileInformation); + +static BOOL WINAPI hook_GetFileAttributesExW( + const wchar_t *lpFileName, + GET_FILEEX_INFO_LEVELS fInfoLevelId, + void *lpFileInformation); + +/* Link pointers */ + +static BOOL WINAPI (*next_CreateDirectoryA)( + const char *lpFileName, + SECURITY_ATTRIBUTES *lpSecurityAttributes); + +static BOOL WINAPI (*next_CreateDirectoryW)( + const wchar_t *lpFileName, + SECURITY_ATTRIBUTES *lpSecurityAttributes); + +static HANDLE WINAPI (*next_CreateFileA)( + const char *lpFileName, + uint32_t dwDesiredAccess, + uint32_t dwShareMode, + SECURITY_ATTRIBUTES *lpSecurityAttributes, + uint32_t dwCreationDisposition, + uint32_t dwFlagsAndAttributes, + HANDLE hTemplateFile); + +static HANDLE WINAPI (*next_CreateFileW)( + const wchar_t *lpFileName, + uint32_t dwDesiredAccess, + uint32_t dwShareMode, + SECURITY_ATTRIBUTES *lpSecurityAttributes, + uint32_t dwCreationDisposition, + uint32_t dwFlagsAndAttributes, + HANDLE hTemplateFile); + +static DWORD WINAPI (*next_GetFileAttributesA)(const char *lpFileName); + +static DWORD WINAPI (*next_GetFileAttributesW)(const wchar_t *lpFileName); + +static BOOL WINAPI (*next_GetFileAttributesExA)( + const char *lpFileName, + GET_FILEEX_INFO_LEVELS fInfoLevelId, + void *lpFileInformation); + +static BOOL WINAPI (*next_GetFileAttributesExW)( + const wchar_t *lpFileName, + GET_FILEEX_INFO_LEVELS fInfoLevelId, + void *lpFileInformation); + +/* Hook table */ + +static const struct hook_symbol path_hook_syms[] = { + { + .name = "CreateDirectoryA", + .patch = hook_CreateDirectoryA, + .link = (void **) &next_CreateDirectoryA, + }, { + .name = "CreateDirectoryW", + .patch = hook_CreateDirectoryW, + .link = (void **) &next_CreateDirectoryW, + }, { + .name = "CreateFileA", + .patch = hook_CreateFileA, + .link = (void **) &next_CreateFileA, + }, { + .name = "CreateFileW", + .patch = hook_CreateFileW, + .link = (void **) &next_CreateFileW, + }, { + .name = "GetFileAttributesA", + .patch = hook_GetFileAttributesA, + .link = (void **) &next_GetFileAttributesA, + }, { + .name = "GetFileAttributesW", + .patch = hook_GetFileAttributesW, + .link = (void **) &next_GetFileAttributesW, + }, { + .name = "GetFileAttributesExA", + .patch = hook_GetFileAttributesExA, + .link = (void **) &next_GetFileAttributesExA, + }, { + .name = "GetFileAttributesExW", + .patch = hook_GetFileAttributesExW, + .link = (void **) &next_GetFileAttributesExW, + } +}; + +static bool path_hook_initted; +static CRITICAL_SECTION path_hook_lock; +static path_hook_t *path_hook_list; +static size_t path_hook_count; + +HRESULT path_hook_push(path_hook_t hook) +{ + path_hook_t *tmp; + HRESULT hr; + + assert(hook != NULL); + + path_hook_init(); + + EnterCriticalSection(&path_hook_lock); + + tmp = realloc( + path_hook_list, + (path_hook_count + 1) * sizeof(path_hook_t)); + + if (tmp == NULL) { + hr = E_OUTOFMEMORY; + + goto end; + } + + path_hook_list = tmp; + path_hook_list[path_hook_count++] = hook; + + hr = S_OK; + +end: + LeaveCriticalSection(&path_hook_lock); + + return hr; +} + +static void path_hook_init(void) +{ + /* Init is not thread safe because API hook init is not thread safe blah + blah blah you know the drill by now. */ + + if (path_hook_initted) { + return; + } + + path_hook_initted = true; + InitializeCriticalSection(&path_hook_lock); + + hook_table_apply( + NULL, + "kernel32.dll", + path_hook_syms, + _countof(path_hook_syms)); +} + +static BOOL path_transform_a(char **out, const char *src) +{ + wchar_t *src_w; + size_t src_c; + wchar_t *dest_w; + char *dest_a; + size_t dest_s; + BOOL ok; + + assert(out != NULL); + + src_w = NULL; + dest_w = NULL; + dest_a = NULL; + *out = NULL; + + if (src == NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + ok = FALSE; + + goto end; + } + + /* Widen the path */ + + mbstowcs_s(&src_c, NULL, 0, src, 0); + src_w = malloc(src_c * sizeof(wchar_t)); + + if (src_w == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + ok = FALSE; + + goto end; + } + + mbstowcs_s(NULL, src_w, src_c, src, src_c - 1); + + /* Try applying a path transform */ + + ok = path_transform_w(&dest_w, src_w); /* Take ownership! */ + + if (!ok || dest_w == NULL) { + goto end; + } + + /* Narrow the transformed path */ + + wcstombs_s(&dest_s, NULL, 0, dest_w, 0); + dest_a = malloc(dest_s * sizeof(char)); + + if (dest_a == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + ok = FALSE; + + goto end; + } + + wcstombs_s(NULL, dest_a, dest_s, dest_w, dest_s - 1); + + *out = dest_a; /* Relinquish ownership to caller! */ + ok = TRUE; + +end: + free(dest_w); + free(src_w); + + return ok; +} + +static BOOL path_transform_w(wchar_t **out, const wchar_t *src) +{ + BOOL ok; + HRESULT hr; + wchar_t *dest; + size_t dest_c; + size_t i; + + assert(out != NULL); + + dest = NULL; + *out = NULL; + + EnterCriticalSection(&path_hook_lock); + + for (i = 0 ; i < path_hook_count ; i++) { + hr = path_hook_list[i](src, NULL, &dest_c); + + if (FAILED(hr)) { + ok = hr_propagate_win32(hr, FALSE); + + goto end; + } + + if (hr == S_FALSE) { + continue; + } + + dest = malloc(dest_c * sizeof(wchar_t)); + + if (dest == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + ok = FALSE; + + goto end; + } + + hr = path_hook_list[i](src, dest, &dest_c); + + if (FAILED(hr)) { + ok = hr_propagate_win32(hr, FALSE); + + goto end; + } + + break; + } + + *out = dest; + dest = NULL; + ok = TRUE; + +end: + LeaveCriticalSection(&path_hook_lock); + + free(dest); + + return ok; +} + +/* Dumping ground for kernel32 file system ops whose path parameters we have to + hook into and translate. This list will grow over time as we go back and + fix up older games that don't pay attention to the mount point registry. */ + +static BOOL WINAPI hook_CreateDirectoryA( + const char *lpFileName, + SECURITY_ATTRIBUTES *lpSecurityAttributes) +{ + char *trans; + BOOL ok; + + ok = path_transform_a(&trans, lpFileName); + + if (!ok) { + return FALSE; + } + + ok = next_CreateDirectoryA( + trans ? trans : lpFileName, + lpSecurityAttributes); + + free(trans); + + return ok; +} + +static BOOL WINAPI hook_CreateDirectoryW( + const wchar_t *lpFileName, + SECURITY_ATTRIBUTES *lpSecurityAttributes) +{ + wchar_t *trans; + BOOL ok; + + ok = path_transform_w(&trans, lpFileName); + + if (!ok) { + return FALSE; + } + + ok = next_CreateDirectoryW( + trans ? trans : lpFileName, + lpSecurityAttributes); + + free(trans); + + return ok; +} + +/* Don't pull in the entire iohook framework just for CreateFileA/CreateFileW */ + +static HANDLE WINAPI hook_CreateFileA( + const char *lpFileName, + uint32_t dwDesiredAccess, + uint32_t dwShareMode, + SECURITY_ATTRIBUTES *lpSecurityAttributes, + uint32_t dwCreationDisposition, + uint32_t dwFlagsAndAttributes, + HANDLE hTemplateFile) +{ + char *trans; + HANDLE result; + BOOL ok; + + ok = path_transform_a(&trans, lpFileName); + + if (!ok) { + return INVALID_HANDLE_VALUE; + } + + result = next_CreateFileA( + trans ? trans : lpFileName, + dwDesiredAccess, + dwShareMode, + lpSecurityAttributes, + dwCreationDisposition, + dwFlagsAndAttributes, + hTemplateFile); + + free(trans); + + return result; +} + +static HANDLE WINAPI hook_CreateFileW( + const wchar_t *lpFileName, + uint32_t dwDesiredAccess, + uint32_t dwShareMode, + SECURITY_ATTRIBUTES *lpSecurityAttributes, + uint32_t dwCreationDisposition, + uint32_t dwFlagsAndAttributes, + HANDLE hTemplateFile) +{ + wchar_t *trans; + HANDLE result; + BOOL ok; + + ok = path_transform_w(&trans, lpFileName); + + if (!ok) { + return INVALID_HANDLE_VALUE; + } + + result = next_CreateFileW( + trans ? trans : lpFileName, + dwDesiredAccess, + dwShareMode, + lpSecurityAttributes, + dwCreationDisposition, + dwFlagsAndAttributes, + hTemplateFile); + + free(trans); + + return result; +} + +static DWORD WINAPI hook_GetFileAttributesA(const char *lpFileName) +{ + char *trans; + DWORD result; + BOOL ok; + + ok = path_transform_a(&trans, lpFileName); + + if (!ok) { + return INVALID_FILE_ATTRIBUTES; + } + + result = next_GetFileAttributesA(trans ? trans : lpFileName); + free(trans); + + return result; +} + +static DWORD WINAPI hook_GetFileAttributesW(const wchar_t *lpFileName) +{ + wchar_t *trans; + DWORD result; + BOOL ok; + + ok = path_transform_w(&trans, lpFileName); + + if (!ok) { + return INVALID_FILE_ATTRIBUTES; + } + + result = next_GetFileAttributesW(trans ? trans : lpFileName); + free(trans); + + return result; +} + +static BOOL WINAPI hook_GetFileAttributesExA( + const char *lpFileName, + GET_FILEEX_INFO_LEVELS fInfoLevelId, + void *lpFileInformation) +{ + char *trans; + BOOL ok; + + ok = path_transform_a(&trans, lpFileName); + + if (!ok) { + return INVALID_FILE_ATTRIBUTES; + } + + ok = next_GetFileAttributesExA( + trans ? trans : lpFileName, + fInfoLevelId, + lpFileInformation); + + free(trans); + + return ok; +} + +static BOOL WINAPI hook_GetFileAttributesExW( + const wchar_t *lpFileName, + GET_FILEEX_INFO_LEVELS fInfoLevelId, + void *lpFileInformation) +{ + wchar_t *trans; + BOOL ok; + + ok = path_transform_w(&trans, lpFileName); + + if (!ok) { + return INVALID_FILE_ATTRIBUTES; + } + + ok = next_GetFileAttributesExW( + trans ? trans : lpFileName, + fInfoLevelId, + lpFileInformation); + + free(trans); + + return ok; +} diff --git a/hooklib/path.h b/hooklib/path.h new file mode 100644 index 0000000..9e7b408 --- /dev/null +++ b/hooklib/path.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include + +typedef HRESULT (*path_hook_t)( + const wchar_t *src, + wchar_t *dest, + size_t *count); + +HRESULT path_hook_push(path_hook_t hook);