mirror of
https://gitea.tendokyu.moe/TeamTofuShop/bananatools.git
synced 2026-10-10 00:08:32 +03:00
Initial Commit
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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(®_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(®_hook_lock);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static void reg_hook_init(void)
|
||||
{
|
||||
if (reg_hook_initted) {
|
||||
return;
|
||||
}
|
||||
|
||||
reg_hook_initted = true;
|
||||
InitializeCriticalSection(®_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 = ®_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 = ®_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(®_hook_lock);
|
||||
err = reg_hook_open_locked(parent, name, out);
|
||||
LeaveCriticalSection(®_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(®_hook_lock);
|
||||
err = reg_hook_open_locked(parent, name, out);
|
||||
LeaveCriticalSection(®_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(®_hook_lock);
|
||||
|
||||
for (i = 0 ; i < reg_hook_nkeys ; i++) {
|
||||
key = ®_hook_keys[i];
|
||||
|
||||
if (key->handle == handle) {
|
||||
//dprintf("Registry: Closed virtual key %S\n", key->name);
|
||||
key->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(®_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(®_hook_lock);
|
||||
|
||||
key = reg_hook_match_key_locked(handle);
|
||||
|
||||
/* Check if this is a virtualized registry key */
|
||||
|
||||
if (key == NULL) {
|
||||
LeaveCriticalSection(®_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(®_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(®_hook_lock);
|
||||
key = reg_hook_match_key_locked(handle);
|
||||
|
||||
if (key == NULL) {
|
||||
LeaveCriticalSection(®_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(®_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(®_hook_lock);
|
||||
|
||||
key = reg_hook_match_key_locked(handle);
|
||||
|
||||
if (key == NULL) {
|
||||
LeaveCriticalSection(®_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(®_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(®_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(®_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(®_hook_lock);
|
||||
err = ERROR_SUCCESS;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(®_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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void spike_hook_init(const wchar_t *ini_file);
|
||||
Reference in New Issue
Block a user