Files
bemanitools/src/main/bio2emu/setupapi.c
icex2 e4a221e15b refactor: Entire code base, thread and log usage
Boils down to:
- Include headers
- Reduce boiler plate with helpers
- Swap out explicit usages with core API layer
  and ensure the right API is configured beforehand
2024-02-25 09:30:53 +01:00

381 lines
11 KiB
C

#define LOG_MODULE "setupapi-hook"
#include <initguid.h>
#include <windows.h>
#include <cfgmgr32.h>
#include <setupapi.h>
#include "core/log.h"
#include "hook/table.h"
#include "bio2emu/emu.h"
#include "bio2emu/setupapi.h"
#include "util/defs.h"
#include "util/str.h"
#include "util/time.h"
static struct array *bio2_assigned_ports = NULL;
DEFINE_GUID(
GUID_COM_BUS_ENUMERATOR,
0x4D36E978,
0xE325,
0x11CE,
0xBF,
0xC1,
0x08,
0x00,
0x2B,
0xE1,
0x03,
0x18);
#define MAX_INSTANCES_HOOKED 16
#define CUSTOM_DEVICE_INSTANCE 0x3113ca70
#define CUSTOM_DEVICE_INSTANCE_MASK 0xfffffff0
#define CUSTOM_DEVICE_INSTANCE_IDXMASK 0x0000000f
static void *CUSTOM_DEVICE_HANDLE;
static struct HKEY__ CUSTOM_REGISTRY_HANDLE[MAX_INSTANCES_HOOKED];
// end result should be like BIO2(VIDEO)(COM4)
static const char DEVICE_PROPERTY_VALUE[] = "BIO2(VIDEO)(";
static const size_t DEVICE_PROPERTY_LENGTH = 12;
static const char devpath[] = "USB\\VID_5730&PID_804C&MI_00\\000";
static const size_t devpathsize = 32;
// check if HKEY handle is one of ours
static BOOL check_if_match(HKEY ptr, HKEY base)
{
return (ptr >= &base[0]) && (ptr <= &base[MAX_INSTANCES_HOOKED - 1]);
}
// turn HKEY handle back into idx
static size_t get_match_index(HKEY ptr, HKEY base)
{
for (size_t i = 0; i < MAX_INSTANCES_HOOKED; ++i) {
if (ptr == &base[i]) {
return i;
}
}
return -1;
}
// check if devinst is on of ours (can't use pointer since this is a DWORD)
static BOOL check_instances_limit(DWORD devinst)
{
if ((devinst & CUSTOM_DEVICE_INSTANCE_MASK) != CUSTOM_DEVICE_INSTANCE) {
return false;
}
size_t num = devinst & CUSTOM_DEVICE_INSTANCE_IDXMASK;
if (num >= MAX_INSTANCES_HOOKED) {
return false;
}
if (num >= bio2_assigned_ports->nitems) {
return false;
}
return true;
}
//
// hooked functions
//
static BOOL my_SetupDiDestroyDeviceInfoList(HDEVINFO DeviceInfoSet);
static BOOL (*real_SetupDiDestroyDeviceInfoList)(HDEVINFO DeviceInfoSet);
static BOOL my_SetupDiEnumDeviceInfo(
HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData);
static BOOL (*real_SetupDiEnumDeviceInfo)(
HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData);
static HKEY my_SetupDiOpenDevRegKey(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
DWORD Scope,
DWORD HwProfile,
DWORD KeyType,
REGSAM samDesired);
static HKEY (*real_SetupDiOpenDevRegKey)(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
DWORD Scope,
DWORD HwProfile,
DWORD KeyType,
REGSAM samDesired);
static BOOL my_SetupDiGetDeviceRegistryPropertyA(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
DWORD Property,
PDWORD PropertyRegDataType,
PBYTE PropertyBuffer,
DWORD PropertyBufferSize,
PDWORD RequiredSize);
static BOOL (*real_SetupDiGetDeviceRegistryPropertyA)(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
DWORD Property,
PDWORD PropertyRegDataType,
PBYTE PropertyBuffer,
DWORD PropertyBufferSize,
PDWORD RequiredSize);
static BOOL my_SetupDiGetDeviceInfoListDetailA(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_LIST_DETAIL_DATA_A DeviceInfoSetDetailData);
static BOOL (*real_SetupDiGetDeviceInfoListDetailA)(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_LIST_DETAIL_DATA_A DeviceInfoSetDetailData);
static HDEVINFO my_SetupDiGetClassDevsA(
CONST GUID *ClassGuid, PCSTR Enumerator, HWND hwndParent, DWORD Flags);
static HDEVINFO (*real_SetupDiGetClassDevsA)(
CONST GUID *ClassGuid, PCSTR Enumerator, HWND hwndParent, DWORD Flags);
static CONFIGRET my_CM_Get_Device_IDA(
DEVINST dnDevInst, PSTR Buffer, ULONG BufferLen, ULONG ulFlags);
static CONFIGRET (*real_CM_Get_Device_IDA)(
DEVINST dnDevInst, PSTR Buffer, ULONG BufferLen, ULONG ulFlags);
static const struct hook_symbol bio2emu_setupapi_syms[] = {
{.name = "SetupDiDestroyDeviceInfoList",
.patch = my_SetupDiDestroyDeviceInfoList,
.link = (void **) &real_SetupDiDestroyDeviceInfoList},
{.name = "SetupDiEnumDeviceInfo",
.patch = my_SetupDiEnumDeviceInfo,
.link = (void **) &real_SetupDiEnumDeviceInfo},
{.name = "SetupDiOpenDevRegKey",
.patch = my_SetupDiOpenDevRegKey,
.link = (void **) &real_SetupDiOpenDevRegKey},
{.name = "SetupDiGetDeviceRegistryPropertyA",
.patch = my_SetupDiGetDeviceRegistryPropertyA,
.link = (void **) &real_SetupDiGetDeviceRegistryPropertyA},
{.name = "SetupDiGetDeviceInfoListDetailA",
.patch = my_SetupDiGetDeviceInfoListDetailA,
.link = (void **) &real_SetupDiGetDeviceInfoListDetailA},
{.name = "SetupDiGetClassDevsA",
.patch = my_SetupDiGetClassDevsA,
.link = (void **) &real_SetupDiGetClassDevsA},
{.name = "CM_Get_Device_IDA",
.patch = my_CM_Get_Device_IDA,
.link = (void **) &real_CM_Get_Device_IDA},
};
static LSTATUS my_RegQueryValueExA(
HKEY hKey,
LPCSTR lpValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData);
static LSTATUS (*real_RegQueryValueExA)(
HKEY hKey,
LPCSTR lpValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData);
static const struct hook_symbol bio2emu_Advapi32_syms[] = {
{.name = "RegQueryValueExA",
.patch = my_RegQueryValueExA,
.link = (void **) &real_RegQueryValueExA},
};
static BOOL my_SetupDiDestroyDeviceInfoList(HDEVINFO DeviceInfoSet)
{
if (DeviceInfoSet == &CUSTOM_DEVICE_HANDLE) {
log_info("Inside: %s", __FUNCTION__);
return true;
}
return real_SetupDiDestroyDeviceInfoList(DeviceInfoSet);
}
static BOOL my_SetupDiEnumDeviceInfo(
HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData)
{
if (DeviceInfoSet == &CUSTOM_DEVICE_HANDLE) {
log_info("%s: Loaded idx %ld", __FUNCTION__, MemberIndex);
if (MemberIndex < bio2_assigned_ports->nitems) {
DeviceInfoData->DevInst = CUSTOM_DEVICE_INSTANCE | MemberIndex;
return true;
}
return false;
}
return real_SetupDiEnumDeviceInfo(
DeviceInfoSet, MemberIndex, DeviceInfoData);
}
static HKEY my_SetupDiOpenDevRegKey(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
DWORD Scope,
DWORD HwProfile,
DWORD KeyType,
REGSAM samDesired)
{
if (DeviceInfoSet == &CUSTOM_DEVICE_HANDLE) {
if (check_instances_limit(DeviceInfoData->DevInst)) {
log_info("%s: matched instance", __FUNCTION__);
return &CUSTOM_REGISTRY_HANDLE
[DeviceInfoData->DevInst & CUSTOM_DEVICE_INSTANCE_IDXMASK];
}
}
return real_SetupDiOpenDevRegKey(
DeviceInfoSet, DeviceInfoData, Scope, HwProfile, KeyType, samDesired);
}
static BOOL my_SetupDiGetDeviceRegistryPropertyA(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
DWORD Property,
PDWORD PropertyRegDataType,
PBYTE PropertyBuffer,
DWORD PropertyBufferSize,
PDWORD RequiredSize)
{
if (DeviceInfoSet == &CUSTOM_DEVICE_HANDLE) {
if (check_instances_limit(DeviceInfoData->DevInst)) {
struct bio2emu_port *selected_port = *array_item(
struct bio2emu_port *,
bio2_assigned_ports,
DeviceInfoData->DevInst & CUSTOM_DEVICE_INSTANCE_IDXMASK);
size_t portname_len = strlen(selected_port->port);
size_t required_size =
(DEVICE_PROPERTY_LENGTH + portname_len + 1 +
1); // + 1 for ')', + 1 for NULL
if (PropertyBuffer && (PropertyBufferSize >= required_size)) {
char *PropBuffStr = (char *) PropertyBuffer;
strcpy(PropBuffStr, DEVICE_PROPERTY_VALUE);
strcpy(
PropBuffStr + DEVICE_PROPERTY_LENGTH, selected_port->port);
strcpy(
PropBuffStr + DEVICE_PROPERTY_LENGTH + portname_len, ")");
log_info(
"%s: Done copying property name [%s]",
__FUNCTION__,
PropBuffStr);
} else {
log_info("%s: Returning size", __FUNCTION__);
*RequiredSize = required_size;
}
}
log_info("%s: STUB RETURN", __FUNCTION__);
return true;
}
return real_SetupDiGetDeviceRegistryPropertyA(
DeviceInfoSet,
DeviceInfoData,
Property,
PropertyRegDataType,
PropertyBuffer,
PropertyBufferSize,
RequiredSize);
}
static BOOL my_SetupDiGetDeviceInfoListDetailA(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_LIST_DETAIL_DATA_A DeviceInfoSetDetailData)
{
if (DeviceInfoSet == &CUSTOM_DEVICE_HANDLE) {
log_info("Inside: %s", __FUNCTION__);
return true;
}
return real_SetupDiGetDeviceInfoListDetailA(
DeviceInfoSet, DeviceInfoSetDetailData);
}
static HDEVINFO my_SetupDiGetClassDevsA(
CONST GUID *ClassGuid, PCSTR Enumerator, HWND hwndParent, DWORD Flags)
{
if (ClassGuid) {
if (IsEqualGUID(ClassGuid, &GUID_COM_BUS_ENUMERATOR)) {
log_info("Inside: %s", __FUNCTION__);
return &CUSTOM_DEVICE_HANDLE;
}
}
return real_SetupDiGetClassDevsA(ClassGuid, Enumerator, hwndParent, Flags);
}
static LSTATUS my_RegQueryValueExA(
HKEY hKey,
LPCSTR lpValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData)
{
if (check_if_match(hKey, CUSTOM_REGISTRY_HANDLE)) {
if (strcmp(lpValueName, "PortName") == 0) {
if (lpData) {
size_t portidx = get_match_index(hKey, CUSTOM_REGISTRY_HANDLE);
struct bio2emu_port *selected_port = *array_item(
struct bio2emu_port *, bio2_assigned_ports, portidx);
strncpy((char *) lpData, selected_port->port, *lpcbData);
log_info("%s: Queried %s", __FUNCTION__, selected_port->port);
return ERROR_SUCCESS;
}
return ERROR_MORE_DATA;
}
}
return real_RegQueryValueExA(
hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
}
static CONFIGRET my_CM_Get_Device_IDA(
DEVINST dnDevInst, PSTR Buffer, ULONG BufferLen, ULONG ulFlags)
{
if (Buffer && BufferLen > devpathsize) {
if (check_instances_limit(dnDevInst)) {
log_info("%s: Injecting custom parent ID for BIO2", __FUNCTION__);
strcpy(Buffer, devpath);
Buffer[devpathsize - 2] =
'0' + (dnDevInst & CUSTOM_DEVICE_INSTANCE_IDXMASK);
log_info("%s: %s", __FUNCTION__, Buffer);
return CR_SUCCESS;
}
}
return real_CM_Get_Device_IDA(dnDevInst, Buffer, BufferLen, ulFlags);
}
void bio2emu_setupapi_hook_init(struct array *bio2_ports)
{
bio2_assigned_ports = bio2_ports;
hook_table_apply(
NULL,
"setupapi.dll",
bio2emu_setupapi_syms,
lengthof(bio2emu_setupapi_syms));
hook_table_apply(
NULL,
"Advapi32.dll",
bio2emu_Advapi32_syms,
lengthof(bio2emu_Advapi32_syms));
log_info("Inserted setupapi hooks");
}