micetools/src/micetools/dll/hooks/files.c

452 lines
17 KiB
C

#define _MICE_FILES
#include "files.h"
HANDLE open_hook(file_hook_t* file_hook) {
open_hook_t* opened = (open_hook_t*)malloc(sizeof(open_hook_t));
ZeroMemory(opened, sizeof *opened);
opened->hook = file_hook;
HANDLE handle = GetDummyHandle();
opened->ctx.m_Handle = handle;
opened->ctx.m_HookData = file_hook->hook_data;
SetDataForHandle(handle, HDATA_FILE, opened, TRUE);
return handle;
}
file_hook_t* file_hook_list = NULL;
file_hook_t* new_file_hook(LPCWSTR filename) {
file_hook_t* hook = (file_hook_t*)malloc(sizeof(file_hook_t));
memset(hook, 0, sizeof *hook);
hook->filename = filename;
return hook;
}
void hook_file(file_hook_t* hook) {
hook->next = NULL;
if (file_hook_list == NULL) {
file_hook_list = hook;
return;
}
file_hook_t* hl = file_hook_list;
while (hl->next != NULL) hl = hl->next;
hl->next = hook;
};
struct buffer_file {
LPBYTE buffer;
DWORD nBytes;
DWORD access;
};
BOOL bf_ReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) {
struct buffer_file* pBF = (struct buffer_file*)ctx->m_HookData;
if (!(pBF->access & GENERIC_READ)) {
SetLastError(ERROR_ACCESS_DENIED);
return FALSE;
}
if (ctx->m_Pointer.QuadPart > pBF->nBytes) {
*lpNumberOfBytesRead = 0;
return TRUE;
}
if (ctx->m_Pointer.QuadPart + nNumberOfBytesToRead > pBF->nBytes) {
nNumberOfBytesToRead = (pBF->nBytes - ctx->m_Pointer.QuadPart) & 0xffffffff;
}
*lpNumberOfBytesRead = nNumberOfBytesToRead;
memcpy(lpBuffer, pBF->buffer + ctx->m_Pointer.QuadPart, nNumberOfBytesToRead);
return TRUE;
}
BOOL bf_WriteFile(file_context_t* ctx, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) {
struct buffer_file* pBF = (struct buffer_file*)ctx->m_HookData;
if (!(pBF->access & GENERIC_WRITE)) {
SetLastError(ERROR_ACCESS_DENIED);
return FALSE;
}
if (ctx->m_Pointer.QuadPart > pBF->nBytes) {
*lpNumberOfBytesWritten = 0;
return TRUE;
}
if (ctx->m_Pointer.QuadPart + nNumberOfBytesToWrite > pBF->nBytes) {
nNumberOfBytesToWrite = (pBF->nBytes - ctx->m_Pointer.QuadPart) & 0xffffffff;
}
*lpNumberOfBytesWritten = nNumberOfBytesToWrite;
memcpy(pBF->buffer + ctx->m_Pointer.QuadPart, lpBuffer, nNumberOfBytesToWrite);
return TRUE;
}
void hook_file_with_buffer(LPCWSTR filename, LPBYTE buffer, DWORD nBytes, DWORD access) {
file_hook_t* hook = new_file_hook(filename);
struct buffer_file** ppBF = &((struct buffer_file*)hook->hook_data);
*ppBF = malloc(sizeof **ppBF);
(*ppBF)->buffer = buffer;
(*ppBF)->nBytes = nBytes;
(*ppBF)->access = access;
hook->ReadFile = bf_ReadFile;
hook->WriteFile = bf_WriteFile;
hook_file(hook);
}
drive_redirect_t DRIVE_REDIRECT_TABLE[] = {
// Note: Had to create last_shime.log
{ .drive = "C:\\Documents and Settings\\AppUser\\temp\\", .path = ".\\dev\\temp\\" },
{ .drive = "E:\\", .path = "\\\\.\\E:" },
// {.drive = "C:\\ProgramData/boost_interprocess/", .path = "\\\\.\\ipc\\"},
};
char _redirected_path[MAX_PATH];
file_hook_t* find_hook(LPCWSTR lpFileName) {
file_hook_t* file_hook = file_hook_list;
while (file_hook != NULL) {
if (wcscmp(lpFileName, file_hook->filename) == 0 ||
(file_hook->altFilename != NULL && wcscmp(lpFileName, file_hook->altFilename) == 0)) {
return file_hook;
}
file_hook = file_hook->next;
}
return NULL;
};
char WORKING_DIR[MAX_PATH + 1] = { 0 };
char get_gamedata_drive() {
if (WORKING_DIR[0] == 0x00) {
GetCurrentDirectoryA(sizeof WORKING_DIR, WORKING_DIR);
}
return WORKING_DIR[0];
}
inline char char_lower(char value) {
if ('A' <= value && value <= 'Z') return value - 'A' + 'a';
return value;
}
BOOL redirect_path(LPCSTR path, LPCSTR* redirected) {
for (int i = 0; i < sizeof DRIVE_REDIRECT_TABLE / sizeof DRIVE_REDIRECT_TABLE[0]; i++) {
drive_redirect_t row = DRIVE_REDIRECT_TABLE[i];
if (strncmp(path, row.drive, strlen(row.drive)) == 0) {
log_trace(HOOKS_LOGGER, "Redirecting '%s' to '%s'", path, row.path);
size_t new_len = strlen(path) - strlen(row.drive) + strlen(row.path);
strcpy_s(_redirected_path, new_len + 1, row.path);
char* dst = _redirected_path + strlen(row.path);
size_t len = strlen(path) - strlen(row.drive);
const char* src = path + strlen(row.drive);
for (; len > 0; len--) (dst++)[0] = (src++)[0];
dst[0] = 0;
log_trace(HOOKS_LOGGER, "New filename: '%s'", _redirected_path);
make_dirs(_redirected_path);
*redirected = _redirected_path;
return TRUE;
}
}
// Don't redirect local paths
GetCurrentDirectoryA(sizeof WORKING_DIR, WORKING_DIR);
if (strstr(path, WORKING_DIR) == path) {
puts("SKIP");
return FALSE;
}
if ((('a' <= path[0] && path[0] <= 'z') || ('A' <= path[0] && path[0] <= 'Z')) &&
path[1] == ':' && (path[2] == '/' || path[2] == '\\')) {
ZeroMemory(_redirected_path, sizeof _redirected_path);
snprintf(_redirected_path, sizeof _redirected_path, "dev\\%c\\%s", char_lower(path[0]),
path + 3);
make_dirs(_redirected_path);
*redirected = _redirected_path;
return TRUE;
}
return FALSE;
}
BOOL redirect_path_w(LPCWSTR lpFileName, LPCSTR* redirected) {
char cFileName[MAX_PATH] = { 0 };
WideCharToMultiByte(CP_ACP, 0, lpFileName, wcslen(lpFileName), cFileName, sizeof cFileName,
NULL, NULL);
return redirect_path(cFileName, redirected);
}
HANDLE WINAPI FakeCreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile) {
file_hook_t* found_fh = find_hook(lpFileName);
if (found_fh != NULL) {
HANDLE handle = open_hook(found_fh);
log_info(HOOKS_LOGGER, "CreateFileW(%ls) -> 0x%p", lpFileName, handle);
return handle;
}
HANDLE handle;
LPCSTR redirected;
if (redirect_path_w(lpFileName, &redirected)) {
handle = TrueCreateFileA(redirected, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
log_misc(HOOKS_LOGGER, "CreateFileW(%s) -> 0x%p", redirected, handle);
} else {
handle = TrueCreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
log_misc(HOOKS_LOGGER, "CreateFileW(%ls) -> 0x%p", lpFileName, handle);
}
return handle;
}
HANDLE WINAPI FakeCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile) {
WCHAR wideFileName[MAX_PATH + 1];
MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, (LPWSTR)&wideFileName, MAX_PATH + 1);
file_hook_t* found_fh = find_hook(wideFileName);
if (found_fh != NULL) {
HANDLE handle = open_hook(found_fh);
log_info(HOOKS_LOGGER, "CreateFileA(%s) -> 0x%p", lpFileName, handle);
return handle;
}
redirect_path(lpFileName, &lpFileName);
HANDLE handle = TrueCreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
log_misc(HOOKS_LOGGER, "CreateFileA(%s) -> 0x%p", lpFileName, handle);
return handle;
}
BOOL WINAPI FakePathFileExistsA(LPCSTR pszPath) {
log_misc(HOOKS_LOGGER, "PathFileExists(%s)", pszPath);
redirect_path(pszPath, &pszPath);
BOOL ret = TruePathFileExistsA(pszPath);
return ret;
}
BOOL WINAPI FakePathFileExistsW(LPCWSTR pszPath) {
log_misc(HOOKS_LOGGER, "PathFileExists(%ls)", pszPath);
LPCSTR redirected;
if (redirect_path_w(pszPath, &redirected)) {
return TruePathFileExistsA(redirected);
}
return TruePathFileExistsW(pszPath);
}
BOOL WINAPI FakeDeleteFileA(LPCSTR pszPath) {
LPCSTR redirected;
if (redirect_path(pszPath, &redirected)) {
return TrueDeleteFileA(redirected);
}
return TrueDeleteFileA(pszPath);
}
BOOL WINAPI FakeDeleteFileW(LPCWSTR pszPath) {
LPCSTR redirected;
if (redirect_path_w(pszPath, &redirected)) {
return TrueDeleteFileA(redirected);
}
return TrueDeleteFileW(pszPath);
}
BOOL WINAPI FakeDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer,
DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) {
open_hook_t* pHData = GetDataForHandle(hDevice, HDATA_FILE);
if (pHData == NULL) {
log_trace(HOOKS_LOGGER, "DeviceIoControl(0x%p, 0x%08x, 0x%p, 0x%x, -, 0x%x, 0, 0)", hDevice,
dwIoControlCode, lpInBuffer, nInBufferSize, nOutBufferSize);
return TrueDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer,
nOutBufferSize, lpBytesReturned, lpOverlapped);
}
file_hook_t* file_hook = pHData->hook;
if (!file_hook->DeviceIoControl) {
log_error(HOOKS_LOGGER, "DeviceIoControl(%ls) unimplemented", file_hook->filename);
return FALSE;
}
BOOL ret =
file_hook->DeviceIoControl(&(pHData->ctx), dwIoControlCode, lpInBuffer, nInBufferSize,
lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped);
if (lpOverlapped) {
SetEvent(lpOverlapped->hEvent);
if (ret && lpBytesReturned) {
lpOverlapped->InternalHigh = *lpBytesReturned;
}
}
return ret;
}
DWORD WINAPI FakeSetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
DWORD dwMoveMethod) {
open_hook_t* pHData = GetDataForHandle(hFile, HDATA_FILE);
if (pHData == NULL)
return TrueSetFilePointer(hFile, lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod);
if (dwMoveMethod == FILE_BEGIN) {
if (*lpDistanceToMoveHigh)
pHData->ctx.m_Pointer.HighPart = *lpDistanceToMoveHigh;
else
pHData->ctx.m_Pointer.HighPart = 0;
pHData->ctx.m_Pointer.LowPart = lDistanceToMove;
} else if (dwMoveMethod == FILE_END) {
log_error("files", "FILE_END unimplemented");
return 0xFFFFFFFF;
} else {
if (lpDistanceToMoveHigh) pHData->ctx.m_Pointer.HighPart += *lpDistanceToMoveHigh;
pHData->ctx.m_Pointer.LowPart += lDistanceToMove;
}
return pHData->ctx.m_Pointer.LowPart;
}
BOOL WINAPI FakeSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove,
PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) {
open_hook_t* pHData = GetDataForHandle(hFile, HDATA_FILE);
if (pHData != NULL) {
if (dwMoveMethod == FILE_BEGIN) {
pHData->ctx.m_Pointer = liDistanceToMove;
} else if (dwMoveMethod == FILE_END) {
log_error("files", "FILE_END unimplemented");
return FALSE;
} else {
pHData->ctx.m_Pointer.QuadPart += liDistanceToMove.QuadPart;
}
if (lpNewFilePointer) lpNewFilePointer->QuadPart = pHData->ctx.m_Pointer.QuadPart;
return TRUE;
}
return TrueSetFilePointerEx(hFile, liDistanceToMove, lpNewFilePointer, dwMoveMethod);
}
DWORD WINAPI FakeGetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize) {
open_hook_t* pHData = GetDataForHandle(hFile, HDATA_FILE);
if (pHData == NULL) {
return TrueGetFileSizeEx(hFile, lpFileSize);
}
file_hook_t* file_hook = pHData->hook;
if (!file_hook->GetFileSizeEx) {
log_error(HOOKS_LOGGER, "GetFileSizeEx(%ls) unimplemented", file_hook->filename);
return FALSE;
}
return file_hook->GetFileSizeEx(&(pHData->ctx), lpFileSize);
}
DWORD WINAPI FakeWriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) {
if (hFile == GetStdHandle(STD_OUTPUT_HANDLE) || hFile == GetStdHandle(STD_ERROR_HANDLE)) {
return TrueWriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten,
lpOverlapped);
}
open_hook_t* pHData = GetDataForHandle(hFile, HDATA_FILE);
if (pHData == NULL) {
return TrueWriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten,
lpOverlapped);
}
file_hook_t* file_hook = pHData->hook;
if (!file_hook->WriteFile) {
log_error(HOOKS_LOGGER, "WriteFile(%ls) unimplemented", file_hook->filename);
return FALSE;
}
BOOL ret = file_hook->WriteFile(&(pHData->ctx), lpBuffer, nNumberOfBytesToWrite,
lpNumberOfBytesWritten, lpOverlapped);
if (ret) pHData->ctx.m_Pointer.QuadPart += *lpNumberOfBytesWritten;
return ret;
}
BOOL WINAPI FakeReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) {
if (hFile == GetStdHandle(STD_INPUT_HANDLE)) {
return TrueReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead,
lpOverlapped);
}
// log_misc(HOOKS_LOGGER, "ReadFile(%d) %d", hFile, nNumberOfBytesToRead);
open_hook_t* pHData = GetDataForHandle(hFile, HDATA_FILE);
if (pHData == NULL) {
return TrueReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead,
lpOverlapped);
}
file_hook_t* file_hook = pHData->hook;
if (!file_hook->ReadFile) {
log_error(HOOKS_LOGGER, "ReadFile(%ls) unimplemented", file_hook->filename);
return FALSE;
}
BOOL ret = file_hook->ReadFile(&(pHData->ctx), lpBuffer, nNumberOfBytesToRead,
lpNumberOfBytesRead, lpOverlapped);
if (ret) pHData->ctx.m_Pointer.QuadPart += *lpNumberOfBytesRead;
return ret;
}
BOOL WINAPI FakeCloseHandle(HANDLE hObject) {
RemoveDataForHandle(hObject, HDATA_ANY);
return TrueCloseHandle(hObject);
}
int WINAPIV Fake_stat64i32(const char* path, struct _stat64i32* buffer) {
redirect_path((char*)path, &path);
return True_stat64i32(path, buffer);
};
DWORD WINAPI FakeGetFileAttributesA(LPCSTR lpFileName) {
// The game quits out if MiniDump is present!
if (strcmp(lpFileName, "Y:\\MiniDump\\") == 0) {
return 0;
}
LPCSTR redirected;
if (redirect_path(lpFileName, &redirected)) {
return TrueGetFileAttributesA(redirected);
}
return TrueGetFileAttributesA(lpFileName);
}
DWORD WINAPI FakeGetFileAttributesW(LPCWSTR lpFileName) {
LPCSTR redirected;
if (redirect_path_w(lpFileName, &redirected)) {
return TrueGetFileAttributesA(redirected);
}
return TrueGetFileAttributesW(lpFileName);
}
void hook_io() {
hook("Kernel32.dll", "DeviceIoControl", FakeDeviceIoControl, (void**)&TrueDeviceIoControl);
hook("Kernel32.dll", "CreateFileA", FakeCreateFileA, (void**)&TrueCreateFileA);
hook("Kernel32.dll", "CreateFileW", FakeCreateFileW, (void**)&TrueCreateFileW);
hook("Kernel32.dll", "CloseHandle", FakeCloseHandle, (void**)&TrueCloseHandle);
hook("Kernel32.dll", "SetFilePointer", FakeSetFilePointer, (void**)&TrueSetFilePointer);
hook("Kernel32.dll", "SetFilePointerEx", FakeSetFilePointerEx, (void**)&TrueSetFilePointerEx);
hook("Kernel32.dll", "WriteFile", FakeWriteFile, (void**)&TrueWriteFile);
hook("Kernel32.dll", "ReadFile", FakeReadFile, (void**)&TrueReadFile);
hook("Kernel32.dll", "GetFileSizeEx", FakeGetFileSizeEx, (void**)&TrueGetFileSizeEx);
hook("Shlwapi.dll", "PathFileExistsA", FakePathFileExistsA, (void**)&TruePathFileExistsA);
hook("Shlwapi.dll", "PathFileExistsW", FakePathFileExistsW, (void**)&TruePathFileExistsW);
hook("Kernel32.dll", "DeleteFileA", FakeDeleteFileA, (void**)&TrueDeleteFileA);
hook("Kernel32.dll", "DeleteFileW", FakeDeleteFileW, (void**)&TrueDeleteFileW);
hook("Kernel32.dll", "GetFileAttributesA", FakeGetFileAttributesA,
(void**)&TrueGetFileAttributesA);
hook("Kernel32.dll", "GetFileAttributesW", FakeGetFileAttributesW,
(void**)&TrueGetFileAttributesW);
hook("MSVCR90.DLL", "_stat64i32", Fake_stat64i32, (void**)&True_stat64i32);
}