#include "hooklib/memfile.h" #include #include #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); }