mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-04-01 06:45:04 -05:00
269 lines
6.5 KiB
C
269 lines
6.5 KiB
C
#include "hooklib/memfile.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <windows.h>
|
|
|
|
#include "hook/hr.h"
|
|
#include "hook/iohook.h"
|
|
#include "hook/table.h"
|
|
|
|
#include "util/array.h"
|
|
#include "util/log.h"
|
|
#include "util/str.h"
|
|
|
|
struct file_entry {
|
|
char *path;
|
|
wchar_t *wpath;
|
|
HANDLE fd;
|
|
int64_t pos;
|
|
|
|
const void *data;
|
|
uint32_t sz;
|
|
enum memfile_hook_path_mode path_mode;
|
|
};
|
|
|
|
static struct array hooked_files;
|
|
static CRITICAL_SECTION hooked_files_cs;
|
|
|
|
|
|
BOOL my_GetFileInformationByHandle(
|
|
HANDLE hFile,
|
|
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
|
|
);
|
|
|
|
BOOL (*real_GetFileInformationByHandle)(
|
|
HANDLE hFile,
|
|
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
|
|
);
|
|
|
|
static const struct hook_symbol memfile_hook_kernel32_syms[] = {
|
|
{.name = "GetFileInformationByHandle",
|
|
.patch = my_GetFileInformationByHandle,
|
|
.link = (void **) &real_GetFileInformationByHandle},
|
|
};
|
|
|
|
|
|
void memfile_hook_init(void)
|
|
{
|
|
array_init(&hooked_files);
|
|
InitializeCriticalSection(&hooked_files_cs);
|
|
|
|
hook_table_apply(
|
|
NULL,
|
|
"Kernel32.dll",
|
|
memfile_hook_kernel32_syms,
|
|
lengthof(memfile_hook_kernel32_syms));
|
|
|
|
log_info("Initialized");
|
|
}
|
|
|
|
void memfile_hook_fini(void)
|
|
{
|
|
EnterCriticalSection(&hooked_files_cs);
|
|
struct file_entry *entry;
|
|
|
|
for (size_t i = 0; i < hooked_files.nitems; i++) {
|
|
entry = array_item(struct file_entry, &hooked_files, i);
|
|
|
|
free(entry->wpath);
|
|
free(entry->path);
|
|
|
|
if (entry->fd != NULL) {
|
|
CloseHandle(entry->fd);
|
|
}
|
|
}
|
|
|
|
array_fini(&hooked_files);
|
|
LeaveCriticalSection(&hooked_files_cs);
|
|
|
|
DeleteCriticalSection(&hooked_files_cs);
|
|
|
|
log_info("Finished");
|
|
}
|
|
|
|
void memfile_hook_add_fd(
|
|
const char *path, enum memfile_hook_path_mode path_mode, const void *data, uint32_t sz)
|
|
{
|
|
log_assert(path != NULL);
|
|
|
|
HRESULT hr;
|
|
struct file_entry *entry = NULL;
|
|
|
|
EnterCriticalSection(&hooked_files_cs);
|
|
entry = array_append(struct file_entry, &hooked_files);
|
|
|
|
entry->path = strdup(path);
|
|
entry->wpath = str_widen(path);
|
|
|
|
hr = iohook_open_nul_fd(&entry->fd);
|
|
|
|
if (hr != S_OK) {
|
|
LeaveCriticalSection(&hooked_files_cs);
|
|
log_fatal("Opening nul fd failed: %08lx", hr);
|
|
}
|
|
|
|
entry->pos = 0;
|
|
|
|
entry->data = data;
|
|
entry->sz = sz;
|
|
entry->path_mode = path_mode;
|
|
|
|
LeaveCriticalSection(&hooked_files_cs);
|
|
|
|
log_misc("memfile_hook_add_fd: path %s, mode %d, data size %d", path, path_mode, sz);
|
|
}
|
|
|
|
static struct file_entry *memfile_hook_match_irp(const struct irp *irp)
|
|
{
|
|
struct file_entry *entry;
|
|
log_assert(irp != NULL);
|
|
|
|
if (irp->op == IRP_OP_OPEN) {
|
|
for (size_t i = 0; i < hooked_files.nitems; i++) {
|
|
entry = array_item(struct file_entry, &hooked_files, i);
|
|
|
|
if (entry->path_mode == ABSOLUTE_MATCH) {
|
|
if (wstr_eq(entry->wpath, irp->open_filename)) {
|
|
log_misc("Memfile Open: Absolute matched: %s", entry->path);
|
|
return entry;
|
|
}
|
|
} else if (entry->path_mode == ENDING_MATCH) {
|
|
if (wstr_ends_with(irp->open_filename, entry->wpath)) {
|
|
log_misc("Memfile Open: Ending matched: %s", entry->path);
|
|
return entry;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (size_t i = 0; i < hooked_files.nitems; i++) {
|
|
entry = array_item(struct file_entry, &hooked_files, i);
|
|
|
|
if (entry->fd == irp->fd) {
|
|
return entry;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static HRESULT memfile_hook_irp_read(struct file_entry *entry, struct irp *irp)
|
|
{
|
|
log_assert(entry != NULL);
|
|
log_assert(irp != NULL);
|
|
|
|
size_t nread = min(entry->sz - entry->pos, irp->read.nbytes);
|
|
|
|
if (nread > 0) {
|
|
memcpy(irp->read.bytes + irp->read.pos, (uint8_t*)entry->data + entry->pos, nread);
|
|
|
|
entry->pos += nread;
|
|
irp->read.pos += nread;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT memfile_hook_irp_seek(struct file_entry *entry, struct irp *irp)
|
|
{
|
|
log_assert(entry != NULL);
|
|
log_assert(irp != NULL);
|
|
|
|
switch (irp->seek_origin) {
|
|
case FILE_BEGIN:
|
|
entry->pos = irp->seek_offset;
|
|
break;
|
|
case FILE_CURRENT:
|
|
entry->pos += irp->seek_offset;
|
|
break;
|
|
case FILE_END:
|
|
entry->pos = entry->sz + irp->seek_offset;
|
|
break;
|
|
default:
|
|
log_fatal("Invalid seek origin");
|
|
}
|
|
|
|
irp->seek_pos = entry->pos;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT
|
|
memfile_hook_irp_handler(struct file_entry *entry, struct irp *irp)
|
|
{
|
|
log_assert(entry != NULL);
|
|
log_assert(irp != NULL);
|
|
|
|
switch (irp->op) {
|
|
case IRP_OP_OPEN:
|
|
irp->fd = entry->fd;
|
|
entry->pos = 0;
|
|
return S_OK;
|
|
|
|
case IRP_OP_CLOSE:
|
|
return S_OK;
|
|
|
|
case IRP_OP_READ:
|
|
return memfile_hook_irp_read(entry, irp);
|
|
|
|
case IRP_OP_WRITE:
|
|
log_warning("write attempted on memfile, unsupported");
|
|
return S_OK;
|
|
|
|
case IRP_OP_IOCTL:
|
|
log_warning("ioctl attempted on memfile, unsupported");
|
|
return S_OK;
|
|
|
|
case IRP_OP_FSYNC:
|
|
log_warning("fsync attempted on memfile, unsupported");
|
|
return S_OK;
|
|
|
|
case IRP_OP_SEEK:
|
|
return memfile_hook_irp_seek(entry, irp);
|
|
|
|
default:
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
HRESULT memfile_hook_dispatch_irp(struct irp *irp)
|
|
{
|
|
log_assert(irp != NULL);
|
|
|
|
HRESULT hr;
|
|
|
|
EnterCriticalSection(&hooked_files_cs);
|
|
|
|
struct file_entry *entry = memfile_hook_match_irp(irp);
|
|
|
|
if (!entry) {
|
|
LeaveCriticalSection(&hooked_files_cs);
|
|
return iohook_invoke_next(irp);
|
|
}
|
|
|
|
hr = memfile_hook_irp_handler(entry, irp);
|
|
|
|
LeaveCriticalSection(&hooked_files_cs);
|
|
return hr;
|
|
}
|
|
|
|
// this isn't a standard iohook IRP atm.
|
|
BOOL my_GetFileInformationByHandle(
|
|
HANDLE hFile,
|
|
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
|
|
)
|
|
{
|
|
struct file_entry *entry;
|
|
for (size_t i = 0; i < hooked_files.nitems; i++) {
|
|
entry = array_item(struct file_entry, &hooked_files, i);
|
|
|
|
if (entry->fd == hFile) {
|
|
lpFileInformation->dwFileAttributes = FILE_ATTRIBUTE_READONLY;
|
|
lpFileInformation->nFileSizeHigh = 0;
|
|
lpFileInformation->nFileSizeLow = entry->sz;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return real_GetFileInformationByHandle(hFile, lpFileInformation);
|
|
}
|