Initial Commit

This commit is contained in:
Kevin Trocolli
2023-01-02 23:39:03 -05:00
parent a302b49950
commit ceee973ad7
150 changed files with 12229 additions and 89 deletions
+16
View File
@@ -0,0 +1,16 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include "hooklib/config.h"
#include "hooklib/dvd.h"
void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"dvd", L"enable", 1, filename);
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <stddef.h>
#include "hooklib/dvd.h"
void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename);
+351
View File
@@ -0,0 +1,351 @@
/* This is general enough to break out into capnhook eventually.
Don't introduce util/ dependencies here. */
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "hook/table.h"
#include "hooklib/dll.h"
struct dll_hook_reg {
const wchar_t *name;
HMODULE redir_mod;
};
/* Helper functions */
static void dll_hook_init(void);
static HMODULE dll_hook_search_dll(const wchar_t *name);
/* Hook functions */
static BOOL WINAPI hook_FreeLibrary(HMODULE mod);
static HMODULE WINAPI hook_GetModuleHandleA(const char *name);
static HMODULE WINAPI hook_GetModuleHandleW(const wchar_t *name);
static HMODULE WINAPI hook_LoadLibraryA(const char *name);
static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name);
static HMODULE WINAPI hook_LoadLibraryExA(const char *name, HANDLE file, DWORD flags);
static HMODULE WINAPI hook_LoadLibraryExW(const wchar_t *name, HANDLE file, DWORD flags);
/* Link pointers */
static BOOL (WINAPI *next_FreeLibrary)(HMODULE mod);
static HMODULE (WINAPI *next_GetModuleHandleA)(const char *name);
static HMODULE (WINAPI *next_GetModuleHandleW)(const wchar_t *name);
static HMODULE (WINAPI *next_LoadLibraryA)(const char *name);
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
static HMODULE (WINAPI *next_LoadLibraryExA)(const char *name, HANDLE file, DWORD flags);
static HMODULE (WINAPI *next_LoadLibraryExW)(const wchar_t *name, HANDLE file, DWORD flags);
static const struct hook_symbol dll_loader_syms[] = {
{
.name = "FreeLibrary",
.patch = hook_FreeLibrary,
.link = (void **) &next_FreeLibrary,
}, {
.name = "GetModuleHandleA",
.patch = hook_GetModuleHandleA,
.link = (void **) &next_GetModuleHandleA,
}, {
.name = "GetModuleHandleW",
.patch = hook_GetModuleHandleW,
.link = (void **) &next_GetModuleHandleW,
}, {
.name = "LoadLibraryA",
.patch = hook_LoadLibraryA,
.link = (void **) &next_LoadLibraryA,
}, {
.name = "LoadLibraryW",
.patch = hook_LoadLibraryW,
.link = (void **) &next_LoadLibraryW,
}, {
.name = "LoadLibraryExA",
.patch = hook_LoadLibraryExA,
.link = (void **) &next_LoadLibraryExA,
}, {
.name = "LoadLibraryExW",
.patch = hook_LoadLibraryExW,
.link = (void **) &next_LoadLibraryExW,
}
};
static bool dll_hook_initted;
static CRITICAL_SECTION dll_hook_lock;
static struct dll_hook_reg *dll_hook_list;
static size_t dll_hook_count;
HRESULT dll_hook_push(
HMODULE redir_mod,
const wchar_t *name)
{
struct dll_hook_reg *new_item;
struct dll_hook_reg *new_mem;
HRESULT hr;
assert(name != NULL);
dll_hook_init();
EnterCriticalSection(&dll_hook_lock);
new_mem = realloc(
dll_hook_list,
(dll_hook_count + 1) * sizeof(struct dll_hook_reg));
if (new_mem == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
new_item = &new_mem[dll_hook_count];
new_item->name = name;
new_item->redir_mod = redir_mod;
dll_hook_list = new_mem;
dll_hook_count++;
hr = S_OK;
end:
LeaveCriticalSection(&dll_hook_lock);
return hr;
}
static void dll_hook_init(void)
{
HMODULE kernel32;
/* Init is not thread safe, because API hooking is not thread safe. */
if (dll_hook_initted) {
return;
}
dll_hook_initted = true;
InitializeCriticalSection(&dll_hook_lock);
/* Protect against the (probably impossible) scenario where nothing in the
process imports LoadLibraryW but something imports LoadLibraryA. Also
do the same with LoadLibraryExW.
We know something imports GetModuleHandleW because we do, right here.
(we're about to hook these APIs of course, so we have to set this up
before the hooks go in) */
kernel32 = GetModuleHandleW(L"kernel32.dll");
next_LoadLibraryW = (void *) GetProcAddress(kernel32, "LoadLibraryW");
next_LoadLibraryExW = (void *) GetProcAddress(kernel32, "LoadLibraryExW");
/* Now we can apply the hook table */
hook_table_apply(
NULL,
"kernel32.dll",
dll_loader_syms,
_countof(dll_loader_syms));
}
static HMODULE dll_hook_search_dll(const wchar_t *name)
{
HMODULE result;
size_t i;
result = NULL;
EnterCriticalSection(&dll_hook_lock);
for (i = 0 ; i < dll_hook_count ; i++) {
if (wcsicmp(name, dll_hook_list[i].name) == 0) {
result = dll_hook_list[i].redir_mod;
break;
}
}
LeaveCriticalSection(&dll_hook_lock);
return result;
}
static BOOL WINAPI hook_FreeLibrary(HMODULE mod)
{
bool match;
size_t i;
match = false;
EnterCriticalSection(&dll_hook_lock);
for (i = 0 ; i < dll_hook_count ; i++) {
if (mod == dll_hook_list[i].redir_mod) {
match = true;
break;
}
}
LeaveCriticalSection(&dll_hook_lock);
if (match) {
/* Block attempts to unload redirected modules, since this could cause
a hook DLL to unexpectedly vanish and crash the whole application.
Reference counting might be another solution, although it is
possible that a buggy application might cause a hook DLL unload in
that case. */
SetLastError(ERROR_SUCCESS);
return TRUE;
}
return next_FreeLibrary(mod);
}
static HMODULE WINAPI hook_GetModuleHandleA(const char *name)
{
HMODULE result;
wchar_t *name_w;
size_t name_c;
if (name == NULL) {
return next_GetModuleHandleA(NULL);
}
mbstowcs_s(&name_c, NULL, 0, name, 0);
name_w = malloc(name_c * sizeof(wchar_t));
if (name_w == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
return NULL;
}
mbstowcs_s(NULL, name_w, name_c, name, name_c - 1);
result = hook_GetModuleHandleW(name_w);
free(name_w);
return result;
}
static HMODULE WINAPI hook_GetModuleHandleW(const wchar_t *name)
{
HMODULE result;
if (name == NULL) {
return next_GetModuleHandleW(NULL);
}
result = dll_hook_search_dll(name);
if (result != NULL) {
SetLastError(ERROR_SUCCESS);
} else {
result = next_GetModuleHandleW(name);
}
return result;
}
static HMODULE WINAPI hook_LoadLibraryA(const char *name)
{
HMODULE result;
wchar_t *name_w;
size_t name_c;
if (name == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
mbstowcs_s(&name_c, NULL, 0, name, 0);
name_w = malloc(name_c * sizeof(wchar_t));
if (name_w == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
return NULL;
}
mbstowcs_s(NULL, name_w, name_c, name, name_c - 1);
result = hook_LoadLibraryW(name_w);
free(name_w);
return result;
}
static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name)
{
HMODULE result;
if (name == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
result = dll_hook_search_dll(name);
if (result != NULL) {
SetLastError(ERROR_SUCCESS);
} else {
result = next_LoadLibraryW(name);
}
return result;
}
static HMODULE WINAPI hook_LoadLibraryExA(const char *name, HANDLE file, DWORD flags)
{
HMODULE result;
wchar_t *name_w;
size_t name_c;
if (name == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
mbstowcs_s(&name_c, NULL, 0, name, 0);
name_w = malloc(name_c * sizeof(wchar_t));
if (name_w == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
return NULL;
}
mbstowcs_s(NULL, name_w, name_c, name, name_c - 1);
result = hook_LoadLibraryExW(name_w, file, flags);
free(name_w);
return result;
}
static HMODULE WINAPI hook_LoadLibraryExW(const wchar_t *name, HANDLE file, DWORD flags)
{
HMODULE result;
if (name == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
result = dll_hook_search_dll(name);
if (result != NULL) {
SetLastError(ERROR_SUCCESS);
} else {
result = next_LoadLibraryExW(name, file, flags);
}
return result;
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
HRESULT dll_hook_push(
HMODULE redir_mod,
const wchar_t *name);
+472
View File
@@ -0,0 +1,472 @@
/* Might push this to capnhook, don't add any util dependencies. */
#include <windows.h>
#include <windns.h>
#include <ws2tcpip.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "hook/hr.h"
#include "hook/table.h"
#include "hooklib/dns.h"
#include "util/dprintf.h"
/* Latest w32headers does not include DnsQueryEx, so we'll have to "polyfill"
its associated data types here for the time being.
Results and cancel handle are passed through, so we'll just use void
pointers for those args. So are most of the fields in this structure, for
that matter. */
typedef struct POLYFILL_DNS_QUERY_REQUEST {
ULONG Version;
PCWSTR QueryName;
WORD QueryType;
ULONG64 QueryOptions;
void* pDnsServerList;
ULONG InterfaceIndex;
void* pQueryCompletionCallback;
PVOID pQueryContext;
} POLYFILL_DNS_QUERY_REQUEST;
struct dns_hook_entry {
wchar_t *from;
wchar_t *to;
};
/* Hook funcs */
static DNS_STATUS WINAPI hook_DnsQuery_A(
const char *pszName,
WORD wType,
DWORD Options,
void *pExtra,
DNS_RECORD **ppQueryResults,
void *pReserved);
static DNS_STATUS WINAPI hook_DnsQuery_W(
const wchar_t *pszName,
WORD wType,
DWORD Options,
void *pExtra,
DNS_RECORD **ppQueryResults,
void *pReserved);
static DNS_STATUS WINAPI hook_DnsQueryEx(
POLYFILL_DNS_QUERY_REQUEST *pRequest,
void *pQueryResults,
void *pCancelHandle);
static int WSAAPI hook_getaddrinfo(
const char *pNodeName,
const char *pServiceName,
const ADDRINFOA *pHints,
ADDRINFOA **ppResult);
/* Link pointers */
static DNS_STATUS (WINAPI *next_DnsQuery_A)(
const char *pszName,
WORD wType,
DWORD Options,
void *pExtra,
DNS_RECORD **ppQueryResults,
void *pReserved);
static DNS_STATUS (WINAPI *next_DnsQuery_W)(
const wchar_t *pszName,
WORD wType,
DWORD Options,
void *pExtra,
DNS_RECORD **ppQueryResults,
void *pReserved);
static DNS_STATUS (WINAPI *next_DnsQueryEx)(
POLYFILL_DNS_QUERY_REQUEST *pRequest,
void *pQueryResults,
void *pCancelHandle);
static int (WSAAPI *next_getaddrinfo)(
const char *pNodeName,
const char *pServiceName,
const ADDRINFOA *pHints,
ADDRINFOA **ppResult);
static const struct hook_symbol dns_hook_syms_dnsapi[] = {
{
.name = "DnsQuery_A",
.patch = hook_DnsQuery_A,
.link = (void **) &next_DnsQuery_A,
}, {
.name = "DnsQuery_W",
.patch = hook_DnsQuery_W,
.link = (void **) &next_DnsQuery_W,
}, {
.name = "DnsQueryEx",
.patch = hook_DnsQueryEx,
.link = (void **) &next_DnsQueryEx,
}
};
static const struct hook_symbol dns_hook_syms_ws2[] = {
{
.name = "getaddrinfo",
.ordinal = 176,
.patch = hook_getaddrinfo,
.link = (void **) &next_getaddrinfo,
}
};
static bool dns_hook_initted;
static CRITICAL_SECTION dns_hook_lock;
static struct dns_hook_entry *dns_hook_entries;
static size_t dns_hook_nentries;
static void dns_hook_init(void)
{
if (dns_hook_initted) {
return;
}
dns_hook_initted = true;
InitializeCriticalSection(&dns_hook_lock);
hook_table_apply(
NULL,
"dnsapi.dll",
dns_hook_syms_dnsapi,
_countof(dns_hook_syms_dnsapi));
hook_table_apply(
NULL,
"ws2_32.dll",
dns_hook_syms_ws2,
_countof(dns_hook_syms_ws2));
}
HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src)
{
HRESULT hr;
struct dns_hook_entry *newmem;
struct dns_hook_entry *newitem;
wchar_t *from;
wchar_t *to;
assert(from_src != NULL);
to = NULL;
from = NULL;
dns_hook_init();
EnterCriticalSection(&dns_hook_lock);
from = _wcsdup(from_src);
if (from == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
if(to_src != NULL) {
to = _wcsdup(to_src);
if (to == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
}
newmem = realloc(
dns_hook_entries,
(dns_hook_nentries + 1) * sizeof(struct dns_hook_entry));
if (newmem == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
dns_hook_entries = newmem;
newitem = &newmem[dns_hook_nentries++];
newitem->from = from;
newitem->to = to;
from = NULL;
to = NULL;
hr = S_OK;
end:
LeaveCriticalSection(&dns_hook_lock);
free(to);
free(from);
return hr;
}
static DNS_STATUS WINAPI hook_DnsQuery_A(
const char *pszName,
WORD wType,
DWORD Options,
void *pExtra,
DNS_RECORD **ppQueryResults,
void *pReserved)
{
const struct dns_hook_entry *pos;
size_t i;
size_t wstr_c;
wchar_t *wstr;
size_t str_c;
char *str;
DNS_STATUS code;
HRESULT hr;
wstr = NULL;
str = NULL;
if (pszName == NULL) {
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto end;
}
dprintf("DNS: %s Lookup %s", __func__, pszName);
mbstowcs_s(&wstr_c, NULL, 0, pszName, 0);
wstr = malloc(wstr_c * sizeof(wchar_t));
if (wstr == NULL) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto end;
}
mbstowcs_s(NULL, wstr, wstr_c, pszName, wstr_c - 1);
EnterCriticalSection(&dns_hook_lock);
for (i = 0 ; i < dns_hook_nentries ; i++) {
pos = &dns_hook_entries[i];
if (_wcsicmp(wstr, pos->from) == 0) {
if(pos->to == NULL) {
LeaveCriticalSection(&dns_hook_lock);
hr = HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
goto end;
}
wcstombs_s(&str_c, NULL, 0, pos->to, 0);
str = malloc(str_c * sizeof(char));
if (str == NULL) {
LeaveCriticalSection(&dns_hook_lock);
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto end;
}
wcstombs_s(NULL, str, str_c, pos->to, str_c - 1);
pszName = str;
break;
}
}
LeaveCriticalSection(&dns_hook_lock);
code = next_DnsQuery_A(
pszName,
wType,
Options,
pExtra,
ppQueryResults,
pReserved);
hr = HRESULT_FROM_WIN32(code);
end:
free(str);
free(wstr);
return hr_to_win32_error(hr);
}
static DNS_STATUS WINAPI hook_DnsQuery_W(
const wchar_t *pszName,
WORD wType,
DWORD Options,
void *pExtra,
DNS_RECORD **ppQueryResults,
void *pReserved)
{
const struct dns_hook_entry *pos;
size_t i;
if (pszName == NULL) {
return ERROR_INVALID_PARAMETER;
}
dprintf("DNS: %s Lookup %ls", __func__, pszName);
EnterCriticalSection(&dns_hook_lock);
for (i = 0 ; i < dns_hook_nentries ; i++) {
pos = &dns_hook_entries[i];
if (_wcsicmp(pszName, pos->from) == 0) {
if(pos->to == NULL) {
LeaveCriticalSection(&dns_hook_lock);
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
}
pszName = pos->to;
break;
}
}
LeaveCriticalSection(&dns_hook_lock);
return next_DnsQuery_W(
pszName,
wType,
Options,
pExtra,
ppQueryResults,
pReserved);
}
static DNS_STATUS WINAPI hook_DnsQueryEx(
POLYFILL_DNS_QUERY_REQUEST *pRequest,
void *pQueryResults,
void *pCancelHandle)
{
const wchar_t *orig;
const struct dns_hook_entry *pos;
DNS_STATUS code;
size_t i;
if (pRequest == NULL) {
return ERROR_INVALID_PARAMETER;
}
orig = pRequest->QueryName;
dprintf("DNS: %s Lookup %ls", __func__, orig);
EnterCriticalSection(&dns_hook_lock);
for (i = 0 ; i < dns_hook_nentries ; i++) {
pos = &dns_hook_entries[i];
if (_wcsicmp(pRequest->QueryName, pos->from) == 0) {
if(pos->to == NULL) {
LeaveCriticalSection(&dns_hook_lock);
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
}
pRequest->QueryName = pos->to;
break;
}
}
LeaveCriticalSection(&dns_hook_lock);
code = next_DnsQueryEx(pRequest, pQueryResults, pCancelHandle);
/* Caller might not appreciate QueryName changing under its feet. It is
strongly implied by MSDN that a copy of *pRequest is taken by WINAPI,
so we can change it back after the call has been issued with no ill
effect... we hope.
Hopefully the completion callback is issued from an APC or something
(or otherwise happens after this returns) or we're in trouble. */
pRequest->QueryName = orig;
return code;
}
static int WSAAPI hook_getaddrinfo(
const char *pNodeName,
const char *pServiceName,
const ADDRINFOA *pHints,
ADDRINFOA **ppResult)
{
const struct dns_hook_entry *pos;
char *str;
size_t str_c;
wchar_t *wstr;
size_t wstr_c;
int result;
size_t i;
str = NULL;
wstr = NULL;
dprintf("DNS: getaddrinfo pNodeName %s pServiceName %s\n", pNodeName, pServiceName);
if (pNodeName == NULL) {
result = WSA_INVALID_PARAMETER;
goto end;
}
mbstowcs_s(&wstr_c, NULL, 0, pNodeName, 0);
wstr = malloc(wstr_c * sizeof(wchar_t));
if (wstr == NULL) {
result = WSA_NOT_ENOUGH_MEMORY;
goto end;
}
mbstowcs_s(NULL, wstr, wstr_c, pNodeName, wstr_c - 1);
EnterCriticalSection(&dns_hook_lock);
for (i = 0 ; i < dns_hook_nentries ; i++) {
pos = &dns_hook_entries[i];
if (_wcsicmp(wstr, pos->from) == 0) {
if(pos->to == NULL) {
LeaveCriticalSection(&dns_hook_lock);
result = EAI_NONAME;
goto end;
}
wcstombs_s(&str_c, NULL, 0, pos->to, 0);
str = malloc(str_c * sizeof(char));
if (str == NULL) {
LeaveCriticalSection(&dns_hook_lock);
result = WSA_NOT_ENOUGH_MEMORY;
goto end;
}
wcstombs_s(NULL, str, str_c, pos->to, str_c - 1);
pNodeName = str;
break;
}
}
LeaveCriticalSection(&dns_hook_lock);
result = next_getaddrinfo(pNodeName, pServiceName, pHints, ppResult);
end:
free(wstr);
free(str);
return result;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <windows.h>
#include <stddef.h>
// if to_src is NULL, all lookups for from_src will fail
HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src);
+82
View File
@@ -0,0 +1,82 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include "hook/com-proxy.h"
#include "hook/table.h"
#include "hooklib/config.h"
#include "hooklib/dll.h"
#include "hooklib/dvd.h"
#include "util/dprintf.h"
/* API hooks */
static DWORD WINAPI hook_QueryDosDeviceW(
const wchar_t *lpDeviceName,
wchar_t *lpTargetPath,
DWORD ucchMax);
/* Link pointers */
static DWORD (WINAPI *next_QueryDosDeviceW)(
const wchar_t *lpDeviceName,
wchar_t *lpTargetPath,
DWORD ucchMax);
static bool dvd_hook_initted;
static struct dvd_config dvd_config;
static const struct hook_symbol dvd_hooks[] = {
{
.name = "QueryDosDeviceW",
.patch = hook_QueryDosDeviceW,
.link = (void **) &next_QueryDosDeviceW
},
};
void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self)
{
assert(cfg != NULL);
if (!cfg->enable) {
return;
}
if (dvd_hook_initted) {
return;
}
dvd_hook_initted = true;
memcpy(&dvd_config, cfg, sizeof(*cfg));
hook_table_apply(NULL, "kernel32.dll", dvd_hooks, _countof(dvd_hooks));
dprintf("DVD: hook enabled.\n");
}
DWORD WINAPI hook_QueryDosDeviceW(
const wchar_t *lpDeviceName,
wchar_t *lpTargetPath,
DWORD ucchMax)
{
DWORD ok;
wchar_t *p_dest;
wchar_t *dvd_string = L"CdRom";
ok = next_QueryDosDeviceW(
lpDeviceName,
lpTargetPath,
ucchMax);
p_dest = wcsstr (lpTargetPath, dvd_string);
if ( p_dest != NULL ) {
dprintf("DVD: Hiding DVD drive.\n");
return 0;
}
return ok;
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct dvd_config {
bool enable;
};
/* Init is not thread safe because API hook init is not thread safe blah
blah blah you know the drill by now. */
void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self);
+218
View 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
View 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);
+29
View File
@@ -0,0 +1,29 @@
hooklib_lib = static_library(
'hooklib',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],
sources : [
'config.c',
'config.h',
'dll.c',
'dll.h',
'dns.c',
'dns.h',
'dvd.c',
'dvd.h',
'fdshark.c',
'fdshark.h',
'path.c',
'path.h',
'reg.c',
'reg.h',
'setupapi.c',
'setupapi.h',
'spike.c',
'spike.h',
],
)
+856
View File
@@ -0,0 +1,856 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "hook/hr.h"
#include "hook/table.h"
#include "hooklib/path.h"
/* Helpers */
static void path_hook_init(void);
static BOOL path_transform_a(char **out, const char *src);
static BOOL path_transform_w(wchar_t **out, const wchar_t *src);
/* API hooks */
static BOOL WINAPI hook_CreateDirectoryA(
const char *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL WINAPI hook_CreateDirectoryW(
const wchar_t *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL WINAPI hook_CreateDirectoryExA(
const char *lpTemplateDirectory,
const char *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL WINAPI hook_CreateDirectoryExW(
const wchar_t *lpTemplateDirectory,
const wchar_t *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static HANDLE WINAPI hook_CreateFileA(
const char *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static HANDLE WINAPI hook_CreateFileW(
const wchar_t *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static HANDLE WINAPI hook_FindFirstFileA(
const char *lpFileName,
LPWIN32_FIND_DATAA lpFindFileData);
static HANDLE WINAPI hook_FindFirstFileW(
const wchar_t *lpFileName,
LPWIN32_FIND_DATAW lpFindFileData);
static HANDLE WINAPI hook_FindFirstFileExA(
const char *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static HANDLE WINAPI hook_FindFirstFileExW(
const wchar_t *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static DWORD WINAPI hook_GetFileAttributesA(const char *lpFileName);
static DWORD WINAPI hook_GetFileAttributesW(const wchar_t *lpFileName);
static BOOL WINAPI hook_GetFileAttributesExA(
const char *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation);
static BOOL WINAPI hook_GetFileAttributesExW(
const wchar_t *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation);
static BOOL WINAPI hook_RemoveDirectoryA(const char *lpFileName);
static BOOL WINAPI hook_RemoveDirectoryW(const wchar_t *lpFileName);
/* Link pointers */
static BOOL (WINAPI *next_CreateDirectoryA)(
const char *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL (WINAPI *next_CreateDirectoryW)(
const wchar_t *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL (WINAPI *next_CreateDirectoryExA)(
const char *lpTemplateDirectory,
const char *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static BOOL (WINAPI *next_CreateDirectoryExW)(
const wchar_t *lpTemplateDirectory,
const wchar_t *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes);
static HANDLE (WINAPI *next_CreateFileA)(
const char *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static HANDLE (WINAPI *next_CreateFileW)(
const wchar_t *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile);
static HANDLE (WINAPI *next_FindFirstFileA)(
const char *lpFileName,
LPWIN32_FIND_DATAA lpFindFileData);
static HANDLE (WINAPI *next_FindFirstFileW)(
const wchar_t *lpFileName,
LPWIN32_FIND_DATAW lpFindFileData);
static HANDLE (WINAPI *next_FindFirstFileExA)(
const char *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static HANDLE (WINAPI *next_FindFirstFileExW)(
const wchar_t *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags);
static DWORD (WINAPI *next_GetFileAttributesA)(const char *lpFileName);
static DWORD (WINAPI *next_GetFileAttributesW)(const wchar_t *lpFileName);
static BOOL (WINAPI *next_GetFileAttributesExA)(
const char *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation);
static BOOL (WINAPI *next_GetFileAttributesExW)(
const wchar_t *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation);
static BOOL (WINAPI *next_RemoveDirectoryA)(const char *lpFileName);
static BOOL (WINAPI *next_RemoveDirectoryW)(const wchar_t *lpFileName);
/* Hook table */
static const struct hook_symbol path_hook_syms[] = {
{
.name = "CreateDirectoryA",
.patch = hook_CreateDirectoryA,
.link = (void **) &next_CreateDirectoryA,
}, {
.name = "CreateDirectoryW",
.patch = hook_CreateDirectoryW,
.link = (void **) &next_CreateDirectoryW,
}, {
.name = "CreateDirectoryExA",
.patch = hook_CreateDirectoryExA,
.link = (void **) &next_CreateDirectoryExA,
}, {
.name = "CreateDirectoryExW",
.patch = hook_CreateDirectoryExW,
.link = (void **) &next_CreateDirectoryExW,
}, {
.name = "CreateFileA",
.patch = hook_CreateFileA,
.link = (void **) &next_CreateFileA,
}, {
.name = "CreateFileW",
.patch = hook_CreateFileW,
.link = (void **) &next_CreateFileW,
}, {
.name = "FindFirstFileA",
.patch = hook_FindFirstFileA,
.link = (void **) &next_FindFirstFileA,
}, {
.name = "FindFirstFileW",
.patch = hook_FindFirstFileW,
.link = (void **) &next_FindFirstFileW,
}, {
.name = "FindFirstFileExA",
.patch = hook_FindFirstFileExA,
.link = (void **) &next_FindFirstFileExA,
}, {
.name = "FindFirstFileExW",
.patch = hook_FindFirstFileExW,
.link = (void **) &next_FindFirstFileExW,
}, {
.name = "GetFileAttributesA",
.patch = hook_GetFileAttributesA,
.link = (void **) &next_GetFileAttributesA,
}, {
.name = "GetFileAttributesW",
.patch = hook_GetFileAttributesW,
.link = (void **) &next_GetFileAttributesW,
}, {
.name = "GetFileAttributesExA",
.patch = hook_GetFileAttributesExA,
.link = (void **) &next_GetFileAttributesExA,
}, {
.name = "GetFileAttributesExW",
.patch = hook_GetFileAttributesExW,
.link = (void **) &next_GetFileAttributesExW,
}, {
.name = "RemoveDirectoryA",
.patch = hook_RemoveDirectoryA,
.link = (void **) &next_RemoveDirectoryA,
}, {
.name = "RemoveDirectoryW",
.patch = hook_RemoveDirectoryW,
.link = (void **) &next_RemoveDirectoryW,
}
};
static bool path_hook_initted;
static CRITICAL_SECTION path_hook_lock;
static path_hook_t *path_hook_list;
static size_t path_hook_count;
HRESULT path_hook_push(path_hook_t hook)
{
path_hook_t *tmp;
HRESULT hr;
assert(hook != NULL);
path_hook_init();
EnterCriticalSection(&path_hook_lock);
tmp = realloc(
path_hook_list,
(path_hook_count + 1) * sizeof(path_hook_t));
if (tmp == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
path_hook_list = tmp;
path_hook_list[path_hook_count++] = hook;
hr = S_OK;
end:
LeaveCriticalSection(&path_hook_lock);
return hr;
}
static void path_hook_init(void)
{
/* Init is not thread safe because API hook init is not thread safe blah
blah blah you know the drill by now. */
if (path_hook_initted) {
return;
}
path_hook_initted = true;
InitializeCriticalSection(&path_hook_lock);
path_hook_insert_hooks(NULL);
}
void path_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
path_hook_syms,
_countof(path_hook_syms));
}
static BOOL path_transform_a(char **out, const char *src)
{
wchar_t *src_w;
size_t src_c;
wchar_t *dest_w;
char *dest_a;
size_t dest_s;
BOOL ok;
assert(out != NULL);
src_w = NULL;
dest_w = NULL;
dest_a = NULL;
*out = NULL;
if (src == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
ok = FALSE;
goto end;
}
/* Widen the path */
mbstowcs_s(&src_c, NULL, 0, src, 0);
src_w = malloc(src_c * sizeof(wchar_t));
if (src_w == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
ok = FALSE;
goto end;
}
mbstowcs_s(NULL, src_w, src_c, src, src_c - 1);
/* Try applying a path transform */
ok = path_transform_w(&dest_w, src_w); /* Take ownership! */
if (!ok || dest_w == NULL) {
goto end;
}
/* Narrow the transformed path */
wcstombs_s(&dest_s, NULL, 0, dest_w, 0);
dest_a = malloc(dest_s * sizeof(char));
if (dest_a == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
ok = FALSE;
goto end;
}
wcstombs_s(NULL, dest_a, dest_s, dest_w, dest_s - 1);
*out = dest_a; /* Relinquish ownership to caller! */
ok = TRUE;
end:
free(dest_w);
free(src_w);
return ok;
}
static BOOL path_transform_w(wchar_t **out, const wchar_t *src)
{
BOOL ok;
HRESULT hr;
wchar_t *dest;
size_t dest_c;
size_t i;
assert(out != NULL);
dest = NULL;
*out = NULL;
EnterCriticalSection(&path_hook_lock);
for (i = 0 ; i < path_hook_count ; i++) {
hr = path_hook_list[i](src, NULL, &dest_c);
if (FAILED(hr)) {
ok = hr_propagate_win32(hr, FALSE);
goto end;
}
if (hr == S_FALSE) {
continue;
}
dest = malloc(dest_c * sizeof(wchar_t));
if (dest == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
ok = FALSE;
goto end;
}
hr = path_hook_list[i](src, dest, &dest_c);
if (FAILED(hr)) {
ok = hr_propagate_win32(hr, FALSE);
goto end;
}
break;
}
*out = dest;
dest = NULL;
ok = TRUE;
end:
LeaveCriticalSection(&path_hook_lock);
free(dest);
return ok;
}
int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count)
{
size_t i;
wchar_t c1, c2;
assert(string1 != NULL);
assert(string2 != NULL);
for (i = 0; i < count && string1[i] && string2[i]; i++) {
c1 = towlower(string1[i]);
if (c1 == '/') {
c1 = '\\';
}
c2 = towlower(string2[i]);
if (c2 == '/') {
c2 = '\\';
}
if (c1 != c2) {
break;
}
}
return i == count ? 0 : string2[i] - string1[i];
}
/* Dumping ground for kernel32 file system ops whose path parameters we have to
hook into and translate. This list will grow over time as we go back and
fix up older games that don't pay attention to the mount point registry. */
static BOOL WINAPI hook_CreateDirectoryA(
const char *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes)
{
char *trans;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_CreateDirectoryA(
trans ? trans : lpFileName,
lpSecurityAttributes);
free(trans);
return ok;
}
static BOOL WINAPI hook_CreateDirectoryW(
const wchar_t *lpFileName,
SECURITY_ATTRIBUTES *lpSecurityAttributes)
{
wchar_t *trans;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_CreateDirectoryW(
trans ? trans : lpFileName,
lpSecurityAttributes);
free(trans);
return ok;
}
static BOOL WINAPI hook_CreateDirectoryExA(
const char *lpTemplateDirectory,
const char *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes)
{
char *trans;
BOOL ok;
ok = path_transform_a(&trans, lpNewDirectory);
if (!ok) {
return FALSE;
}
ok = next_CreateDirectoryExA(
lpTemplateDirectory,
trans ? trans : lpNewDirectory,
lpSecurityAttributes);
free(trans);
return ok;
}
static BOOL WINAPI hook_CreateDirectoryExW(
const wchar_t *lpTemplateDirectory,
const wchar_t *lpNewDirectory,
SECURITY_ATTRIBUTES *lpSecurityAttributes)
{
wchar_t *trans;
BOOL ok;
ok = path_transform_w(&trans, lpNewDirectory);
if (!ok) {
return FALSE;
}
ok = next_CreateDirectoryExW(
lpTemplateDirectory,
trans ? trans : lpNewDirectory,
lpSecurityAttributes);
free(trans);
return ok;
}
/* Don't pull in the entire iohook framework just for CreateFileA/CreateFileW */
static HANDLE WINAPI hook_CreateFileA(
const char *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
char *trans;
HANDLE result;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_CreateFileA(
trans ? trans : lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
free(trans);
return result;
}
static HANDLE WINAPI hook_CreateFileW(
const wchar_t *lpFileName,
uint32_t dwDesiredAccess,
uint32_t dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes,
uint32_t dwCreationDisposition,
uint32_t dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
wchar_t *trans;
HANDLE result;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_CreateFileW(
trans ? trans : lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
free(trans);
return result;
}
static HANDLE WINAPI hook_FindFirstFileA(
const char *lpFileName,
LPWIN32_FIND_DATAA lpFindFileData)
{
char *trans;
HANDLE result;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileA(trans ? trans : lpFileName, lpFindFileData);
free(trans);
return result;
}
static HANDLE WINAPI hook_FindFirstFileW(
const wchar_t *lpFileName,
LPWIN32_FIND_DATAW lpFindFileData)
{
wchar_t *trans;
HANDLE result;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileW(trans ? trans : lpFileName, lpFindFileData);
free(trans);
return result;
}
static HANDLE WINAPI hook_FindFirstFileExA(
const char *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags)
{
char *trans;
HANDLE result;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileExA(
trans ? trans : lpFileName,
fInfoLevelId,
lpFindFileData,
fSearchOp,
lpSearchFilter,
dwAdditionalFlags);
free(trans);
return result;
}
static HANDLE WINAPI hook_FindFirstFileExW(
const wchar_t *lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
void *lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
void *lpSearchFilter,
DWORD dwAdditionalFlags)
{
wchar_t *trans;
HANDLE result;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_HANDLE_VALUE;
}
result = next_FindFirstFileExW(
trans ? trans : lpFileName,
fInfoLevelId,
lpFindFileData,
fSearchOp,
lpSearchFilter,
dwAdditionalFlags);
free(trans);
return result;
}
static DWORD WINAPI hook_GetFileAttributesA(const char *lpFileName)
{
char *trans;
DWORD result;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_FILE_ATTRIBUTES;
}
result = next_GetFileAttributesA(trans ? trans : lpFileName);
free(trans);
return result;
}
static DWORD WINAPI hook_GetFileAttributesW(const wchar_t *lpFileName)
{
wchar_t *trans;
DWORD result;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_FILE_ATTRIBUTES;
}
result = next_GetFileAttributesW(trans ? trans : lpFileName);
free(trans);
return result;
}
static BOOL WINAPI hook_GetFileAttributesExA(
const char *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation)
{
char *trans;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return INVALID_FILE_ATTRIBUTES;
}
ok = next_GetFileAttributesExA(
trans ? trans : lpFileName,
fInfoLevelId,
lpFileInformation);
free(trans);
return ok;
}
static BOOL WINAPI hook_GetFileAttributesExW(
const wchar_t *lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
void *lpFileInformation)
{
wchar_t *trans;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return INVALID_FILE_ATTRIBUTES;
}
ok = next_GetFileAttributesExW(
trans ? trans : lpFileName,
fInfoLevelId,
lpFileInformation);
free(trans);
return ok;
}
static BOOL WINAPI hook_RemoveDirectoryA(const char *lpFileName)
{
char *trans;
BOOL ok;
ok = path_transform_a(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_RemoveDirectoryA(trans ? trans : lpFileName);
free(trans);
return ok;
}
static BOOL WINAPI hook_RemoveDirectoryW(const wchar_t *lpFileName)
{
wchar_t *trans;
BOOL ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_RemoveDirectoryW(trans ? trans : lpFileName);
free(trans);
return ok;
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
typedef HRESULT (*path_hook_t)(
const wchar_t *src,
wchar_t *dest,
size_t *count);
HRESULT path_hook_push(path_hook_t hook);
void path_hook_insert_hooks(HMODULE target);
int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count);
static inline bool path_is_separator_w(wchar_t c)
{
return c == L'\\' || c == L'/';
}
+891
View File
@@ -0,0 +1,891 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include "hook/table.h"
#include "hooklib/reg.h"
#include "util/dprintf.h"
#include "util/str.h"
struct reg_hook_key {
HKEY root;
const wchar_t *name;
const struct reg_hook_val *vals;
size_t nvals;
HKEY handle;
};
/* Helper functions */
static void reg_hook_init(void);
static LRESULT reg_hook_propagate_hr(HRESULT hr);
static struct reg_hook_key *reg_hook_match_key_locked(HKEY handle);
static const struct reg_hook_val *reg_hook_match_val_locked(
struct reg_hook_key *key,
const wchar_t *name);
static LSTATUS reg_hook_open_locked(
HKEY parent,
const wchar_t *name,
HKEY *out);
static LSTATUS reg_hook_query_val_locked(
struct reg_hook_key *key,
const wchar_t *name,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
/* API hooks */
static LSTATUS WINAPI hook_RegOpenKeyExW(
HKEY parent,
const wchar_t *name,
uint32_t flags,
uint32_t access,
HKEY *out);
static LSTATUS WINAPI hook_RegCreateKeyExW(
HKEY parent,
const wchar_t *name,
uint32_t reserved,
const wchar_t *class_,
uint32_t options,
uint32_t access,
const SECURITY_ATTRIBUTES *sa,
HKEY *out,
uint32_t *disposition);
static LSTATUS WINAPI hook_RegCloseKey(HKEY handle);
static LSTATUS WINAPI hook_RegQueryValueExA(
HKEY handle,
const char *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS WINAPI hook_RegQueryValueExW(
HKEY handle,
const wchar_t *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS WINAPI hook_RegSetValueExW(
HKEY handle,
const wchar_t *name,
uint32_t reserved,
uint32_t type,
const void *bytes,
uint32_t nbytes);
static LSTATUS WINAPI hook_RegGetValueW(
HKEY hkey,
LPCWSTR lpSubKey,
LPCWSTR lpValue,
uint32_t flags,
uint32_t *type,
void *pData,
uint32_t *numData
);
/* Link pointers */
static LSTATUS (WINAPI *next_RegOpenKeyExW)(
HKEY parent,
const wchar_t *name,
uint32_t flags,
uint32_t access,
HKEY *out);
static LSTATUS (WINAPI *next_RegCreateKeyExW)(
HKEY parent,
const wchar_t *name,
uint32_t reserved,
const wchar_t *class_,
uint32_t options,
uint32_t access,
const SECURITY_ATTRIBUTES *sa,
HKEY *out,
uint32_t *disposition);
static LSTATUS (WINAPI *next_RegCloseKey)(HKEY handle);
static LSTATUS (WINAPI *next_RegQueryValueExA)(
HKEY handle,
const char *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS (WINAPI *next_RegQueryValueExW)(
HKEY handle,
const wchar_t *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS (WINAPI *next_RegSetValueExW)(
HKEY handle,
const wchar_t *name,
uint32_t reserved,
uint32_t type,
const void *bytes,
uint32_t nbytes);
static LSTATUS (WINAPI *next_RegGetValueW)(
HKEY hkey,
LPCWSTR lpSubKey,
LPCWSTR lpValue,
uint32_t flags,
uint32_t *type,
void *pData,
uint32_t *numData
);
static const struct hook_symbol reg_hook_syms[] = {
{
.name = "RegOpenKeyExW",
.patch = hook_RegOpenKeyExW,
.link = (void **) &next_RegOpenKeyExW,
}, {
.name = "RegCreateKeyExW",
.patch = hook_RegCreateKeyExW,
.link = (void **) &next_RegCreateKeyExW,
}, {
.name = "RegCloseKey",
.patch = hook_RegCloseKey,
.link = (void **) &next_RegCloseKey,
}, {
.name = "RegQueryValueExA",
.patch = hook_RegQueryValueExA,
.link = (void **) &next_RegQueryValueExA,
}, {
.name = "RegQueryValueExW",
.patch = hook_RegQueryValueExW,
.link = (void **) &next_RegQueryValueExW,
}, {
.name = "RegSetValueExW",
.patch = hook_RegSetValueExW,
.link = (void **) &next_RegSetValueExW,
}, {
.name = "RegGetValueW",
.patch = hook_RegGetValueW,
.link = (void **) &next_RegGetValueW,
}
};
static bool reg_hook_initted;
static CRITICAL_SECTION reg_hook_lock;
static struct reg_hook_key *reg_hook_keys;
static size_t reg_hook_nkeys;
HRESULT reg_hook_push_key(
HKEY root,
const wchar_t *name,
const struct reg_hook_val *vals,
size_t nvals)
{
struct reg_hook_key *new_mem;
struct reg_hook_key *new_key;
HRESULT hr;
assert(root != NULL);
assert(name != NULL);
assert(vals != NULL || nvals == 0);
reg_hook_init();
/*dprintf("Pushing reg key %ls:\n", name);
for (int i = 0; i < nvals; i++) {
dprintf("\t%ls\n", vals[i].name);
} */
EnterCriticalSection(&reg_hook_lock);
new_mem = realloc(
reg_hook_keys,
(reg_hook_nkeys + 1) * sizeof(struct reg_hook_key));
if (new_mem == NULL) {
hr = E_OUTOFMEMORY;
goto end;
}
new_key = &new_mem[reg_hook_nkeys];
memset(new_key, 0, sizeof(*new_key));
new_key->root = root;
new_key->name = name; /* Expect this to be statically allocated */
new_key->vals = vals;
new_key->nvals = nvals;
reg_hook_keys = new_mem;
reg_hook_nkeys++;
hr = S_OK;
end:
LeaveCriticalSection(&reg_hook_lock);
return hr;
}
static void reg_hook_init(void)
{
if (reg_hook_initted) {
return;
}
reg_hook_initted = true;
InitializeCriticalSection(&reg_hook_lock);
dprintf("Reg hook init\n");
hook_table_apply(
NULL,
"advapi32.dll",
reg_hook_syms,
_countof(reg_hook_syms));
}
static LRESULT reg_hook_propagate_hr(HRESULT hr)
{
if (SUCCEEDED(hr)) {
return ERROR_SUCCESS;
} else if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
return HRESULT_CODE(hr);
} else {
return ERROR_GEN_FAILURE;
}
}
static struct reg_hook_key *reg_hook_match_key_locked(HKEY handle)
{
struct reg_hook_key *key;
size_t i;
if (handle == NULL || handle == INVALID_HANDLE_VALUE) {
return NULL;
}
for (i = 0 ; i < reg_hook_nkeys ; i++) {
key = &reg_hook_keys[i];
if (key->handle == handle) {
return key;
}
}
return NULL;
}
static const struct reg_hook_val *reg_hook_match_val_locked(
struct reg_hook_key *key,
const wchar_t *name)
{
const struct reg_hook_val *val;
size_t i;
/* Watch out for accesses to the key's default value */
if (name == NULL) {
name = L"";
}
for (i = 0 ; i < key->nvals ; i++) {
val = &key->vals[i];
if (wstr_ieq(val->name, name)) {
return val;
}
}
return NULL;
}
static LSTATUS reg_hook_open_locked(
HKEY parent,
const wchar_t *name,
HKEY *out)
{
struct reg_hook_key *key;
LSTATUS err;
size_t i;
*out = NULL;
for (i = 0 ; i < reg_hook_nkeys ; i++) {
/* Assume reg keys are referenced from a root key and not from some
intermediary key */
key = &reg_hook_keys[i];
if (key->root == parent && wstr_ieq(key->name, name)) {
break;
}
}
/* (Bail out if we didn't find anything; this causes the open/create call
to be passed onward down the hook chain) */
if (i >= reg_hook_nkeys) {
return ERROR_SUCCESS;
}
/* Assume only one handle will be open at a time */
if (key->handle != NULL) {
return ERROR_SHARING_VIOLATION;
}
/* Open a unique HKEY handle that we can use to identify accesses to
this virtual registry key. We open a read-only handle to an arbitrary
registry key that we can reliably assume exists and isn't one of the
hardcoded root handles. HKLM\SOFTWARE will suffice for this purpose. */
err = next_RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE",
0,
KEY_READ,
out);
if (err == ERROR_SUCCESS) {
key->handle = *out;
}
return err;
}
static LSTATUS WINAPI hook_RegOpenKeyExW(
HKEY parent,
const wchar_t *name,
uint32_t flags,
uint32_t access,
HKEY *out)
{
LSTATUS err;
if (out == NULL) {
return ERROR_INVALID_PARAMETER;
}
EnterCriticalSection(&reg_hook_lock);
err = reg_hook_open_locked(parent, name, out);
LeaveCriticalSection(&reg_hook_lock);
if (err == ERROR_SUCCESS) {
if (*out != NULL) {
//dprintf("Registry: Opened virtual key %S\n", name);
} else {
err = next_RegOpenKeyExW(parent, name, flags, access, out);
}
}
return err;
}
static LSTATUS WINAPI hook_RegCreateKeyExW(
HKEY parent,
const wchar_t *name,
uint32_t reserved,
const wchar_t *class_,
uint32_t options,
uint32_t access,
const SECURITY_ATTRIBUTES *sa,
HKEY *out,
uint32_t *disposition)
{
LSTATUS err;
if (out == NULL) {
return ERROR_INVALID_PARAMETER;
}
EnterCriticalSection(&reg_hook_lock);
err = reg_hook_open_locked(parent, name, out);
LeaveCriticalSection(&reg_hook_lock);
if (err == ERROR_SUCCESS) {
if (*out != NULL) {
//dprintf("Registry: Created virtual key %S\n", name);
} else {
err = next_RegCreateKeyExW(
parent,
name,
reserved,
class_,
options,
access,
sa,
out,
disposition);
}
}
return err;
}
static LSTATUS WINAPI hook_RegCloseKey(HKEY handle)
{
struct reg_hook_key *key;
size_t i;
EnterCriticalSection(&reg_hook_lock);
for (i = 0 ; i < reg_hook_nkeys ; i++) {
key = &reg_hook_keys[i];
if (key->handle == handle) {
//dprintf("Registry: Closed virtual key %S\n", key->name);
key->handle = NULL;
}
}
LeaveCriticalSection(&reg_hook_lock);
return next_RegCloseKey(handle);
}
static LSTATUS WINAPI hook_RegQueryValueExW(
HKEY handle,
const wchar_t *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes)
{
struct reg_hook_key *key;
LSTATUS err;
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(handle);
/* Check if this is a virtualized registry key */
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegQueryValueExW(
handle,
name,
reserved,
type,
bytes,
nbytes);
}
/* Call the factored out core of this function because RegQueryValueExA
has to be a blight upon my existence */
err = reg_hook_query_val_locked(key, name, type, bytes, nbytes);
LeaveCriticalSection(&reg_hook_lock);
return err;
}
/* now this right here is a pain in my ass */
static LSTATUS WINAPI hook_RegQueryValueExA(
HKEY handle,
const char *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes)
{
/* _s: sizeof, _c: _countof(), _w: widened */
struct reg_hook_key *key;
wchar_t *name_w;
size_t name_c;
wchar_t *content;
uint32_t content_s;
size_t content_c;
uint32_t type_site;
LSTATUS err;
name_w = NULL;
content = NULL;
/* Normalize inconvenient inputs */
if (name == NULL) {
name = "";
}
if (type == NULL) {
type = &type_site;
}
/* Look up key handle, early exit if no match */
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(handle);
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegQueryValueExA(
handle,
name,
reserved,
type,
bytes,
nbytes);
}
/* OK, first off we need to widen the name. This requires a temporary
buffer allocation. */
mbstowcs_s(&name_c, NULL, 0, name, 0);
name_w = malloc(name_c * sizeof(wchar_t));
if (name_w == NULL) {
err = ERROR_OUTOFMEMORY;
goto end;
}
mbstowcs_s(NULL, name_w, name_c, name, name_c - 1);
/* Next, check to see if the caller even cares about the content. We can
pass through if they don't. */
if (bytes == NULL && nbytes == NULL) {
err = reg_hook_query_val_locked(key, name_w, type, NULL, NULL);
goto end;
}
/* Next, we need to check the key type to see if it's REG_SZ. */
err = reg_hook_query_val_locked(key, name_w, type, NULL, NULL);
if (err != ERROR_SUCCESS) {
goto end;
}
/* If it is not REG_SZ then pass the content directly.
(We ignore the REG_MULTI_SZ case here). */
assert(*type != REG_MULTI_SZ);
if (*type != REG_SZ) {
err = reg_hook_query_val_locked(key, name_w, type, bytes, nbytes);
goto end;
}
/* Otherwise things get more complicated. First we must measure the wide-
character length of the value (hopefully said value does not change
under our feet, of course). */
err = reg_hook_query_val_locked(key, name_w, type, NULL, &content_s);
if (err != ERROR_SUCCESS) {
goto end;
}
/* Next, allocate a scratch buffer. Even if the caller doesn't supply an
output buffer we need to know the actual content to be able to size the
narrow version. */
content = malloc(content_s);
if (content == NULL) {
err = ERROR_OUTOFMEMORY;
goto end;
}
/* Get the data... */
err = reg_hook_query_val_locked(key, name_w, type, content, &content_s);
if (err != ERROR_SUCCESS) {
goto end;
}
/* Now size the corresponding narrow form and return it to the caller */
wcstombs_s(&content_c, NULL, 0, content, 0);
if (bytes != NULL) {
if (nbytes == NULL) {
err = ERROR_INVALID_PARAMETER;
goto end;
}
if (*nbytes < content_c) {
err = ERROR_MORE_DATA;
goto end;
}
wcstombs_s(NULL, bytes, *nbytes, content, content_c - 1);
}
if (nbytes != NULL) { /* It really should be, based on earlier checks ... */
*nbytes = content_c;
}
err = ERROR_SUCCESS;
end:
LeaveCriticalSection(&reg_hook_lock);
free(content);
free(name_w);
return err;
}
static LSTATUS reg_hook_query_val_locked(
struct reg_hook_key *key,
const wchar_t *name,
uint32_t *type,
void *bytes,
uint32_t *nbytes)
{
const struct reg_hook_val *val;
LSTATUS err;
HRESULT hr;
val = reg_hook_match_val_locked(key, name);
if (val != NULL) {
if (type != NULL) {
*type = val->type;
}
if (val->read != NULL) {
hr = val->read(bytes, nbytes);
err = reg_hook_propagate_hr(hr);
} else {
dprintf("Registry: %S: Val %S has no read handler\n",
key->name,
name);
err = ERROR_ACCESS_DENIED;
}
} else {
dprintf("Registry: Key %S: Val %S not found\n", key->name, name);
err = ERROR_FILE_NOT_FOUND;
}
return err;
}
static LSTATUS WINAPI hook_RegSetValueExW(
HKEY handle,
const wchar_t *name,
uint32_t reserved,
uint32_t type,
const void *bytes,
uint32_t nbytes)
{
struct reg_hook_key *key;
const struct reg_hook_val *val;
LSTATUS err;
HRESULT hr;
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(handle);
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegSetValueExW(
handle,
name,
reserved,
type,
bytes,
nbytes);
}
val = reg_hook_match_val_locked(key, name);
if (val != NULL) {
if (val->write != NULL) {
if (type != val->type) {
dprintf( "Registry: Key %S: Val %S: Type mismatch "
"(expected %i got %i)\n",
key->name,
name,
val->type,
type);
err = ERROR_ACCESS_DENIED;
} else {
dprintf("Registry: Write virtual key %S value %S\n",
key->name,
val->name);
hr = val->write(bytes, nbytes);
err = reg_hook_propagate_hr(hr);
}
} else {
/* No write handler (the common case), black-hole whatever gets
written. */
err = ERROR_SUCCESS;
}
} else {
dprintf("Registry: Key %S: Val %S not found\n", key->name, name);
err = ERROR_FILE_NOT_FOUND;
}
LeaveCriticalSection(&reg_hook_lock);
return err;
}
static LSTATUS WINAPI hook_RegGetValueW(
HKEY handle,
LPCWSTR subkey,
LPCWSTR name,
uint32_t flags,
uint32_t *type,
void *pData,
uint32_t *numData)
{
struct reg_hook_key *key;
HKEY tmp = NULL;
const struct reg_hook_val *val;
LSTATUS err;
EnterCriticalSection(&reg_hook_lock);
//dprintf("Registry: RegGetValueW lookup for %ls\\%ls\n", subkey, name);
if (subkey == NULL) {
key = reg_hook_match_key_locked(handle);
} else {
err = hook_RegOpenKeyExW(handle, subkey, flags, 1, &tmp);
key = reg_hook_match_key_locked(tmp);
}
//dprintf("Registry: RegGetValueW key is %ls", key->name);
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
dprintf("Registry: RegGetValueW Failed to find %ls\\%ls, passing on\n", subkey, name);
return next_RegGetValueW(
handle,
subkey,
name,
flags,
type,
pData,
numData);
}
val = reg_hook_match_val_locked(key, name);
if (val != NULL) {
//dprintf("Registry: RegGetValueW found %ls\\%ls!\n", subkey, name);
if (val->read != NULL) {
val->read(pData, numData);
if (tmp != NULL) {
hook_RegCloseKey(tmp);
}
LeaveCriticalSection(&reg_hook_lock);
err = ERROR_SUCCESS;
return err;
}
}
LeaveCriticalSection(&reg_hook_lock);
err = ERROR_NOT_FOUND;
return err;
}
HRESULT reg_hook_read_bin(
void *bytes,
uint32_t *nbytes,
const void *src_bytes,
size_t src_nbytes)
{
assert(src_bytes != NULL || src_nbytes == 0);
if (bytes != NULL) {
if (nbytes == NULL || *nbytes < src_nbytes) {
return HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
memcpy(bytes, src_bytes, src_nbytes);
}
if (nbytes != NULL) {
*nbytes = src_nbytes;
}
return S_OK;
}
HRESULT reg_hook_read_u32(
void *bytes,
uint32_t *nbytes,
uint32_t src)
{
if (bytes != NULL) {
if (nbytes == NULL || *nbytes < sizeof(uint32_t)) {
return HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
memcpy(bytes, &src, sizeof(uint32_t));
}
if (nbytes != NULL) {
*nbytes = sizeof(uint32_t);
}
return S_OK;
}
HRESULT reg_hook_read_wstr(
void *bytes,
uint32_t *nbytes,
const wchar_t *src)
{
size_t src_nbytes;
assert(src != NULL);
src_nbytes = (wcslen(src) + 1) * sizeof(wchar_t);
if (bytes != NULL) {
if (nbytes == NULL || *nbytes < src_nbytes) {
return HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
memcpy(bytes, src, src_nbytes);
}
if (nbytes != NULL) {
*nbytes = src_nbytes;
}
return S_OK;
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
struct reg_hook_val {
const wchar_t *name;
HRESULT (*read)(void *bytes, uint32_t *nbytes);
HRESULT (*write)(const void *bytes, uint32_t nbytes);
uint32_t type;
};
HRESULT reg_hook_push_key(
HKEY root,
const wchar_t *name,
const struct reg_hook_val *vals,
size_t nvals);
HRESULT reg_hook_read_bin(
void *bytes,
uint32_t *nbytes,
const void *src_bytes,
size_t src_nbytes);
HRESULT reg_hook_read_u32(
void *bytes,
uint32_t *nbytes,
uint32_t src);
HRESULT reg_hook_read_wstr(
void *bytes,
uint32_t *nbytes,
const wchar_t *src);
+340
View File
@@ -0,0 +1,340 @@
#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;
hr = S_OK;
end:
LeaveCriticalSection(&setupapi_lock);
return hr;
}
static void setupapi_hook_init()
{
if (setupapi_initted) {
return;
}
setupapi_hook_insert_hooks(NULL);
InitializeCriticalSection(&setupapi_lock);
setupapi_initted = true;
}
void setupapi_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"setupapi.dll",
setupapi_syms,
_countof(setupapi_syms));
}
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);
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include <windows.h>
#include <stddef.h>
HRESULT setupapi_add_phantom_dev(const GUID *iface_class, const wchar_t *path);
void setupapi_hook_insert_hooks(HMODULE target);
+249
View File
@@ -0,0 +1,249 @@
#include <windows.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include "hook/pe.h"
#include "hooklib/spike.h"
#include "util/dprintf.h"
static void spike_hook_read_config(const wchar_t *spike_file);
/* 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 wchar_t *ini_file)
{
wchar_t module[MAX_PATH];
wchar_t path[MAX_PATH];
const wchar_t *basename;
const wchar_t *slash;
assert(ini_file != NULL);
/* Get the filename (strip path) of the host EXE */
GetModuleFileNameW(NULL, module, _countof(module));
slash = wcsrchr(module, L'\\');
if (slash != NULL) {
basename = slash + 1;
} else {
basename = module;
}
/* Check our INI file to see if any spikes are configured for this EXE.
Normally we separate out config reading into a separate module... */
GetPrivateProfileStringW(
L"spike",
basename,
L"",
path,
_countof(path),
ini_file);
if (path[0] != L'\0') {
dprintf("Spiking %S using config from %S\n", basename, path);
spike_hook_read_config(path);
}
}
static void spike_hook_read_config(const wchar_t *spike_file)
{
int match;
int count;
int rva;
char line[80];
char *ret;
FILE *f;
f = _wfopen(spike_file, L"r");
if (f == NULL) {
dprintf("Error opening spike file %S\n", spike_file);
return;
}
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);
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <stddef.h>
void spike_hook_init(const wchar_t *ini_file);