segatools/hooklib/path.c

628 lines
14 KiB
C
Raw Normal View History

#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#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);
2019-09-02 21:11:32 +00:00
static HANDLE WINAPI hook_FindFirstFileExA(
const char *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static HANDLE WINAPI hook_FindFirstFileExW(
const wchar_t *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
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);
2019-09-02 21:11:32 +00:00
static HANDLE WINAPI (*next_FindFirstFileExA)(
const char *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static HANDLE WINAPI (*next_FindFirstFileExW)(
const wchar_t *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
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,
2019-09-02 21:11:32 +00:00
}, {
.name = "FindFirstFileExA",
.patch = hook_FindFirstFileExA,
.link = (void **) &next_FindFirstFileExA,
}, {
.name = "FindFirstFileExW",
.patch = hook_FindFirstFileExW,
.link = (void **) &next_FindFirstFileExW,
}, {
.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;
}
2019-09-02 21:11:32 +00:00
static HANDLE WINAPI hook_FindFirstFileExA(
const char *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags)
{
char *trans;
HANDLE result;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileExA(
trans ? trans : lpFileName,
fInfoLevelId,
lpFindFileData,
fSearchOp,
lpSearchFilter,
dwAdditionalFlags);
free(trans);
return result;
}
static HANDLE WINAPI hook_FindFirstFileExW(
const wchar_t *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags)
{
wchar_t *trans;
HANDLE result;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileExW(
trans ? trans : lpFileName,
fInfoLevelId,
lpFindFileData,
fSearchOp,
lpSearchFilter,
dwAdditionalFlags);
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;
}