forked from TeamTofuShop/segatools
hooklib: Split out of util
This is distinct from capnhook's hooklib, although the intent is the same. Hopefully these two libraries sharing a name won't get too confusing...
This commit is contained in:
213
hooklib/clock.c
Normal file
213
hooklib/clock.c
Normal file
@ -0,0 +1,213 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static void WINAPI my_GetSystemTimeAsFileTime(FILETIME *out);
|
||||
static BOOL WINAPI my_GetLocalTime(SYSTEMTIME *out);
|
||||
static BOOL WINAPI my_GetSystemTime(SYSTEMTIME *out);
|
||||
static DWORD WINAPI my_GetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo);
|
||||
static BOOL WINAPI my_SetLocalTime(SYSTEMTIME *in);
|
||||
static BOOL WINAPI my_SetSystemTime(SYSTEMTIME *in);
|
||||
static BOOL WINAPI my_SetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo);
|
||||
|
||||
static BOOL (WINAPI * next_GetSystemTimeAsFileTime)(FILETIME *out);
|
||||
static int64_t clock_current_day;
|
||||
|
||||
static const struct hook_symbol clock_hook_syms[] = {
|
||||
{
|
||||
.name = "GetSystemTimeAsFileTime",
|
||||
.patch = my_GetSystemTimeAsFileTime,
|
||||
.link = (void **) &next_GetSystemTimeAsFileTime,
|
||||
}, {
|
||||
.name = "GetLocalTime",
|
||||
.patch = my_GetLocalTime,
|
||||
}, {
|
||||
.name = "GetSystemTime",
|
||||
.patch = my_GetSystemTime,
|
||||
}, {
|
||||
.name = "GetTimeZoneInformation",
|
||||
.patch = my_GetTimeZoneInformation,
|
||||
}, {
|
||||
.name = "SetLocalTime",
|
||||
.patch = my_SetLocalTime,
|
||||
}, {
|
||||
.name = "SetSystemTime",
|
||||
.patch = my_SetSystemTime,
|
||||
}, {
|
||||
.name = "SetTimeZoneInformation",
|
||||
.patch = my_SetTimeZoneInformation,
|
||||
},
|
||||
};
|
||||
|
||||
/* FILETIME is expressed in 100ns i.e. 0.1us i.e. 10^-7 sec units.
|
||||
No official name for these units is given so let's call them "jiffies". */
|
||||
|
||||
#define jiffies_per_sec 10000000LL
|
||||
#define jiffies_per_hour (jiffies_per_sec * 3600LL)
|
||||
#define jiffies_per_day (jiffies_per_hour * 24LL)
|
||||
|
||||
static void WINAPI my_GetSystemTimeAsFileTime(FILETIME *out)
|
||||
{
|
||||
FILETIME in;
|
||||
int64_t day;
|
||||
int64_t real_jiffies;
|
||||
int64_t real_jiffies_biased;
|
||||
int64_t real_time;
|
||||
int64_t fake_time;
|
||||
int64_t fake_jiffies_biased;
|
||||
int64_t fake_jiffies;
|
||||
|
||||
if (out == NULL) {
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get and convert real jiffies */
|
||||
|
||||
next_GetSystemTimeAsFileTime(&in);
|
||||
real_jiffies = (((int64_t) in.dwHighDateTime) << 32) | in.dwLowDateTime;
|
||||
|
||||
/* Keepout period is JST [02:00, 07:00), which is equivalent to
|
||||
UTC [17:00, 22:00). Bias UTC forward by 2 hours, changing this interval
|
||||
to [19:00, 00:00) to make the math easier. We revert this bias later. */
|
||||
|
||||
real_jiffies_biased = real_jiffies + 2LL * jiffies_per_hour;
|
||||
|
||||
/* Split date and time */
|
||||
|
||||
day = real_jiffies_biased / jiffies_per_day;
|
||||
real_time = real_jiffies_biased % jiffies_per_day;
|
||||
|
||||
/* Debug log */
|
||||
|
||||
if (clock_current_day != 0 && clock_current_day != day) {
|
||||
dprintf("\n*** CLOCK JUMP! ***\n\n");
|
||||
}
|
||||
|
||||
clock_current_day = day;
|
||||
|
||||
/* We want to skip the final five hours of our UTC+2 biased reference frame,
|
||||
so scale time-of-day by 19/24. */
|
||||
|
||||
fake_time = (real_time * 19LL) / 24LL;
|
||||
|
||||
/* Un-split date and time */
|
||||
|
||||
fake_jiffies_biased = day * jiffies_per_day + fake_time;
|
||||
|
||||
/* Revert bias */
|
||||
|
||||
fake_jiffies = fake_jiffies_biased - 2LL * jiffies_per_hour;
|
||||
|
||||
/* Return result */
|
||||
|
||||
out->dwLowDateTime = fake_jiffies;
|
||||
out->dwHighDateTime = fake_jiffies >> 32;
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_GetLocalTime(SYSTEMTIME *out)
|
||||
{
|
||||
ULARGE_INTEGER arith;
|
||||
FILETIME linear;
|
||||
|
||||
/* Force JST */
|
||||
|
||||
my_GetSystemTimeAsFileTime(&linear);
|
||||
|
||||
arith.LowPart = linear.dwLowDateTime;
|
||||
arith.HighPart = linear.dwHighDateTime;
|
||||
arith.QuadPart += 9ULL * jiffies_per_hour;
|
||||
linear.dwLowDateTime = arith.LowPart;
|
||||
linear.dwHighDateTime = arith.HighPart;
|
||||
|
||||
return FileTimeToSystemTime(&linear, out);
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_GetSystemTime(SYSTEMTIME *out)
|
||||
{
|
||||
FILETIME linear;
|
||||
BOOL ok;
|
||||
|
||||
my_GetSystemTimeAsFileTime(&linear);
|
||||
ok = FileTimeToSystemTime(&linear, out);
|
||||
|
||||
if (!ok) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int last_second;
|
||||
|
||||
if (out->wSecond != last_second) {
|
||||
dprintf("%04i/%02i/%02i %02i:%02i:%02i\n",
|
||||
out->wYear,
|
||||
out->wMonth,
|
||||
out->wDay,
|
||||
out->wHour,
|
||||
out->wMinute,
|
||||
out->wSecond);
|
||||
}
|
||||
|
||||
last_second = out->wSecond;
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static DWORD WINAPI my_GetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo)
|
||||
{
|
||||
dprintf("%s\n", __func__);
|
||||
|
||||
if (tzinfo == NULL) {
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
|
||||
return TIME_ZONE_ID_INVALID;
|
||||
}
|
||||
|
||||
/* Force JST (UTC+9), SEGA games malfunction in any other time zone.
|
||||
Strings and boundary times don't matter, we only set the offset. */
|
||||
|
||||
memset(tzinfo, 0, sizeof(*tzinfo));
|
||||
tzinfo->Bias = -9 * 60;
|
||||
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
|
||||
/* "Unknown" here means that this region does not observe DST */
|
||||
|
||||
return TIME_ZONE_ID_UNKNOWN;
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_SetLocalTime(SYSTEMTIME *in)
|
||||
{
|
||||
dprintf("Prevented application from screwing with the local time\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_SetSystemTime(SYSTEMTIME *in)
|
||||
{
|
||||
dprintf("Prevented application from screwing with the system time\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_SetTimeZoneInformation(TIME_ZONE_INFORMATION *in)
|
||||
{
|
||||
dprintf("Prevented application from screwing with the timezone\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void clock_hook_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"kernel32.dll",
|
||||
clock_hook_syms,
|
||||
_countof(clock_hook_syms));
|
||||
}
|
3
hooklib/clock.h
Normal file
3
hooklib/clock.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void clock_hook_init(void);
|
218
hooklib/fdshark.c
Normal file
218
hooklib/fdshark.c
Normal file
@ -0,0 +1,218 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/fdshark.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
static const wchar_t *fdshark_path;
|
||||
static HANDLE fdshark_target_fd;
|
||||
static int fdshark_flags;
|
||||
|
||||
static HRESULT fdshark_handle_irp(struct irp *irp);
|
||||
static HRESULT fdshark_handle_open(struct irp *irp);
|
||||
static HRESULT fdshark_handle_close(struct irp *irp);
|
||||
static HRESULT fdshark_handle_read(struct irp *irp);
|
||||
static HRESULT fdshark_handle_write(struct irp *irp);
|
||||
static HRESULT fdshark_handle_ioctl(struct irp *irp);
|
||||
static bool fdshark_force_sync(struct irp *irp, HRESULT hr);
|
||||
|
||||
HRESULT fdshark_hook_init(const wchar_t *path, int flags)
|
||||
{
|
||||
assert(path != NULL);
|
||||
assert(!(flags & ~FDSHARK_ALL_FLAGS_));
|
||||
|
||||
fdshark_path = path;
|
||||
fdshark_flags = flags;
|
||||
|
||||
return iohook_push_handler(fdshark_handle_irp);
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_irp(struct irp *irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != fdshark_target_fd) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return fdshark_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return fdshark_handle_close(irp);
|
||||
case IRP_OP_READ: return fdshark_handle_read(irp);
|
||||
case IRP_OP_WRITE: return fdshark_handle_write(irp);
|
||||
case IRP_OP_IOCTL: return fdshark_handle_ioctl(irp);
|
||||
default: return iohook_invoke_next(irp);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_open(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (_wcsicmp(irp->open_filename, fdshark_path) != 0) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
hr = iohook_invoke_next(irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("FdShark: Opened %S\n", fdshark_path);
|
||||
fdshark_target_fd = irp->fd;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_close(struct irp *irp)
|
||||
{
|
||||
dprintf("FdShark: Closed %S\n", fdshark_path);
|
||||
fdshark_target_fd = NULL;
|
||||
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_read(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!(fdshark_flags & FDSHARK_TRACE_READ)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("FdShark: Read %p:%i/%i\n",
|
||||
irp->read.bytes,
|
||||
(int) irp->read.pos,
|
||||
(int) irp->read.nbytes);
|
||||
|
||||
hr = iohook_invoke_next(irp);
|
||||
|
||||
if (FAILED(hr) && !fdshark_force_sync(irp, hr)) {
|
||||
dprintf("FdShark: FAILED: %x\n", (int) hr);
|
||||
} else {
|
||||
dprintf("FdShark: Read %p:%i/%i OK\n",
|
||||
irp->read.bytes,
|
||||
(int) irp->read.pos,
|
||||
(int) irp->read.nbytes);
|
||||
dump_iobuf(&irp->read);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_write(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!(fdshark_flags & FDSHARK_TRACE_WRITE)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("FdShark: Write %p:%i/%i\n",
|
||||
irp->write.bytes,
|
||||
(int) irp->write.pos,
|
||||
(int) irp->write.nbytes);
|
||||
dump_const_iobuf(&irp->write);
|
||||
|
||||
hr = iohook_invoke_next(irp);
|
||||
|
||||
if (FAILED(hr) && !fdshark_force_sync(irp, hr)) {
|
||||
dprintf("FdShark: FAILED: %x\n", (int) hr);
|
||||
} else {
|
||||
dprintf("FdShark: Write %p:%i/%i OK\n",
|
||||
irp->write.bytes,
|
||||
(int) irp->write.pos,
|
||||
(int) irp->write.nbytes);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT fdshark_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!(fdshark_flags & FDSHARK_TRACE_IOCTL)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("FdShark: Ioctl %08x w:%p:%i/%i r:%p:%i/%i\n",
|
||||
irp->ioctl,
|
||||
irp->write.bytes,
|
||||
(int) irp->write.pos,
|
||||
(int) irp->write.nbytes,
|
||||
irp->read.bytes,
|
||||
(int) irp->read.pos,
|
||||
(int) irp->read.nbytes);
|
||||
dump_const_iobuf(&irp->write);
|
||||
|
||||
hr = iohook_invoke_next(irp);
|
||||
|
||||
if (FAILED(hr) && !fdshark_force_sync(irp, hr)) {
|
||||
dprintf("FdShark: FAILED: %x\n", (int) hr);
|
||||
} else {
|
||||
dprintf("FdShark: Ioctl %08x w:%p:%i/%i r:%p:%i/%i OK\n",
|
||||
irp->ioctl,
|
||||
irp->write.bytes,
|
||||
(int) irp->write.pos,
|
||||
(int) irp->write.nbytes,
|
||||
irp->read.bytes,
|
||||
(int) irp->read.pos,
|
||||
(int) irp->read.nbytes);
|
||||
dump_iobuf(&irp->read);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static bool fdshark_force_sync(struct irp *irp, HRESULT hr)
|
||||
{
|
||||
DWORD xferred;
|
||||
BOOL ok;
|
||||
|
||||
if (!(fdshark_flags & FDSHARK_FORCE_SYNC)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING) &&
|
||||
hr != HRESULT_FROM_NT(STATUS_PENDING)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = GetOverlappedResult(irp->fd, irp->ovl, &xferred, TRUE);
|
||||
|
||||
if (!ok) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("FdShark: Synchronous block failed: %x\n", (int) hr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_READ:
|
||||
case IRP_OP_IOCTL:
|
||||
irp->read.pos += xferred;
|
||||
|
||||
break;
|
||||
|
||||
case IRP_OP_WRITE:
|
||||
irp->write.pos += xferred;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
16
hooklib/fdshark.h
Normal file
16
hooklib/fdshark.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
enum {
|
||||
FDSHARK_FORCE_SYNC = 0x1,
|
||||
FDSHARK_TRACE_READ = 0x2,
|
||||
FDSHARK_TRACE_WRITE = 0x4,
|
||||
FDSHARK_TRACE_IOCTL = 0x8,
|
||||
FDSHARK_ALL_FLAGS_ = 0xF,
|
||||
};
|
||||
|
||||
HRESULT fdshark_hook_init(const wchar_t *filename, int flags);
|
108
hooklib/gfx.c
Normal file
108
hooklib/gfx.c
Normal file
@ -0,0 +1,108 @@
|
||||
#include <windows.h>
|
||||
#include <d3d9.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hook/com-proxy.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/gfx.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_CreateDevice(
|
||||
IDirect3D9 *self,
|
||||
UINT adapter,
|
||||
D3DDEVTYPE type,
|
||||
HWND hwnd,
|
||||
DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp,
|
||||
IDirect3DDevice9 **pdev);
|
||||
|
||||
static IDirect3D9 * WINAPI my_Direct3DCreate9(UINT sdk_ver);
|
||||
|
||||
static IDirect3D9 * (WINAPI *next_Direct3DCreate9)(UINT sdk_ver);
|
||||
|
||||
static bool gfx_windowed;
|
||||
|
||||
static const struct hook_symbol gfx_hooks[] = {
|
||||
{
|
||||
.name = "Direct3DCreate9",
|
||||
.patch = my_Direct3DCreate9,
|
||||
.link = (void **) &next_Direct3DCreate9
|
||||
},
|
||||
};
|
||||
|
||||
void gfx_hook_init(void)
|
||||
{
|
||||
hook_table_apply(NULL, "d3d9.dll", gfx_hooks, _countof(gfx_hooks));
|
||||
}
|
||||
|
||||
void gfx_set_windowed(void)
|
||||
{
|
||||
gfx_windowed = true;
|
||||
}
|
||||
|
||||
static IDirect3D9 * WINAPI my_Direct3DCreate9(UINT sdk_ver)
|
||||
{
|
||||
struct com_proxy *proxy;
|
||||
IDirect3D9Vtbl *vtbl;
|
||||
IDirect3D9 *api;
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("Direct3DCreate9 hook hit\n");
|
||||
|
||||
api = next_Direct3DCreate9(sdk_ver);
|
||||
|
||||
if (api == NULL) {
|
||||
dprintf("next_Direct3DCreate9 returned NULL\n");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("com_proxy_wrap returned %x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vtbl = proxy->vptr;
|
||||
vtbl->CreateDevice = my_CreateDevice;
|
||||
|
||||
return (IDirect3D9 *) proxy;
|
||||
|
||||
fail:
|
||||
if (api != NULL) {
|
||||
IDirect3D9_Release(api);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_CreateDevice(
|
||||
IDirect3D9 *self,
|
||||
UINT adapter,
|
||||
D3DDEVTYPE type,
|
||||
HWND hwnd,
|
||||
DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp,
|
||||
IDirect3DDevice9 **pdev)
|
||||
{
|
||||
struct com_proxy *proxy;
|
||||
IDirect3D9 *real;
|
||||
|
||||
dprintf("IDirect3D9::CreateDevice hook hit\n");
|
||||
|
||||
proxy = com_proxy_downcast(self);
|
||||
real = proxy->real;
|
||||
|
||||
if (gfx_windowed) {
|
||||
pp->Windowed = TRUE;
|
||||
pp->FullScreen_RefreshRateInHz = 0;
|
||||
}
|
||||
|
||||
return IDirect3D9_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev);
|
||||
}
|
4
hooklib/gfx.h
Normal file
4
hooklib/gfx.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void gfx_hook_init(void);
|
||||
void gfx_set_windowed(void);
|
21
hooklib/meson.build
Normal file
21
hooklib/meson.build
Normal file
@ -0,0 +1,21 @@
|
||||
hooklib_lib = static_library(
|
||||
'hooklib',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
],
|
||||
sources : [
|
||||
'clock.c',
|
||||
'clock.h',
|
||||
'fdshark.c',
|
||||
'fdshark.h',
|
||||
'gfx.c',
|
||||
'gfx.h',
|
||||
'setupapi.c',
|
||||
'setupapi.h',
|
||||
'spike.c',
|
||||
'spike.h',
|
||||
],
|
||||
)
|
334
hooklib/setupapi.c
Normal file
334
hooklib/setupapi.c
Normal file
@ -0,0 +1,334 @@
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "hooklib/setupapi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
struct setupapi_class {
|
||||
const GUID *guid;
|
||||
const wchar_t *path;
|
||||
HDEVINFO cur_handle;
|
||||
};
|
||||
|
||||
static void setupapi_hook_init(void);
|
||||
|
||||
/* API hooks */
|
||||
|
||||
static HDEVINFO WINAPI my_SetupDiGetClassDevsW(
|
||||
const GUID *ClassGuid,
|
||||
wchar_t *Enumerator,
|
||||
HWND hwndParent,
|
||||
DWORD Flags);
|
||||
|
||||
static BOOL WINAPI my_SetupDiEnumDeviceInterfaces(
|
||||
HDEVINFO DeviceInfoSet,
|
||||
SP_DEVINFO_DATA *DeviceInfoData,
|
||||
const GUID *InterfaceClassGuid,
|
||||
DWORD MemberIndex,
|
||||
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData);
|
||||
|
||||
static BOOL WINAPI my_SetupDiGetDeviceInterfaceDetailW(
|
||||
HDEVINFO DeviceInfoSet,
|
||||
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData,
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_W *DeviceInterfaceDetailData,
|
||||
DWORD DeviceInterfaceDetailDataSize,
|
||||
DWORD *RequiredSize,
|
||||
SP_DEVINFO_DATA *DeviceInfoData);
|
||||
|
||||
static BOOL WINAPI my_SetupDiDestroyDeviceInfoList(HDEVINFO DeviceInfoSet);
|
||||
|
||||
/* Links */
|
||||
|
||||
static HDEVINFO (WINAPI *next_SetupDiGetClassDevsW)(
|
||||
const GUID *ClassGuid,
|
||||
wchar_t *Enumerator,
|
||||
HWND hwndParent,
|
||||
DWORD Flags);
|
||||
|
||||
static BOOL (WINAPI *next_SetupDiEnumDeviceInterfaces)(
|
||||
HDEVINFO DeviceInfoSet,
|
||||
SP_DEVINFO_DATA *DeviceInfoData,
|
||||
const GUID *InterfaceClassGuid,
|
||||
DWORD MemberIndex,
|
||||
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData);
|
||||
|
||||
static BOOL (WINAPI *next_SetupDiGetDeviceInterfaceDetailW)(
|
||||
HDEVINFO DeviceInfoSet,
|
||||
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData,
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_W *DeviceInterfaceDetailData,
|
||||
DWORD DeviceInterfaceDetailDataSize,
|
||||
DWORD *RequiredSize,
|
||||
SP_DEVINFO_DATA *DeviceInfoData);
|
||||
|
||||
static BOOL (WINAPI *next_SetupDiDestroyDeviceInfoList)(HDEVINFO DeviceInfoSet);
|
||||
|
||||
/* Hook tbl */
|
||||
|
||||
static const struct hook_symbol setupapi_syms[] = {
|
||||
{
|
||||
.name = "SetupDiGetClassDevsW",
|
||||
.patch = my_SetupDiGetClassDevsW,
|
||||
.link = (void *) &next_SetupDiGetClassDevsW,
|
||||
}, {
|
||||
.name = "SetupDiEnumDeviceInterfaces",
|
||||
.patch = my_SetupDiEnumDeviceInterfaces,
|
||||
.link = (void *) &next_SetupDiEnumDeviceInterfaces,
|
||||
}, {
|
||||
.name = "SetupDiGetDeviceInterfaceDetailW",
|
||||
.patch = my_SetupDiGetDeviceInterfaceDetailW,
|
||||
.link = (void *) &next_SetupDiGetDeviceInterfaceDetailW,
|
||||
}, {
|
||||
.name = "SetupDiDestroyDeviceInfoList",
|
||||
.patch = my_SetupDiDestroyDeviceInfoList,
|
||||
.link = (void *) &next_SetupDiDestroyDeviceInfoList,
|
||||
}
|
||||
};
|
||||
|
||||
static bool setupapi_initted;
|
||||
static CRITICAL_SECTION setupapi_lock;
|
||||
static struct setupapi_class *setupapi_classes;
|
||||
static size_t setupapi_nclasses;
|
||||
|
||||
HRESULT setupapi_add_phantom_dev(const GUID *iface_class, const wchar_t *path)
|
||||
{
|
||||
struct setupapi_class *class_;
|
||||
struct setupapi_class *new_array;
|
||||
HRESULT hr;
|
||||
|
||||
assert(iface_class != NULL);
|
||||
assert(path != NULL);
|
||||
|
||||
setupapi_hook_init();
|
||||
|
||||
EnterCriticalSection(&setupapi_lock);
|
||||
|
||||
new_array = realloc(
|
||||
setupapi_classes,
|
||||
(setupapi_nclasses + 1) * sizeof(struct setupapi_class));
|
||||
|
||||
if (new_array == NULL) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
setupapi_classes = new_array;
|
||||
|
||||
class_ = &setupapi_classes[setupapi_nclasses++];
|
||||
class_->guid = iface_class;
|
||||
class_->path = path;
|
||||
|
||||
end:
|
||||
LeaveCriticalSection(&setupapi_lock);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static void setupapi_hook_init(void)
|
||||
{
|
||||
if (setupapi_initted) {
|
||||
return;
|
||||
}
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"setupapi.dll",
|
||||
setupapi_syms,
|
||||
_countof(setupapi_syms));
|
||||
|
||||
InitializeCriticalSection(&setupapi_lock);
|
||||
setupapi_initted = true;
|
||||
}
|
||||
|
||||
static HDEVINFO WINAPI my_SetupDiGetClassDevsW(
|
||||
const GUID *ClassGuid,
|
||||
wchar_t *Enumerator,
|
||||
HWND hwndParent,
|
||||
DWORD Flags)
|
||||
{
|
||||
struct setupapi_class *class_;
|
||||
HDEVINFO result;
|
||||
size_t i;
|
||||
|
||||
result = next_SetupDiGetClassDevsW(
|
||||
ClassGuid,
|
||||
Enumerator,
|
||||
hwndParent,
|
||||
Flags);
|
||||
|
||||
if (result == INVALID_HANDLE_VALUE || ClassGuid == NULL) {
|
||||
return result;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&setupapi_lock);
|
||||
|
||||
for (i = 0 ; i < setupapi_nclasses ; i++) {
|
||||
class_ = &setupapi_classes[i];
|
||||
if (memcmp(ClassGuid, class_->guid, sizeof(*ClassGuid)) == 0) {
|
||||
class_->cur_handle = result;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&setupapi_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_SetupDiEnumDeviceInterfaces(
|
||||
HDEVINFO DeviceInfoSet,
|
||||
SP_DEVINFO_DATA *DeviceInfoData,
|
||||
const GUID *InterfaceClassGuid,
|
||||
DWORD MemberIndex,
|
||||
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData)
|
||||
{
|
||||
const struct setupapi_class *class_;
|
||||
size_t i;
|
||||
|
||||
if ( DeviceInfoSet == INVALID_HANDLE_VALUE ||
|
||||
DeviceInterfaceData == NULL ||
|
||||
DeviceInterfaceData->cbSize != sizeof(*DeviceInterfaceData)) {
|
||||
goto pass;
|
||||
}
|
||||
|
||||
if (MemberIndex > 0) {
|
||||
MemberIndex--;
|
||||
|
||||
goto pass;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&setupapi_lock);
|
||||
|
||||
for ( i = 0, class_ = NULL ;
|
||||
i < setupapi_nclasses && class_ == NULL ;
|
||||
i++) {
|
||||
if (DeviceInfoSet == setupapi_classes[i].cur_handle) {
|
||||
class_ = &setupapi_classes[i];
|
||||
|
||||
dprintf("SetupAPI: Interface {%08lx-...} -> Device node %S\n",
|
||||
class_->guid->Data1,
|
||||
class_->path);
|
||||
|
||||
memcpy( &DeviceInterfaceData->InterfaceClassGuid,
|
||||
class_->guid,
|
||||
sizeof(GUID));
|
||||
DeviceInterfaceData->Flags = SPINT_ACTIVE;
|
||||
DeviceInterfaceData->Reserved = (ULONG_PTR) class_->path;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&setupapi_lock);
|
||||
|
||||
if (class_ == NULL) {
|
||||
goto pass;
|
||||
}
|
||||
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
|
||||
return TRUE;
|
||||
|
||||
pass:
|
||||
return next_SetupDiEnumDeviceInterfaces(
|
||||
DeviceInfoSet,
|
||||
DeviceInfoData,
|
||||
InterfaceClassGuid,
|
||||
MemberIndex,
|
||||
DeviceInterfaceData);
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_SetupDiGetDeviceInterfaceDetailW(
|
||||
HDEVINFO DeviceInfoSet,
|
||||
SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData,
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_W *DeviceInterfaceDetailData,
|
||||
DWORD DeviceInterfaceDetailDataSize,
|
||||
DWORD *RequiredSize,
|
||||
SP_DEVINFO_DATA *DeviceInfoData)
|
||||
{
|
||||
const wchar_t *wstr;
|
||||
size_t nbytes_wstr;
|
||||
size_t nbytes_total;
|
||||
size_t i;
|
||||
bool match;
|
||||
|
||||
if (DeviceInfoSet == INVALID_HANDLE_VALUE || DeviceInterfaceData == NULL) {
|
||||
goto pass;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&setupapi_lock);
|
||||
|
||||
for ( i = 0, match = false ;
|
||||
i < setupapi_nclasses && !match ;
|
||||
i++) {
|
||||
if ( DeviceInfoSet == setupapi_classes[i].cur_handle &&
|
||||
DeviceInterfaceData->Reserved == (ULONG_PTR) setupapi_classes[i].path) {
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&setupapi_lock);
|
||||
|
||||
if (!match) {
|
||||
goto pass;
|
||||
}
|
||||
|
||||
wstr = (const wchar_t *) DeviceInterfaceData->Reserved;
|
||||
nbytes_wstr = (wcslen(wstr) + 1) * sizeof(wchar_t);
|
||||
nbytes_total = offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath);
|
||||
nbytes_total += nbytes_wstr;
|
||||
|
||||
if (RequiredSize != NULL) {
|
||||
*RequiredSize = (DWORD) nbytes_total;
|
||||
}
|
||||
|
||||
if ( DeviceInterfaceDetailData == NULL &&
|
||||
DeviceInterfaceDetailDataSize < nbytes_total) {
|
||||
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (DeviceInterfaceDetailData->cbSize!=sizeof(*DeviceInterfaceDetailData)) {
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(DeviceInterfaceDetailData->DevicePath, wstr, nbytes_wstr);
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
|
||||
return TRUE;
|
||||
|
||||
pass:
|
||||
return next_SetupDiGetDeviceInterfaceDetailW(
|
||||
DeviceInfoSet,
|
||||
DeviceInterfaceData,
|
||||
DeviceInterfaceDetailData,
|
||||
DeviceInterfaceDetailDataSize,
|
||||
RequiredSize,
|
||||
DeviceInfoData);
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_SetupDiDestroyDeviceInfoList(HDEVINFO DeviceInfoSet)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
EnterCriticalSection(&setupapi_lock);
|
||||
|
||||
for (i = 0 ; i < setupapi_nclasses ; i++) {
|
||||
if (setupapi_classes[i].cur_handle == DeviceInfoSet) {
|
||||
setupapi_classes[i].cur_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&setupapi_lock);
|
||||
|
||||
return next_SetupDiDestroyDeviceInfoList(DeviceInfoSet);
|
||||
}
|
7
hooklib/setupapi.h
Normal file
7
hooklib/setupapi.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
HRESULT setupapi_add_phantom_dev(const GUID *iface_class, const wchar_t *path);
|
209
hooklib/spike.c
Normal file
209
hooklib/spike.c
Normal file
@ -0,0 +1,209 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hook/pe.h"
|
||||
|
||||
#include "hooklib/spike.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
/* Spike functions. Their "style" is named after the libc function they bear
|
||||
the closest resemblance to. */
|
||||
|
||||
static void spike_fn_puts(const char *msg)
|
||||
{
|
||||
char line[512];
|
||||
|
||||
sprintf_s(line, _countof(line), "%s\n", msg);
|
||||
OutputDebugStringA(line);
|
||||
}
|
||||
|
||||
static void spike_fn_fputs(const char *msg)
|
||||
{
|
||||
OutputDebugStringA(msg);
|
||||
}
|
||||
|
||||
static void spike_fn_printf(const char *fmt, ...)
|
||||
{
|
||||
char line[512];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsprintf_s(line, _countof(line), fmt, ap);
|
||||
strcat(line, "\n");
|
||||
OutputDebugStringA(line);
|
||||
}
|
||||
|
||||
static void spike_fn_vprintf(
|
||||
const char *proc,
|
||||
int line_no,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
char msg[512];
|
||||
char line[512];
|
||||
|
||||
vsprintf_s(msg, _countof(msg), fmt, ap);
|
||||
sprintf_s(line, _countof(line), "%s:%i: %s", proc, line_no, msg);
|
||||
OutputDebugStringA(line);
|
||||
}
|
||||
|
||||
static void spike_fn_vwprintf(
|
||||
const wchar_t *proc,
|
||||
int line_no,
|
||||
const wchar_t *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
wchar_t msg[512];
|
||||
wchar_t line[512];
|
||||
|
||||
vswprintf_s(msg, _countof(msg), fmt, ap);
|
||||
swprintf_s(line, _countof(line), L"%s:%i: %s", proc, line_no, msg);
|
||||
OutputDebugStringW(line);
|
||||
}
|
||||
|
||||
static void spike_fn_perror(
|
||||
int a1,
|
||||
int a2,
|
||||
int error,
|
||||
const char *file,
|
||||
int line_no,
|
||||
const char *msg)
|
||||
{
|
||||
char line[512];
|
||||
|
||||
sprintf_s(
|
||||
line,
|
||||
_countof(line),
|
||||
"%s:%i:%08x: %s\n",
|
||||
file,
|
||||
line_no,
|
||||
error,
|
||||
msg);
|
||||
|
||||
OutputDebugStringA(line);
|
||||
}
|
||||
|
||||
/* Spike inserters */
|
||||
|
||||
static void spike_insert_jmp(ptrdiff_t rva, void *proc)
|
||||
{
|
||||
uint8_t *base;
|
||||
uint8_t *target;
|
||||
uint8_t *func_ptr;
|
||||
uint32_t delta;
|
||||
|
||||
base = (uint8_t *) GetModuleHandleW(NULL);
|
||||
|
||||
target = base + rva;
|
||||
func_ptr = proc;
|
||||
delta = func_ptr - target - 4; /* -4: EIP delta, after end of target insn */
|
||||
|
||||
pe_patch(target, &delta, sizeof(delta));
|
||||
}
|
||||
|
||||
static void spike_insert_ptr(ptrdiff_t rva, void *ptr)
|
||||
{
|
||||
uint8_t *base;
|
||||
uint8_t *target;
|
||||
|
||||
base = (uint8_t *) GetModuleHandleW(NULL);
|
||||
target = base + rva;
|
||||
|
||||
pe_patch(target, &ptr, sizeof(ptr));
|
||||
}
|
||||
|
||||
static void spike_insert_log_levels(ptrdiff_t rva, size_t count)
|
||||
{
|
||||
uint8_t *base;
|
||||
uint32_t *levels;
|
||||
size_t i;
|
||||
|
||||
base = (uint8_t *) GetModuleHandleW(NULL);
|
||||
levels = (uint32_t *) (base + rva);
|
||||
|
||||
for (i = 0 ; i < count ; i++) {
|
||||
levels[i] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
/* Config reader */
|
||||
|
||||
void spike_hook_init(const char *path)
|
||||
{
|
||||
int match;
|
||||
int count;
|
||||
int rva;
|
||||
char line[80];
|
||||
char *ret;
|
||||
FILE *f;
|
||||
|
||||
f = fopen(path, "r");
|
||||
|
||||
if (f == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("Found spike config, inserting spikes\n");
|
||||
|
||||
for (;;) {
|
||||
ret = fgets(line, sizeof(line), f);
|
||||
|
||||
if (ret == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (line[0] == '#' || line[0] == '\r' || line[0] == '\n') {
|
||||
continue;
|
||||
}
|
||||
|
||||
match = sscanf(line, "levels %i %i", &rva, &count);
|
||||
|
||||
if (match == 2) {
|
||||
spike_insert_log_levels((ptrdiff_t) rva, count);
|
||||
}
|
||||
|
||||
match = sscanf(line, "j_vprintf %i", &rva);
|
||||
|
||||
if (match == 1) {
|
||||
spike_insert_jmp((ptrdiff_t) rva, spike_fn_vprintf);
|
||||
}
|
||||
|
||||
match = sscanf(line, "j_vwprintf %i", &rva);
|
||||
|
||||
if (match == 1) {
|
||||
spike_insert_jmp((ptrdiff_t) rva, spike_fn_vwprintf);
|
||||
}
|
||||
|
||||
match = sscanf(line, "j_printf %i", &rva);
|
||||
|
||||
if (match == 1) {
|
||||
spike_insert_jmp((ptrdiff_t) rva, spike_fn_printf);
|
||||
}
|
||||
|
||||
match = sscanf(line, "j_puts %i", &rva);
|
||||
|
||||
if (match == 1) {
|
||||
spike_insert_jmp((ptrdiff_t) rva, spike_fn_puts);
|
||||
}
|
||||
|
||||
match = sscanf(line, "j_perror %i", &rva);
|
||||
|
||||
if (match == 1) {
|
||||
spike_insert_jmp((ptrdiff_t) rva, spike_fn_perror);
|
||||
}
|
||||
|
||||
match = sscanf(line, "c_fputs %i", &rva); /* c == "callback" */
|
||||
|
||||
if (match == 1) {
|
||||
spike_insert_ptr((ptrdiff_t) rva, spike_fn_fputs);
|
||||
}
|
||||
}
|
||||
|
||||
dprintf("Spike insertion complete\n");
|
||||
fclose(f);
|
||||
}
|
3
hooklib/spike.h
Normal file
3
hooklib/spike.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void spike_hook_init(const char *path);
|
Reference in New Issue
Block a user