Bemanitools v5.26 release

This commit is contained in:
icex2
2019-09-27 22:36:50 +02:00
commit cbd7720349
718 changed files with 62731 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
libs += hook
src_hook := \
com-proxy.c \
iohook.c \
pe.c \
peb.c \
table.c \
+145
View File
@@ -0,0 +1,145 @@
#include <windows.h>
#include <unknwn.h>
#include <stdlib.h>
#include "hook/com-proxy.h"
#include "util/defs.h"
#include "util/mem.h"
#ifdef _WIN64
/***** 64-BIT TRAMPOLINE *****/
#define SLOT_OFFSET 0x0A
static const uint8_t com_proxy_tramp[] = {
/* mov rcx, [rcx+8] ; Replace this with this->real */
0x48, 0x8B, 0x49, 0x08,
/* mov rax, [rcx] ; Get real->vtbl */
0x48, 0x8B, 0x01,
/* mov rax, [rax+XX] ; Get vtbl->slot_XX */
0x48, 0x8B, 0x80, -1, -1, -1, -1,
/* jmp rax ; Continue to slot_XX */
0xFF, 0xE0,
};
#else
/***** 32-BIT TRAMPOLINE *****/
#define SLOT_OFFSET 0x0F
static const uint8_t com_proxy_tramp[] = {
/* mov eax, [esp+4] ; Get this */
0x8B, 0x44, 0x24, 0x04,
/* mov eax, [eax+4] ; Get this->real */
0x8B, 0x40, 0x04,
/* mov [esp+4], eax ; Replace this with this->real on stack */
0x89, 0x44, 0x24, 0x04,
/* mov ecx, [eax] ; Get real->vtbl */
0x8B, 0x08,
/* mov ecx, [ecx+XX] ; Get vtbl->slot_XX */
0x8B, 0x89, -1, -1, -1, -1,
/* jmp ecx ; Continue to slot_XX */
0xFF, 0xE1
};
#endif
static HRESULT STDCALL com_proxy_query_interface(IUnknown *ptr, REFIID iid,
void **iface)
{
struct com_proxy *self = (struct com_proxy *) ptr;
IUnknown *obj = self->real; /* Not necessarily the real IUnknown* */
/* To some extent, COM is designed to support shennanigans like these.
We can safely pass the call straight through to the underlying
interface pointer because of the following:
"It is specifically not the case that queries for interfaces other
than IUnknown (even the same interface through the same pointer)
must return the same pointer value."
- MSDN documentation for IUnknown::QueryInterface()
Of course, pretty much everyone screws up COM's conventions (probably
including me in this very module to be honest), so if someone ends up
relying on broken assumptions then this could well get a lot more
complicated. */
return IUnknown_QueryInterface(obj, iid, iface);
}
static ULONG STDCALL com_proxy_addref(IUnknown *ptr)
{
struct com_proxy *self = (struct com_proxy *) ptr;
IUnknown *obj = self->real;
return IUnknown_AddRef(obj);
}
static ULONG STDCALL com_proxy_release(IUnknown *ptr)
{
struct com_proxy *self = (struct com_proxy *) ptr;
IUnknown *obj = self->real;
ULONG result;
result = IUnknown_Release(obj);
if (!result) {
/* Last ref to underlying object released */
VirtualFree(self->tramps, 0, MEM_RELEASE);
free(self->vptr);
free(self);
}
return result;
}
struct com_proxy *com_proxy_wrap(void *iface, size_t vtbl_size)
{
struct com_proxy *self;
void **vtbl;
uint8_t *cur_tramp;
uint32_t nslots;
uint32_t i;
nslots = vtbl_size / sizeof(void *);
self = xmalloc(sizeof(*self));
self->vptr = xmalloc(vtbl_size);
self->real = iface;
self->tramps = VirtualAlloc(NULL, sizeof(com_proxy_tramp) * nslots,
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
/* Set up proxied IUnknown impl */
vtbl = self->vptr;
vtbl[0] = com_proxy_query_interface;
vtbl[1] = com_proxy_addref;
vtbl[2] = com_proxy_release;
/* Populate trampoline code for remaining vtbl entries */
for (i = 3 /* Skip IUnknown */ ; i < nslots ; i++) {
cur_tramp = self->tramps + i * sizeof(com_proxy_tramp);
/* Copy template */
memcpy(cur_tramp, com_proxy_tramp, sizeof(com_proxy_tramp));
/* Patch XX into vtbl lookup (see definition of tramp) */
*((uint32_t *) (cur_tramp + SLOT_OFFSET)) = i * sizeof(void *);
/* Set vtable entry */
vtbl[i] = cur_tramp;
}
return self;
}
+54
View File
@@ -0,0 +1,54 @@
#ifndef HOOK_COM_PROXY_H
#define HOOK_COM_PROXY_H
#include <stddef.h>
#include <stdint.h>
/* N.B. Here be dragons. You really ought to be fairly familiar with COM
before using this, otherwise you risk ignoring the subtler issues at your
peril. */
#define COM_PROXY_UNWRAP(self) (((struct com_proxy *) self)->real)
struct com_proxy {
/* Pointer to vtable filled with trampolines. Edit these as you please.
Each com_proxy has its own independent vtable. */
void *vptr;
/* Interface pointer wrapped by this proxy. */
void *real;
/* Dynamically generated x86 trampoline code. The initial vtable entries
all point into code located here. */
uint8_t *tramps;
};
/* Wrap a COM interface pointer in a proxy. This is an object that acts just
like the object that it wraps, but has a freely editable vtable, which you
can modify in order to intercept a subset of the interface's method calls.
By default, all the vtable slots contain dynamically generated trampolines
which pass method calls onwards to the corresponding methods in the
underlying object's vtable.
NOTE! This does not AddRef the underlying interface.
NOTE! This function wraps COM POINTERS, not COM OBJECTS (since the latter
is, in general, impossible). If you're insufficiently versed in COM to
understand the difference... well, you really should be, but the following
observations are a start:
1. Do not wrap IUnknown pointers with this function. This will break the
IUnknown::QueryInterface contract. This refers to _the_ unique
IUnknown* for the object, not for any other interface (which necessarily
extends IUnknown). Wrapping the unique IUnknown* for an object will
cause it to no longer be unique.
2. Callers can "jailbreak" your wrapper using IUnknown::QueryInterface.
Generally this isn't an issue for DirectX objects, since nobody ever seems
to use QueryInterface with them. */
struct com_proxy *com_proxy_wrap(void *iface, size_t vtbl_size);
#endif
+816
View File
@@ -0,0 +1,816 @@
#define LOG_MODULE "iohook"
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include "hook/iohook.h"
#include "hook/table.h"
#include "util/hr.h"
#include "util/log.h"
#include "util/str.h"
/* Helpers */
static BOOL iohook_overlapped_result(
uint32_t *syncout,
OVERLAPPED *ovl,
uint32_t value);
static HRESULT irp_invoke_real(struct irp *irp);
static HRESULT irp_invoke_real_open(struct irp *irp);
static HRESULT irp_invoke_real_close(struct irp *irp);
static HRESULT irp_invoke_real_read(struct irp *irp);
static HRESULT irp_invoke_real_write(struct irp *irp);
static HRESULT irp_invoke_real_seek(struct irp *irp);
static HRESULT irp_invoke_real_fsync(struct irp *irp);
static HRESULT irp_invoke_real_ioctl(struct irp *irp);
/* API hooks */
static BOOL STDCALL my_CloseHandle(HANDLE fd);
static HANDLE STDCALL my_CreateFileW(
const wchar_t *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static HANDLE STDCALL my_CreateFileA(
const char *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static BOOL STDCALL my_ReadFile(
HANDLE hFile,
void *lpBuffer,
uint32_t nNumberOfBytesToRead,
uint32_t *lpNumberOfBytesRead,
OVERLAPPED *lpOverlapped);
static BOOL STDCALL my_WriteFile(
HANDLE hFile,
const void *lpBuffer,
uint32_t nNumberOfBytesToWrite,
uint32_t *lpNumberOfBytesWritten,
OVERLAPPED *lpOverlapped);
static DWORD STDCALL my_SetFilePointer(
HANDLE hFile,
int32_t lDistanceToMove,
int32_t *lpDistanceToMoveHigh,
uint32_t dwMoveMethod);
static BOOL STDCALL my_FlushFileBuffers(HANDLE hFile);
static BOOL STDCALL my_DeviceIoControl(
HANDLE hFile,
uint32_t dwIoControlCode,
void *lpInBuffer,
uint32_t nInBufferSize,
void *lpOutBuffer,
uint32_t nOutBufferSize,
uint32_t *lpBytesReturned,
OVERLAPPED *lpOverlapped);
/* Links */
static BOOL (STDCALL *real_CloseHandle)(HANDLE fd);
static HANDLE (STDCALL *real_CreateFileW)(
const wchar_t *filename,
uint32_t access,
uint32_t share,
SECURITY_ATTRIBUTES *sa,
uint32_t creation,
uint32_t flags,
HANDLE tmpl);
static BOOL (STDCALL *real_DeviceIoControl)(
HANDLE fd,
uint32_t code,
void *in_bytes,
uint32_t in_nbytes,
void *out_bytes,
uint32_t out_nbytes,
uint32_t *out_returned,
OVERLAPPED *ovl);
static BOOL (STDCALL *real_ReadFile)(
HANDLE fd,
void *buf,
uint32_t nbytes,
uint32_t *nread,
OVERLAPPED *ovl);
static BOOL (STDCALL *real_WriteFile)(
HANDLE fd,
const void *buf,
uint32_t nbytes,
uint32_t *nwrit,
OVERLAPPED *ovl);
static DWORD (STDCALL *real_SetFilePointer)(
HANDLE hFile,
int32_t lDistanceToMove,
int32_t *lpDistanceToMoveHigh,
uint32_t dwMoveMethod);
static BOOL (STDCALL *real_FlushFileBuffers)(HANDLE fd);
/* Hook table */
static struct hook_symbol iohook_kernel32_syms[] = {
/* Basic IO */
{
.name = "CloseHandle",
.patch = my_CloseHandle,
.link = (void *) &real_CloseHandle,
},
{
.name = "CreateFileA",
.patch = my_CreateFileA,
},
{
.name = "CreateFileW",
.patch = my_CreateFileW,
.link = (void *) &real_CreateFileW,
},
{
.name = "DeviceIoControl",
.patch = my_DeviceIoControl,
.link = (void *) &real_DeviceIoControl,
},
{
.name = "ReadFile",
.patch = my_ReadFile,
.link = (void *) &real_ReadFile,
},
{
.name = "WriteFile",
.patch = my_WriteFile,
.link = (void *) &real_WriteFile,
},
{
.name = "SetFilePointer",
.patch = my_SetFilePointer,
.link = (void *) &real_SetFilePointer,
},
{
.name = "FlushFileBuffers",
.patch = my_FlushFileBuffers,
.link = (void *) &real_FlushFileBuffers,
},
};
static const irp_handler_t irp_real_handlers[] = {
[IRP_OP_OPEN] = irp_invoke_real_open,
[IRP_OP_CLOSE] = irp_invoke_real_close,
[IRP_OP_READ] = irp_invoke_real_read,
[IRP_OP_WRITE] = irp_invoke_real_write,
[IRP_OP_SEEK] = irp_invoke_real_seek,
[IRP_OP_FSYNC] = irp_invoke_real_fsync,
[IRP_OP_IOCTL] = irp_invoke_real_ioctl,
};
static const irp_handler_t *iohook_handlers;
static size_t iohook_nhandlers;
void iohook_init(const irp_handler_t *handlers, size_t nhandlers)
{
log_assert(handlers != NULL);
log_assert(iohook_handlers == NULL);
iohook_handlers = handlers;
iohook_nhandlers = nhandlers;
hook_table_apply(
NULL,
"kernel32.dll",
iohook_kernel32_syms,
lengthof(iohook_kernel32_syms));
if (real_CreateFileW == NULL) {
/* my_CreateFileA requires this to be present */
real_CreateFileW = (void *) GetProcAddress(
GetModuleHandleA("kernel32.dll"),
"CreateFileW");
}
log_info("IO Hook subsystem initialized");
}
HANDLE iohook_open_dummy_fd(void)
{
HANDLE fd;
fd = real_CreateFileW(
L"NUL",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (fd == INVALID_HANDLE_VALUE) {
log_fatal("Failed to open dummy fd: %08x", (uint32_t) GetLastError());
}
return fd;
}
static BOOL iohook_overlapped_result(
uint32_t *syncout,
OVERLAPPED *ovl,
uint32_t value)
{
if (ovl != NULL) {
ovl->Internal = 0; // (NTSTATUS) STATUS_SUCCESS
ovl->InternalHigh = value;
if (ovl->hEvent != NULL) {
SetEvent(ovl->hEvent);
}
}
if (syncout != NULL) {
*syncout = value;
SetLastError(ERROR_SUCCESS);
return TRUE;
} else {
SetLastError(ERROR_IO_PENDING);
return FALSE;
}
}
HRESULT irp_invoke_next(struct irp *irp)
{
irp_handler_t handler;
HRESULT hr;
log_assert(irp != NULL);
log_assert(irp->next_handler <= iohook_nhandlers);
if (irp->next_handler < iohook_nhandlers) {
handler = iohook_handlers[irp->next_handler++];
hr = handler(irp);
if (FAILED(hr)) {
irp->next_handler = (size_t) -1;
}
} else {
irp->next_handler = (size_t) -1;
hr = irp_invoke_real(irp);
}
return hr;
}
static HRESULT irp_invoke_real(struct irp *irp)
{
irp_handler_t handler;
log_assert(irp != NULL);
log_assert(irp->op < lengthof(irp_real_handlers));
handler = irp_real_handlers[irp->op];
log_assert(handler != NULL);
return handler(irp);
}
static HRESULT irp_invoke_real_open(struct irp *irp)
{
HANDLE fd;
log_assert(irp != NULL);
fd = real_CreateFileW(
irp->open_filename,
irp->open_access,
irp->open_share,
irp->open_sa,
irp->open_creation,
irp->open_flags,
irp->open_tmpl);
if (fd == INVALID_HANDLE_VALUE) {
return hr_from_win32();
}
irp->fd = fd;
return S_OK;
}
static HRESULT irp_invoke_real_close(struct irp *irp)
{
BOOL ok;
log_assert(irp != NULL);
ok = real_CloseHandle(irp->fd);
if (!ok) {
return hr_from_win32();
}
return S_OK;
}
static HRESULT irp_invoke_real_read(struct irp *irp)
{
uint32_t nread;
BOOL ok;
log_assert(irp != NULL);
ok = real_ReadFile(
irp->fd,
&irp->read.bytes[irp->read.pos],
irp->read.nbytes - irp->read.pos,
&nread,
irp->ovl);
if (!ok) {
return hr_from_win32();
}
irp->read.pos += nread;
return S_OK;
}
static HRESULT irp_invoke_real_write(struct irp *irp)
{
uint32_t nwrit;
BOOL ok;
log_assert(irp != NULL);
ok = real_WriteFile(
irp->fd,
&irp->write.bytes[irp->write.pos],
irp->write.nbytes - irp->write.pos,
&nwrit,
irp->ovl);
if (!ok) {
return hr_from_win32();
}
irp->write.pos += nwrit;
return S_OK;
}
static HRESULT irp_invoke_real_seek(struct irp *irp)
{
int32_t hi;
int32_t lo;
HRESULT hr;
log_assert(irp != NULL);
hi = (uint32_t) (irp->seek_offset >> 32);
lo = (uint32_t) (irp->seek_offset );
lo = real_SetFilePointer(irp->fd, (int32_t) lo, hi == 0 ? NULL : &hi,
irp->seek_origin);
if (lo == INVALID_SET_FILE_POINTER) {
hr = hr_from_win32();
if (FAILED(hr)) {
return hr;
}
}
irp->seek_pos = (((uint64_t) hi) << 32) | ((uint32_t) lo);
return S_OK;
}
static HRESULT irp_invoke_real_fsync(struct irp *irp)
{
BOOL ok;
log_assert(irp != NULL);
ok = real_FlushFileBuffers(irp->fd);
if (!ok) {
return hr_from_win32();
}
return S_OK;
}
static HRESULT irp_invoke_real_ioctl(struct irp *irp)
{
uint32_t nread;
BOOL ok;
log_assert(irp != NULL);
/* ioctl in/out params tend to be structs, so it probably does not make
sense to concatenate a synthetic result with a pass-through result in
the same way as one might do with read/write. */
log_assert(irp->write.pos == 0);
log_assert(irp->read.pos == 0);
ok = real_DeviceIoControl(
irp->fd,
irp->ioctl,
(void *) irp->write.bytes, // Cast off const
irp->write.nbytes,
irp->read.bytes,
irp->read.nbytes,
&nread,
irp->ovl);
if (!ok) {
return hr_from_win32();
}
irp->read.pos = nread;
return S_OK;
}
static HANDLE STDCALL my_CreateFileA(
const char *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
wchar_t *wfilename;
HANDLE fd;
if (lpFileName == NULL) {
log_warning("%s: lpFileName == NULL", __func__);
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
wfilename = str_widen(lpFileName);
fd = my_CreateFileW(
wfilename,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes,
hTemplateFile);
free(wfilename);
return fd;
}
static HANDLE STDCALL my_CreateFileW(
const wchar_t *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
struct irp irp;
HRESULT hr;
if (lpFileName == NULL) {
log_warning("%s: lpFileName == NULL", __func__);
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
memset(&irp, 0, sizeof(irp));
irp.op = IRP_OP_OPEN;
irp.fd = INVALID_HANDLE_VALUE;
irp.open_filename = lpFileName;
irp.open_access = dwDesiredAccess;
irp.open_share = dwShareMode;
irp.open_sa = lpSecurityAttributes;
irp.open_creation = dwCreationDisposition;
irp.open_flags = dwFlagsAndAttributes;
irp.open_tmpl = hTemplateFile;
hr = irp_invoke_next(&irp);
if (FAILED(hr)) {
return hr_propagate_win32(hr, INVALID_HANDLE_VALUE);
}
SetLastError(ERROR_SUCCESS);
return irp.fd;
}
static BOOL STDCALL my_CloseHandle(HANDLE hFile)
{
struct irp irp;
HRESULT hr;
if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
log_warning("%s: Invalid file descriptor %p", __func__, hFile);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
memset(&irp, 0, sizeof(irp));
irp.op = IRP_OP_CLOSE;
irp.fd = hFile;
hr = irp_invoke_next(&irp);
if (FAILED(hr)) {
return hr_propagate_win32(hr, FALSE);
}
SetLastError(ERROR_SUCCESS);
return TRUE;
}
static BOOL STDCALL my_ReadFile(
HANDLE hFile,
void *lpBuffer,
uint32_t nNumberOfBytesToRead,
uint32_t *lpNumberOfBytesRead,
OVERLAPPED *lpOverlapped)
{
struct irp irp;
HRESULT hr;
if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
log_warning("%s: Invalid file descriptor %p", __func__, hFile);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (lpBuffer == NULL) {
log_warning("%s: lpBuffer == NULL", __func__);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (lpOverlapped == NULL) {
if (lpNumberOfBytesRead == NULL) {
log_warning( "%s: lpNumberOfBytesRead must be supplied in "
"synchronous mode",
__func__);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
*lpNumberOfBytesRead = 0;
}
memset(&irp, 0, sizeof(irp));
irp.op = IRP_OP_READ;
irp.fd = hFile;
irp.ovl = lpOverlapped;
irp.read.bytes = lpBuffer;
irp.read.nbytes = nNumberOfBytesToRead;
irp.read.pos = 0;
hr = irp_invoke_next(&irp);
if (FAILED(hr)) {
return hr_propagate_win32(hr, FALSE);
}
log_assert(irp.read.pos <= irp.read.nbytes);
return iohook_overlapped_result(
lpNumberOfBytesRead,
lpOverlapped,
irp.read.pos);
}
static BOOL STDCALL my_WriteFile(
HANDLE hFile,
const void *lpBuffer,
uint32_t nNumberOfBytesToWrite,
uint32_t *lpNumberOfBytesWritten,
OVERLAPPED *lpOverlapped)
{
struct irp irp;
HRESULT hr;
if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
/* Don't log this because iidx14 calls WriteFile with a NULL handle */
// log_warning("%s: Invalid file descriptor %p", __func__, hFile);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (lpBuffer == NULL) {
log_warning("%s: lpBuffer == NULL", __func__);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (lpOverlapped == NULL) {
if (lpNumberOfBytesWritten == NULL) {
log_warning( "%s: lpNumberOfBytesWritten must be supplied in "
"synchronous mode",
__func__);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
*lpNumberOfBytesWritten = 0;
}
memset(&irp, 0, sizeof(irp));
irp.op = IRP_OP_WRITE;
irp.fd = hFile;
irp.ovl = lpOverlapped;
irp.write.bytes = lpBuffer;
irp.write.nbytes = nNumberOfBytesToWrite;
irp.write.pos = 0;
hr = irp_invoke_next(&irp);
if (FAILED(hr)) {
return hr_propagate_win32(hr, FALSE);
}
log_assert(irp.write.pos <= irp.write.nbytes);
return iohook_overlapped_result(
lpNumberOfBytesWritten,
lpOverlapped,
irp.write.pos);
}
static DWORD STDCALL my_SetFilePointer(
HANDLE hFile,
int32_t lDistanceToMove,
int32_t *lpDistanceToMoveHigh,
uint32_t dwMoveMethod)
{
struct irp irp;
HRESULT hr;
if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
log_warning("%s: Invalid file descriptor %p", __func__, hFile);
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_SET_FILE_POINTER;
}
memset(&irp, 0, sizeof(irp));
irp.op = IRP_OP_SEEK;
irp.fd = hFile;
irp.seek_origin = dwMoveMethod;
/* This is a clumsy API. In 32-bit mode lDistanceToMove is a signed 32-bit
int, but in 64-bit mode it is a 32-bit UNsigned int. Care must be taken
with sign-extension vs zero-extension here. */
if (lpDistanceToMoveHigh != NULL) {
irp.seek_offset = ((( int64_t) *lpDistanceToMoveHigh) << 32)
| ((uint64_t) lDistanceToMove);
} else {
irp.seek_offset = (int64_t) lDistanceToMove;
}
hr = irp_invoke_next(&irp);
if (FAILED(hr)) {
return hr_propagate_win32(hr, INVALID_SET_FILE_POINTER);
}
SetLastError(ERROR_SUCCESS);
if (lpDistanceToMoveHigh != NULL) {
*lpDistanceToMoveHigh = irp.seek_pos >> 32;
}
return (DWORD) irp.seek_pos;
}
static BOOL STDCALL my_FlushFileBuffers(HANDLE hFile)
{
struct irp irp;
HRESULT hr;
/* Some of the old games using acio (e.g. DistorteD) are calling
FlushFileBuffers. If that call is not hooked, the game will write a bunch
of 0 data to the device and stop doing any read/write calls after that */
if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
log_warning("%s: Invalid file descriptor %p", __func__, hFile);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
memset(&irp, 0, sizeof(irp));
irp.op = IRP_OP_FSYNC;
irp.fd = hFile;
hr = irp_invoke_next(&irp);
if (FAILED(hr)) {
return hr_propagate_win32(hr, FALSE);
}
SetLastError(ERROR_SUCCESS);
return TRUE;
}
static BOOL STDCALL my_DeviceIoControl(
HANDLE hFile,
uint32_t dwIoControlCode,
void *lpInBuffer,
uint32_t nInBufferSize,
void *lpOutBuffer,
uint32_t nOutBufferSize,
uint32_t *lpBytesReturned,
OVERLAPPED *lpOverlapped)
{
struct irp irp;
HRESULT hr;
if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
log_warning("%s: Invalid file descriptor %p", __func__, hFile);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (lpOverlapped == NULL) {
if (lpBytesReturned == NULL) {
log_warning(
"%s: lpBytesReturned must be supplied in synchronous mode",
__func__);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
*lpBytesReturned = 0;
}
memset(&irp, 0, sizeof(irp));
irp.op = IRP_OP_IOCTL;
irp.fd = hFile;
irp.ovl = lpOverlapped;
irp.ioctl = dwIoControlCode;
if (lpInBuffer != NULL) {
irp.write.bytes = lpInBuffer;
irp.write.nbytes = nInBufferSize;
}
if (lpOutBuffer != NULL) {
irp.read.bytes = lpOutBuffer;
irp.read.nbytes = nOutBufferSize;
}
hr = irp_invoke_next(&irp);
if (FAILED(hr)) {
return hr_propagate_win32(hr, FALSE);
}
log_assert(irp.write.pos <= irp.write.nbytes);
log_assert(irp.read.pos <= irp.read.nbytes);
return iohook_overlapped_result(
lpBytesReturned,
lpOverlapped,
irp.read.pos);
}
+47
View File
@@ -0,0 +1,47 @@
#ifndef HOOK_IOHOOK_H
#define HOOK_IOHOOK_H
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "util/iobuf.h"
enum irp_op {
IRP_OP_OPEN,
IRP_OP_CLOSE,
IRP_OP_READ,
IRP_OP_WRITE,
IRP_OP_IOCTL,
IRP_OP_FSYNC,
IRP_OP_SEEK,
};
struct irp {
enum irp_op op;
size_t next_handler;
HANDLE fd;
OVERLAPPED *ovl;
struct const_iobuf write;
struct iobuf read;
uint32_t ioctl;
const wchar_t *open_filename;
uint32_t open_access;
uint32_t open_share;
SECURITY_ATTRIBUTES *open_sa;
uint32_t open_creation;
uint32_t open_flags;
HANDLE *open_tmpl;
uint32_t seek_origin;
int64_t seek_offset;
uint64_t seek_pos;
};
typedef HRESULT (*irp_handler_t)(struct irp *irp);
void iohook_init(const irp_handler_t *handlers, size_t nhandlers);
HANDLE iohook_open_dummy_fd(void);
HRESULT irp_invoke_next(struct irp *irp);
#endif
+286
View File
@@ -0,0 +1,286 @@
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/pe.h"
#include "util/log.h"
typedef BOOL (WINAPI *dll_main_t)(HMODULE, uint32_t, void *);
static const IMAGE_NT_HEADERS *pe_get_nt_header(HMODULE pe);
static uint32_t pe_get_virtual_size(const IMAGE_SECTION_HEADER *sh,
int nsections);
static void *pe_offset(void *ptr, size_t off);
static const void *pe_offsetc(const void *ptr, size_t off);
static void *pe_offset(void *ptr, size_t off)
{
uint8_t *base;
if (off == 0) {
return NULL;
}
base = ptr;
return base + off;
}
static const void *pe_offsetc(const void *ptr, size_t off)
{
const uint8_t *base;
if (off == 0) {
return NULL;
}
base = ptr;
return base + off;
}
static uint32_t pe_get_virtual_size(const IMAGE_SECTION_HEADER *sh,
int nsections)
{
uint32_t sec_end;
uint32_t size;
int i;
size = 0;
for (i = 0 ; i < nsections ; i++) {
sec_end = sh[i].VirtualAddress + sh[i].Misc.VirtualSize;
if (size < sec_end) {
size = sec_end;
}
}
return size;
}
static const IMAGE_NT_HEADERS *pe_get_nt_header(HMODULE pe)
{
const IMAGE_DOS_HEADER *dh;
const IMAGE_NT_HEADERS *nth;
dh = (IMAGE_DOS_HEADER *) pe;
nth = pe_offsetc(pe, dh->e_lfanew);
return nth;
}
const pe_iid_t *pe_iid_get_first(HMODULE pe)
{
const IMAGE_NT_HEADERS *nth;
const IMAGE_IMPORT_DESCRIPTOR *iid;
nth = pe_get_nt_header(pe);
iid = pe_offsetc(pe, nth->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if (iid == NULL || iid->Name == 0) {
return NULL;
}
return iid;
}
const char *pe_iid_get_name(HMODULE pe, const pe_iid_t *iid)
{
return pe_offsetc(pe, iid->Name);
}
const pe_iid_t *pe_iid_get_next(HMODULE pe, const pe_iid_t *iid)
{
const IMAGE_IMPORT_DESCRIPTOR *iid_next;
iid_next = iid + 1;
if (iid_next->Name != 0) {
return iid_next;
} else {
return NULL;
}
}
bool pe_iid_get_iat_entry(HMODULE pe, const pe_iid_t *iid, size_t n,
struct pe_iat_entry *entry)
{
const IMAGE_IMPORT_BY_NAME *import;
uintptr_t *import_rvas;
void **pointers;
if (iid->OriginalFirstThunk == 0) {
log_warning("OriginalFirstThunk == 0");
}
import_rvas = pe_offset(pe, iid->OriginalFirstThunk);
if (import_rvas[n] == 0) {
/* End of imports */
entry->name = NULL;
entry->ordinal = 0;
entry->ppointer = NULL;
return false;
} else if (import_rvas[n] & INTPTR_MIN) {
/* Ordinal import */
entry->name = NULL;
entry->ordinal = (uint16_t) import_rvas[n];
} else {
/* Named import */
import = pe_offsetc(pe, import_rvas[n]);
entry->name = (const char *) import->Name; /* Not an RVA */
entry->ordinal = 0;
}
pointers = pe_offset(pe, iid->FirstThunk);
entry->ppointer = &pointers[n];
return true;
}
void pe_patch_pointer(void **ppointer, void *new_value)
{
DWORD old_protect;
VirtualProtect(ppointer, sizeof(void*), PAGE_EXECUTE_READWRITE, &old_protect);
*ppointer = new_value;
VirtualProtect(ppointer, sizeof(void*), old_protect, &old_protect);
}
HMODULE pe_explode(const uint8_t *bytes, uint32_t nbytes)
{
HMODULE base;
const IMAGE_DOS_HEADER *dh;
const IMAGE_NT_HEADERS *nth;
const IMAGE_SECTION_HEADER *sh;
uint32_t virtual_size;
uint32_t vflags;
int i;
dh = (IMAGE_DOS_HEADER *) bytes;
nth = pe_offsetc(bytes, dh->e_lfanew);
sh = pe_offsetc(bytes, dh->e_lfanew + sizeof(*nth));
virtual_size = pe_get_virtual_size(sh, nth->FileHeader.NumberOfSections);
base = (HMODULE) VirtualAlloc((void *) nth->OptionalHeader.ImageBase,
virtual_size, MEM_RESERVE, PAGE_NOACCESS);
if (base == NULL) {
/* Try again, allowing any base address */
base = (HMODULE) VirtualAlloc(NULL, virtual_size, MEM_RESERVE,
PAGE_NOACCESS);
if (base == NULL) {
/* Aargh */
log_fatal("Failed to VirtualAlloc %#x bytes of address space",
virtual_size);
}
}
log_misc("Exploding PE, base %p actual %p",
(void *) nth->OptionalHeader.ImageBase,
base);
/* Commit header region */
VirtualAlloc((void *) base, nth->OptionalHeader.SizeOfHeaders, MEM_COMMIT,
PAGE_READWRITE);
memcpy(base, dh, sizeof(*dh));
memcpy(pe_offset(base, dh->e_lfanew), nth, sizeof(*nth));
memcpy(pe_offset(base, dh->e_lfanew + sizeof(*nth)), sh,
sizeof(*sh) * nth->FileHeader.NumberOfSections);
for (i = 0 ; i < nth->FileHeader.NumberOfSections ; i++) {
vflags = sh[i].Characteristics & 0x20000000
? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
VirtualAlloc(
pe_offset(base, sh[i].VirtualAddress),
sh[i].Misc.VirtualSize,
MEM_COMMIT,
vflags);
memcpy( pe_offset(base, sh[i].VirtualAddress),
pe_offsetc(bytes, sh[i].PointerToRawData),
sh[i].SizeOfRawData);
}
return base;
}
void pe_relocate(HMODULE pe)
{
const IMAGE_NT_HEADERS *nth;
const IMAGE_DATA_DIRECTORY *dde;
const IMAGE_BASE_RELOCATION *chunk;
intptr_t delta_va;
const uint16_t *reloc;
uintptr_t *addr_ptr;
nth = pe_get_nt_header(pe);
delta_va = (intptr_t) pe - nth->OptionalHeader.ImageBase;
dde = nth->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_BASERELOC;
for (chunk = pe_offsetc(pe, dde->VirtualAddress)
; (void *) chunk < pe_offsetc(pe, dde->VirtualAddress + dde->Size)
; chunk = pe_offsetc(chunk, chunk->SizeOfBlock)) {
for (reloc = (uint16_t *) (chunk + 1)
; (void *) reloc < pe_offsetc(chunk, chunk->SizeOfBlock)
; reloc++) {
if (*reloc >> 12 == IMAGE_REL_BASED_HIGHLOW) {
addr_ptr = pe_offset(
pe,
chunk->VirtualAddress + (*reloc & 0x0FFF));
*addr_ptr += delta_va;
}
}
}
}
void *pe_get_export(HMODULE pe, const char *name, uint16_t ord)
{
const IMAGE_NT_HEADERS *nth;
const IMAGE_EXPORT_DIRECTORY *ied;
const uint32_t *name_rvas;
const uint32_t *target_rvas;
DWORD i;
nth = pe_get_nt_header(pe);
ied = pe_offsetc(pe, nth->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
name_rvas = pe_offsetc(pe, ied->AddressOfNames);
target_rvas = pe_offsetc(pe, ied->AddressOfFunctions);
if (name != NULL) {
for (i = 0 ; i < ied->NumberOfNames ; i++) {
if (name_rvas[i] != 0
&& strcmp(pe_offsetc(pe, name_rvas[i]), name) == 0) {
return pe_offset(pe, target_rvas[i]);
}
}
return NULL;
} else if (ord - ied->Base < ied->NumberOfFunctions) {
return pe_offset(pe, target_rvas[ord - ied->Base]);
} else {
return NULL;
}
}
BOOL pe_call_dll_main(HMODULE pe, uint32_t reason, void *ctx)
{
const IMAGE_NT_HEADERS *nth;
dll_main_t dll_main;
nth = pe_get_nt_header(pe);
dll_main = pe_offset(pe, nth->OptionalHeader.AddressOfEntryPoint);
return dll_main(pe, reason, ctx);
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef HOOK_PE_H
#define HOOK_PE_H
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
typedef IMAGE_IMPORT_DESCRIPTOR pe_iid_t;
struct pe_iat_entry {
const char *name;
uint16_t ordinal;
void **ppointer;
};
const pe_iid_t *pe_iid_get_first(HMODULE pe);
const char *pe_iid_get_name(HMODULE pe, const pe_iid_t *iid);
const pe_iid_t *pe_iid_get_next(HMODULE pe, const pe_iid_t *iid);
bool pe_iid_get_iat_entry(HMODULE pe, const pe_iid_t *iid, size_t n,
struct pe_iat_entry *entry);
void *pe_get_export(HMODULE pe, const char *name, uint16_t ord);
BOOL pe_call_dll_main(HMODULE pe, uint32_t reason, void *ctx);
void pe_patch_pointer(void **ppointer, void *new_value);
HMODULE pe_explode(const uint8_t *bytes, uint32_t nbytes);
void pe_relocate(HMODULE pe);
#endif
+68
View File
@@ -0,0 +1,68 @@
#include <windows.h>
#include <winternl.h>
#include "hook/peb.h"
#include "util/defs.h"
#include "util/str.h"
static const PEB *peb_get(void)
{
#ifdef __amd64
return (const PEB *) __readgsqword(0x60);
#else
return (const PEB *) __readfsdword(0x30);
#endif
}
const peb_dll_t *peb_dll_get_first(void)
{
const PEB *peb;
const LIST_ENTRY *node;
peb = peb_get();
node = peb->Ldr->InMemoryOrderModuleList.Flink;
return containerof(node, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
}
const peb_dll_t *peb_dll_get_next(const peb_dll_t *dll)
{
const PEB *peb;
const LIST_ENTRY *node;
peb = peb_get();
node = dll->InMemoryOrderLinks.Flink;
if (node == peb->Ldr->InMemoryOrderModuleList.Flink) {
return NULL;
}
return containerof(node, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
}
HMODULE peb_dll_get_base(const peb_dll_t *dll)
{
return dll->DllBase;
}
char *peb_dll_dup_name(const peb_dll_t *dll)
{
const UNICODE_STRING *wstr;
char *name;
size_t i;
wstr = &dll->FullDllName;
for (i = wstr->Length / 2 - 1 ; i > 0 ; i--) {
if (wstr->Buffer[i] == L'\\') {
wstr_narrow(&wstr->Buffer[i + 1], &name);
return name;
}
}
return NULL;
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef HOOK_PEB_H
#define HOOK_PEB_H
#include <windows.h>
#include <winternl.h>
typedef LDR_DATA_TABLE_ENTRY peb_dll_t;
const peb_dll_t *peb_dll_get_first(void);
const peb_dll_t *peb_dll_get_next(const peb_dll_t *dll);
HMODULE peb_dll_get_base(const peb_dll_t *dll);
char *peb_dll_dup_name(const peb_dll_t *dll);
#endif
+121
View File
@@ -0,0 +1,121 @@
#include <windows.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "hook/pe.h"
#include "hook/peb.h"
#include "hook/table.h"
#include "util/mem.h"
static void hook_table_apply_to_all(
const char *depname,
const struct hook_symbol *syms,
size_t nsyms);
static void hook_table_apply_to_iid(
HMODULE target,
const pe_iid_t *iid,
const struct hook_symbol *syms,
size_t nsyms);
static bool hook_table_match_proc(
const struct pe_iat_entry *iate,
const struct hook_symbol *sym);
static void hook_table_apply_to_all(
const char *depname,
const struct hook_symbol *syms,
size_t nsyms)
{
const peb_dll_t *dll;
HMODULE pe;
for (dll = peb_dll_get_first()
; dll != NULL
; dll = peb_dll_get_next(dll)) {
pe = peb_dll_get_base(dll);
if (pe == NULL) {
/* wtf? */
continue;
}
hook_table_apply(pe, depname, syms, nsyms);
}
}
void hook_table_apply(
HMODULE target,
const char *depname,
const struct hook_symbol *syms,
size_t nsyms)
{
const pe_iid_t *iid;
const char *iid_name;
if (target == NULL) {
/* Call out, which will then call us back repeatedly. Awkward, but
viewed from the outside it's good for usability. */
hook_table_apply_to_all(depname, syms, nsyms);
} else {
for ( iid = pe_iid_get_first(target) ;
iid != NULL ;
iid = pe_iid_get_next(target, iid)) {
iid_name = pe_iid_get_name(target, iid);
if (_stricmp(iid_name, depname) == 0) {
hook_table_apply_to_iid(target, iid, syms, nsyms);
}
}
}
}
static void hook_table_apply_to_iid(
HMODULE target,
const pe_iid_t *iid,
const struct hook_symbol *syms,
size_t nsyms)
{
struct pe_iat_entry iate;
size_t i;
size_t j;
const struct hook_symbol *sym;
i = 0;
while (pe_iid_get_iat_entry(target, iid, i++, &iate)) {
for (j = 0 ; j < nsyms ; j++) {
sym = &syms[j];
if (hook_table_match_proc(&iate, sym)) {
if (sym->link != NULL && *sym->link == NULL) {
*sym->link = *iate.ppointer;
}
pe_patch_pointer(iate.ppointer, sym->patch);
}
}
}
}
static bool hook_table_match_proc(
const struct pe_iat_entry *iate,
const struct hook_symbol *sym)
{
if ( sym->name != NULL &&
iate->name != NULL &&
strcmp(sym->name, iate->name) == 0) {
return true;
}
if (sym->ordinal != 0 && sym->ordinal == iate->ordinal) {
return true;
}
return false;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef HOOK_TABLE_H
#define HOOK_TABLE_H
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
struct hook_symbol {
const char *name;
uint16_t ordinal;
void *patch;
void **link;
};
void hook_table_apply(
HMODULE target,
const char *depname,
const struct hook_symbol *syms,
size_t nsyms);
#endif