refactor all common parts and games

This commit is contained in:
Dniel97
2025-04-17 20:04:17 +02:00
parent a6126bf290
commit 66a53dd2de
496 changed files with 233 additions and 344 deletions
+182
View File
@@ -0,0 +1,182 @@
#include <windows.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "hook/table.h"
#include "hooklib/dll.h"
#include "hooklib/reg.h"
#include "platform/amvideo.h"
#include "util/dprintf.h"
/* Hook functions */
static HRESULT amvideo_reg_read_name(void *bytes, uint32_t *nbytes);
static HRESULT amvideo_reg_read_port_X(void *bytes, uint32_t *nbytes);
static HRESULT amvideo_reg_read_resolution_1(void *bytes, uint32_t *nbytes);
static HRESULT amvideo_reg_read_setting(void *bytes, uint32_t *nbytes);
static HRESULT amvideo_reg_read_use_segatiming(void *bytes, uint32_t *nbytes);
static const wchar_t amvideo_dll_name[] = L"$amvideo";
static const struct reg_hook_val amvideo_reg_vals[] = {
{
.name = L"name",
.read = amvideo_reg_read_name,
.type = REG_SZ,
},
{
.name = L"name_x86",
.read = amvideo_reg_read_name,
.type = REG_SZ,
}
};
static const struct reg_hook_val amvideo_reg_mode_vals[] = {
{
.name = L"monitor_setting_1",
.read = amvideo_reg_read_setting,
.type = REG_SZ,
}, {
.name = L"monitor_setting_2",
.read = amvideo_reg_read_setting,
.type = REG_SZ,
}, {
.name = L"port_1",
.read = amvideo_reg_read_port_X,
.type = REG_DWORD
}, {
.name = L"port_2",
.read = amvideo_reg_read_port_X,
.type = REG_DWORD
}, {
.name = L"port_3",
.read = amvideo_reg_read_port_X,
.type = REG_DWORD
}, {
.name = L"port_4",
.read = amvideo_reg_read_port_X,
.type = REG_DWORD
}, {
.name = L"port_5",
.read = amvideo_reg_read_port_X,
.type = REG_DWORD
}, {
.name = L"port_6",
.read = amvideo_reg_read_port_X,
.type = REG_DWORD
}, {
.name = L"port_7",
.read = amvideo_reg_read_port_X,
.type = REG_DWORD
}, {
.name = L"port_8",
.read = amvideo_reg_read_port_X,
.type = REG_DWORD
}, {
.name = L"resolution_1",
.read = amvideo_reg_read_resolution_1,
.type = REG_SZ,
}, {
.name = L"use_segatiming",
.read = amvideo_reg_read_use_segatiming,
.type = REG_DWORD,
}
};
HRESULT amvideo_hook_init(const struct amvideo_config *cfg, HMODULE redir_mod)
{
HRESULT hr;
assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty\\amVideo",
amvideo_reg_vals,
_countof(amvideo_reg_vals));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty\\sgsetdisplaysetting\\CurrentSetting",
amvideo_reg_mode_vals,
_countof(amvideo_reg_mode_vals));
if (FAILED(hr)) {
return hr;
}
hr = dll_hook_push(redir_mod, amvideo_dll_name);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
int amDllVideoOpen(void *ctx)
{
dprintf("AmVideo: %s\n", __func__);
return 0;
}
int amDllVideoClose(void *ctx)
{
dprintf("AmVideo: %s\n", __func__);
return 0;
}
int amDllVideoSetResolution(void *ctx, void *param)
{
dprintf("AmVideo: %s\n", __func__);
return 0;
}
int amDllVideoGetVBiosVersion(void *ctx, char *dest, size_t nchars)
{
dprintf("AmVideo: %s\n", __func__);
strcpy(dest, "01.02.03.04.05");
return 0;
}
static HRESULT amvideo_reg_read_name(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, amvideo_dll_name);
}
static HRESULT amvideo_reg_read_port_X(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 1);
}
static HRESULT amvideo_reg_read_resolution_1(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"1920x1080");
}
static HRESULT amvideo_reg_read_setting(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"0");
}
static HRESULT amvideo_reg_read_use_segatiming(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 0);
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct amvideo_config {
bool enable;
};
HRESULT amvideo_hook_init(const struct amvideo_config *cfg, HMODULE redir_mod);
+269
View File
@@ -0,0 +1,269 @@
#include <windows.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include "hook/table.h"
#include "hook/procaddr.h"
#include "platform/clock.h"
#include "util/dprintf.h"
static void WINAPI my_GetSystemTimeAsFileTime(FILETIME *out);
static BOOL WINAPI my_GetLocalTime(SYSTEMTIME *out);
static BOOL WINAPI my_GetSystemTime(SYSTEMTIME *out);
static DWORD WINAPI my_GetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo);
static BOOL WINAPI my_SetLocalTime(SYSTEMTIME *in);
static BOOL WINAPI my_SetSystemTime(SYSTEMTIME *in);
static BOOL WINAPI my_SetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo);
static BOOL (WINAPI * next_GetSystemTimeAsFileTime)(FILETIME *out);
static int64_t clock_current_day;
static bool clock_timezone;
static bool clock_time_warp;
static bool clock_writeable;
static const struct hook_symbol clock_base_hook_syms[] = {
{
.name = "GetSystemTimeAsFileTime",
.patch = my_GetSystemTimeAsFileTime,
.link = (void **) &next_GetSystemTimeAsFileTime,
}
};
static const struct hook_symbol clock_read_hook_syms[] = {
{
.name = "GetLocalTime",
.patch = my_GetLocalTime,
}, {
.name = "GetSystemTime",
.patch = my_GetSystemTime,
}, {
.name = "GetTimeZoneInformation",
.patch = my_GetTimeZoneInformation,
},
};
static const struct hook_symbol clock_write_hook_syms[] = {
{
.name = "SetLocalTime",
.patch = my_SetLocalTime,
}, {
.name = "SetSystemTime",
.patch = my_SetSystemTime,
}, {
.name = "SetTimeZoneInformation",
.patch = my_SetTimeZoneInformation,
},
};
/* FILETIME is expressed in 100ns i.e. 0.1us i.e. 10^-7 sec units.
No official name for these units is given so let's call them "jiffies". */
#define jiffies_per_sec 10000000LL
#define jiffies_per_hour (jiffies_per_sec * 3600LL)
#define jiffies_per_day (jiffies_per_hour * 24LL)
static void WINAPI my_GetSystemTimeAsFileTime(FILETIME *out)
{
FILETIME in;
int64_t day;
int64_t real_jiffies;
int64_t real_jiffies_biased;
int64_t real_time;
int64_t fake_time;
int64_t fake_jiffies_biased;
int64_t fake_jiffies;
if (!clock_time_warp) {
next_GetSystemTimeAsFileTime(out);
return;
}
if (out == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return;
}
/* Get and convert real jiffies */
next_GetSystemTimeAsFileTime(&in);
real_jiffies = (((int64_t) in.dwHighDateTime) << 32) | in.dwLowDateTime;
/* Keepout period is JST [02:00, 07:00), which is equivalent to
UTC [17:00, 22:00). Bias UTC forward by 2 hours, changing this interval
to [19:00, 00:00) to make the math easier. We revert this bias later. */
real_jiffies_biased = real_jiffies + 2LL * jiffies_per_hour;
/* Split date and time */
day = real_jiffies_biased / jiffies_per_day;
real_time = real_jiffies_biased % jiffies_per_day;
/* Debug log */
if (clock_current_day != 0 && clock_current_day != day) {
dprintf("\n*** CLOCK JUMP! ***\n\n");
}
clock_current_day = day;
/* We want to skip the final five hours of our UTC+2 biased reference frame,
so scale time-of-day by 19/24. */
fake_time = (real_time * 19LL) / 24LL;
/* Un-split date and time */
fake_jiffies_biased = day * jiffies_per_day + fake_time;
/* Revert bias */
fake_jiffies = fake_jiffies_biased - 2LL * jiffies_per_hour;
/* Return result */
out->dwLowDateTime = fake_jiffies;
out->dwHighDateTime = fake_jiffies >> 32;
}
static BOOL WINAPI my_GetLocalTime(SYSTEMTIME *out)
{
ULARGE_INTEGER arith;
FILETIME linear;
/* Force JST */
my_GetSystemTimeAsFileTime(&linear);
arith.LowPart = linear.dwLowDateTime;
arith.HighPart = linear.dwHighDateTime;
arith.QuadPart += 9ULL * jiffies_per_hour;
linear.dwLowDateTime = arith.LowPart;
linear.dwHighDateTime = arith.HighPart;
return FileTimeToSystemTime(&linear, out);
}
static BOOL WINAPI my_GetSystemTime(SYSTEMTIME *out)
{
FILETIME linear;
BOOL ok;
my_GetSystemTimeAsFileTime(&linear);
ok = FileTimeToSystemTime(&linear, out);
if (!ok) {
return ok;
}
#if defined(LOG_CLOCK)
static int last_second;
if (out->wSecond != last_second) {
dprintf("%04i/%02i/%02i %02i:%02i:%02i\n",
out->wYear,
out->wMonth,
out->wDay,
out->wHour,
out->wMinute,
out->wSecond);
}
last_second = out->wSecond;
#endif
return TRUE;
}
static DWORD WINAPI my_GetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo)
{
dprintf("Clock: Returning JST timezone\n");
if (tzinfo == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return TIME_ZONE_ID_INVALID;
}
/* Force JST (UTC+9), SEGA games malfunction in any other time zone.
Strings and boundary times don't matter, we only set the offset. */
memset(tzinfo, 0, sizeof(*tzinfo));
tzinfo->Bias = -9 * 60;
SetLastError(ERROR_SUCCESS);
/* "Unknown" here means that this region does not observe DST */
return TIME_ZONE_ID_UNKNOWN;
}
static BOOL WINAPI my_SetLocalTime(SYSTEMTIME *in)
{
dprintf("Clock: Blocked local time update\n");
return TRUE;
}
static BOOL WINAPI my_SetSystemTime(SYSTEMTIME *in)
{
dprintf("Clock: Blocked system time update\n");
return TRUE;
}
static BOOL WINAPI my_SetTimeZoneInformation(TIME_ZONE_INFORMATION *in)
{
dprintf("Clock: Blocked timezone update\n");
return TRUE;
}
HRESULT clock_hook_init(const struct clock_config *cfg)
{
assert(cfg != NULL);
clock_timezone = cfg->timezone;
clock_time_warp = cfg->timewarp;
clock_writeable = cfg->writeable;
clock_hook_insert_hooks(NULL);
return S_OK;
}
void clock_hook_insert_hooks(HMODULE target) {
if (clock_timezone || clock_time_warp || !clock_writeable) {
/* All the clock hooks require the core GSTAFT hook to be installed */
/* Note the ! up there btw. */
hook_table_apply(
target,
"kernel32.dll",
clock_base_hook_syms,
_countof(clock_base_hook_syms));
}
if (clock_timezone) {
hook_table_apply(
target,
"kernel32.dll",
clock_read_hook_syms,
_countof(clock_read_hook_syms));
}
if (!clock_writeable) {
/* Install hook if this config parameter is FALSE! */
hook_table_apply(
target,
"kernel32.dll",
clock_write_hook_syms,
_countof(clock_write_hook_syms));
}
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct clock_config {
bool timezone;
bool timewarp;
bool writeable;
};
HRESULT clock_hook_init(const struct clock_config *cfg);
void clock_hook_insert_hooks(HMODULE target);
+364
View File
@@ -0,0 +1,364 @@
#include <windows.h>
#include <winsock2.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "platform/amvideo.h"
#include "platform/clock.h"
#include "platform/config.h"
#include "platform/dns.h"
#include "platform/epay.h"
#include "platform/hwmon.h"
#include "platform/hwreset.h"
#include "platform/misc.h"
#include "platform/netenv.h"
#include "platform/nusec.h"
#include "platform/pcbid.h"
#include "platform/platform.h"
#include "platform/vfs.h"
#include "platform/system.h"
void platform_config_load(struct platform_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
amvideo_config_load(&cfg->amvideo, filename);
clock_config_load(&cfg->clock, filename);
dns_config_load(&cfg->dns, filename);
epay_config_load(&cfg->epay, filename);
hwmon_config_load(&cfg->hwmon, filename);
hwreset_config_load(&cfg->hwreset, filename);
misc_config_load(&cfg->misc, filename);
pcbid_config_load(&cfg->pcbid, filename);
netenv_config_load(&cfg->netenv, filename);
nusec_config_load(&cfg->nusec, filename);
vfs_config_load(&cfg->vfs, filename);
system_config_load(&cfg->system, filename);
}
void amvideo_config_load(struct amvideo_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"amvideo", L"enable", 1, filename);
}
void clock_config_load(struct clock_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->timezone = GetPrivateProfileIntW(L"clock", L"timezone", 1, filename);
cfg->timewarp = GetPrivateProfileIntW(L"clock", L"timewarp", 0, filename);
cfg->writeable = GetPrivateProfileIntW(
L"clock",
L"writeable",
0,
filename);
}
void dns_config_load(struct dns_config *cfg, const wchar_t *filename)
{
wchar_t default_[128];
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"dns", L"enable", 1, filename);
GetPrivateProfileStringW(
L"dns",
L"default",
L"localhost",
default_,
_countof(default_),
filename);
GetPrivateProfileStringW(
L"dns",
L"router",
default_,
cfg->router,
_countof(cfg->router),
filename);
GetPrivateProfileStringW(
L"dns",
L"startup",
default_,
cfg->startup,
_countof(cfg->startup),
filename);
GetPrivateProfileStringW(
L"dns",
L"billing",
default_,
cfg->billing,
_countof(cfg->billing),
filename);
GetPrivateProfileStringW(
L"dns",
L"aimedb",
default_,
cfg->aimedb,
_countof(cfg->aimedb),
filename);
GetPrivateProfileStringW(
L"dns",
L"title",
L"title",
cfg->title,
_countof(cfg->title),
filename);
cfg->replaceHost = GetPrivateProfileIntW(L"dns", L"replaceHost", 0, filename);
cfg->startupPort = GetPrivateProfileIntW(L"dns", L"startupPort", 0, filename);
cfg->billingPort = GetPrivateProfileIntW(L"dns", L"billingPort", 0, filename);
cfg->aimedbPort = GetPrivateProfileIntW(L"dns", L"aimedbPort", 0, filename);
}
void hwmon_config_load(struct hwmon_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"hwmon", L"enable", 1, filename);
}
void hwreset_config_load(struct hwreset_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"hwreset", L"enable", 1, filename);
}
void misc_config_load(struct misc_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"misc", L"enable", 1, filename);
}
void netenv_config_load(struct netenv_config *cfg, const wchar_t *filename)
{
wchar_t mac_addr[18];
assert(cfg != NULL);
assert(filename != NULL);
memset(cfg, 0, sizeof(*cfg));
cfg->enable = GetPrivateProfileIntW(L"netenv", L"enable", 0, filename);
cfg->addr_suffix = GetPrivateProfileIntW(
L"netenv",
L"addrSuffix",
11,
filename);
cfg->router_suffix = GetPrivateProfileIntW(
L"netenv",
L"routerSuffix",
254,
filename);
GetPrivateProfileStringW(
L"netenv",
L"macAddr",
L"01:02:03:04:05:06",
mac_addr,
_countof(mac_addr),
filename);
swscanf(mac_addr,
L"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&cfg->mac_addr[0],
&cfg->mac_addr[1],
&cfg->mac_addr[2],
&cfg->mac_addr[3],
&cfg->mac_addr[4],
&cfg->mac_addr[5],
&cfg->mac_addr[6]);
}
void nusec_config_load(struct nusec_config *cfg, const wchar_t *filename)
{
wchar_t keychip_id[17];
wchar_t game_id[5];
wchar_t platform_id[5];
wchar_t subnet[16];
unsigned int ip[4];
size_t i;
assert(cfg != NULL);
assert(filename != NULL);
memset(cfg, 0, sizeof(*cfg));
memset(keychip_id, 0, sizeof(keychip_id));
memset(game_id, 0, sizeof(game_id));
memset(platform_id, 0, sizeof(platform_id));
memset(subnet, 0, sizeof(subnet));
cfg->enable = GetPrivateProfileIntW(L"keychip", L"enable", 1, filename);
GetPrivateProfileStringW(
L"keychip",
L"id",
L"A69E-01A88888888",
keychip_id,
_countof(keychip_id),
filename);
GetPrivateProfileStringW(
L"keychip",
L"gameId",
L"",
game_id,
_countof(game_id),
filename);
GetPrivateProfileStringW(
L"keychip",
L"platformId",
L"",
platform_id,
_countof(platform_id),
filename);
cfg->region = GetPrivateProfileIntW(L"keychip", L"region", 1, filename);
cfg->billing_type = GetPrivateProfileIntW(L"keychip", L"billingType", 1, filename);
cfg->system_flag = GetPrivateProfileIntW(
L"keychip",
L"systemFlag",
0x64,
filename);
GetPrivateProfileStringW(
L"keychip",
L"subnet",
L"192.168.100.0",
subnet,
_countof(subnet),
filename);
for (i = 0 ; i < 16 ; i++) {
cfg->keychip_id[i] = (char) keychip_id[i];
}
for (i = 0 ; i < 4 ; i++) {
cfg->game_id[i] = (char) game_id[i];
}
for (i = 0 ; i < 4 ; i++) {
cfg->platform_id[i] = (char) platform_id[i];
}
swscanf(subnet, L"%u.%u.%u.%u", &ip[0], &ip[1], &ip[2], &ip[3]);
cfg->subnet = (ip[0] << 24) | (ip[1] << 16) | (ip[2] << 8) | 0;
GetPrivateProfileStringW(
L"keychip",
L"billingCa",
L"DEVICE\\ca.crt",
cfg->billing_ca,
_countof(cfg->billing_ca),
filename);
GetPrivateProfileStringW(
L"keychip",
L"billingPub",
L"DEVICE\\billing.pub",
cfg->billing_pub,
_countof(cfg->billing_pub),
filename);
}
void pcbid_config_load(struct pcbid_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"pcbid", L"enable", 1, filename);
GetPrivateProfileStringW(
L"pcbid",
L"serialNo",
L"ACAE01A99999999",
cfg->serial_no,
_countof(cfg->serial_no),
filename);
}
void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"vfs", L"enable", 1, filename);
GetPrivateProfileStringW(
L"vfs",
L"amfs",
L"",
cfg->amfs,
_countof(cfg->amfs),
filename);
GetPrivateProfileStringW(
L"vfs",
L"appdata",
L"",
cfg->appdata,
_countof(cfg->appdata),
filename);
GetPrivateProfileStringW(
L"vfs",
L"option",
L"",
cfg->option,
_countof(cfg->option),
filename);
}
void system_config_load(struct system_config *cfg, const wchar_t *filename)
{
wchar_t name[7];
size_t i;
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"system", L"enable", 0, filename);
cfg->freeplay = GetPrivateProfileIntW(L"system", L"freeplay", 0, filename);
wcscpy_s(name, _countof(name), L"dipsw0");
for (i = 0 ; i < 8 ; i++) {
name[5] = L'1' + i;
cfg->dipsw[i] = GetPrivateProfileIntW(L"system", name, 0, filename);
}
}
void epay_config_load(struct epay_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"epay", L"enable", 1, filename);
cfg->hook = GetPrivateProfileIntW(L"epay", L"hook", 0, filename);
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "platform/amvideo.h"
#include "platform/clock.h"
#include "platform/dns.h"
#include "platform/epay.h"
#include "platform/hwmon.h"
#include "platform/hwreset.h"
#include "platform/misc.h"
#include "platform/netenv.h"
#include "platform/nusec.h"
#include "platform/pcbid.h"
#include "platform/platform.h"
#include "platform/vfs.h"
#include "platform/system.h"
void platform_config_load(
struct platform_config *cfg,
const wchar_t *filename);
void amvideo_config_load(struct amvideo_config *cfg, const wchar_t *filename);
void clock_config_load(struct clock_config *cfg, const wchar_t *filename);
void dns_config_load(struct dns_config *cfg, const wchar_t *filename);
void epay_config_load(struct epay_config *cfg, const wchar_t *filename);
void hwmon_config_load(struct hwmon_config *cfg, const wchar_t *filename);
void hwreset_config_load(struct hwreset_config *cfg, const wchar_t *filename);
void misc_config_load(struct misc_config *cfg, const wchar_t *filename);
void netenv_config_load(struct netenv_config *cfg, const wchar_t *filename);
void nusec_config_load(struct nusec_config *cfg, const wchar_t *filename);
void pcbid_config_load(struct pcbid_config *cfg, const wchar_t *filename);
void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename);
void system_config_load(struct system_config *cfg, const wchar_t *filename);
+189
View File
@@ -0,0 +1,189 @@
#include <windows.h>
#include <assert.h>
#include "hooklib/dns.h"
#include "platform/dns.h"
HRESULT dns_platform_hook_init(const struct dns_config *cfg)
{
HRESULT hr;
assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
if (cfg->replaceHost){
http_hook_init();
}
if (cfg->startupPort || cfg->billingPort || cfg->aimedbPort){
port_hook_init(cfg->startupPort, cfg->billingPort, cfg->aimedbPort);
}
hr = dns_hook_push(L"tenporouter.loc", cfg->router);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"bbrouter.loc", cfg->router);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"naominet.jp", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"anbzvarg.wc", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"op.auth.sys-all.net", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"at.auth.sys-all.net", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"at.sys-all.net", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"ib.naominet.jp", cfg->billing);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"vo.anbzvarg.wc", cfg->billing);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"aime.naominet.jp", cfg->aimedb);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"nvzr.anbzvarg.wc", cfg->aimedb);
if (FAILED(hr)) {
return hr;
}
// crossbeats REV.
hr = dns_hook_push(L"https://rev-ent.ac.capcom.jp:443", cfg->title);
if (FAILED(hr)) {
return hr;
}
// AimePay
hr = dns_hook_push(L"api-aime.am-all.net", cfg->startup);
if (FAILED(hr)) {
return hr;
}
// E-MONEY
hr = dns_hook_push(L"tasms-api-basis.thincacloud.com", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"shop.tfps.thincacloud.com", cfg->startup);
if (FAILED(hr)) {
return hr;
}
// WAHLAP PowerOn
hr = dns_hook_push(L"at.sys-all.cn", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"at.sys-allnet.cn", cfg->startup);
if (FAILED(hr)) {
return hr;
}
// WAHLAP WeChat AimeDB Server
hr = dns_hook_push(L"ai.sys-all.cn", cfg->aimedb);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"ai.sys-allnet.cn", cfg->aimedb);
if (FAILED(hr)) {
return hr;
}
// WAHLAP Billing
hr = dns_hook_push(L"bl.sys-all.cn", cfg->billing);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"bl.sys-allnet.cn", cfg->billing);
if (FAILED(hr)) {
return hr;
}
// if your ISP resolves bad domains, it will kill the network. These 2
// *cannot* resolve
hr = dns_hook_push(L"mobirouter.loc", NULL);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"dslrouter.loc", NULL);
if (FAILED(hr)) {
return hr;
}
// Disable api/polling to the original servers
hr = dns_hook_push(L"*.amlog.sys-all.net", NULL);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"*.d-amlog.sys-all.net", NULL);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
struct dns_config {
bool enable;
wchar_t router[128];
wchar_t startup[128];
wchar_t billing[128];
wchar_t aimedb[128];
wchar_t title[128];
bool replaceHost;
unsigned short startupPort;
unsigned short billingPort;
unsigned short aimedbPort;
};
HRESULT dns_platform_hook_init(const struct dns_config *cfg);
+366
View File
@@ -0,0 +1,366 @@
#include <windows.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "hook/table.h"
#include "hooklib/dns.h"
#include "hooklib/reg.h"
#include "platform/epay.h"
#include <hooklib/path.h>
#include "util/dprintf.h"
static HRESULT misc_read_thinca_adapter(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_ca_loc(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_ca_client_loc(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_network_timeout(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_pattern0(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_network_timeout0(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_pattern1(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_network_timeout1(void *bytes, uint32_t *nbytes);
static uint64_t thinca_initialize(struct thinca_impl * self, uint64_t val);
static uint64_t thinca_dispose(struct thinca_impl * self);
static uint64_t thinca_set_resource(struct thinca_impl * self, char * res);
static uint64_t thinca_set_pay_log(struct thinca_impl * self, uint64_t val, char * log, uint64_t val2, const char * size_lim);
static uint64_t thinca_set_client_log(struct thinca_impl * self, uint64_t val, char * log);
static uint64_t thinca_set_client_cfg(struct thinca_impl * self, char * log, uint64_t val);
static uint64_t thinca_set_goods_code(struct thinca_impl * self, char * code);
static uint64_t thinca_set_evt_handler(struct thinca_impl * self, void* handler);
static uint64_t thinca_set_cert(struct thinca_impl * self, char * cert, uint64_t val);
static uint64_t thinca_set_serial(struct thinca_impl * self, char * cert);
static uint64_t thinca_check_deal(struct thinca_impl * self, void* deal);
static uint64_t thinca_cancel(struct thinca_impl * self);
static uint64_t thinca_select(struct thinca_impl * self);
static uint64_t thinca_unk(struct thinca_impl * self, uint64_t val);
static void thinca_unk8(struct thinca_impl * self);
static uint64_t my_ThincaPaymentGetVersion();
static uint64_t (*next_ThincaPaymentGetVersion)();
static struct thinca_main* my_ThincaPaymentGetInstance(uint64_t ver);
static struct thinca_main* (*next_ThincaPaymentGetInstance)(uint64_t ver);
static struct thinca_main* thinca_stub;
static const struct reg_hook_val epay_adapter_keys[] = {
{
.name = L"TfpsAimeRwAdapter",
.read = misc_read_thinca_adapter,
.type = REG_SZ,
}
};
static const struct reg_hook_val epay_tcap_keys[] = {
{
.name = L"CaLocation",
.read = misc_read_ca_loc,
.type = REG_SZ,
},
{
.name = L"ThincaTcapClientPath",
.read = misc_read_ca_client_loc,
.type = REG_SZ,
},
{
.name = L"ClientNetworkTimeout",
.read = misc_read_network_timeout,
.type = REG_DWORD,
}
};
static const struct reg_hook_val epay_tcap_url0_keys[] = {
{
.name = L"Pattern",
.read = misc_read_pattern0,
.type = REG_SZ,
},
{
.name = L"ClientNetworkTimeout",
.read = misc_read_network_timeout0,
.type = REG_DWORD,
}
};
static const struct reg_hook_val epay_tcap_url1_keys[] = {
{
.name = L"Pattern",
.read = misc_read_pattern1,
.type = REG_SZ,
},
{
.name = L"ClientNetworkTimeout",
.read = misc_read_network_timeout1,
.type = REG_DWORD,
}
};
static const struct hook_symbol epay_syms[] = {
{
.name = "ThincaPaymentGetVersion",
.patch = my_ThincaPaymentGetVersion,
.link = (void **) &next_ThincaPaymentGetVersion,
.ordinal = 1,
},
{
.name = "__imp_ThincaPaymentGetInstance",
.patch = my_ThincaPaymentGetInstance,
.link = (void **) &next_ThincaPaymentGetInstance,
.ordinal = 2,
},
{
.name = "ThincaPaymentGetInstance",
.patch = my_ThincaPaymentGetInstance,
.link = (void **) &next_ThincaPaymentGetInstance,
.ordinal = 2,
}
};
HRESULT epay_apply_registry_hooks(){
HRESULT hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\TFPaymentService\\ThincaRwAdapter",
epay_adapter_keys,
_countof(epay_adapter_keys));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\TFPaymentService\\ThincaTcapClient",
epay_tcap_keys,
_countof(epay_tcap_keys));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\TFPaymentService\\ThincaTcapClient\\URL0",
epay_tcap_url0_keys,
_countof(epay_tcap_url0_keys));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\TFPaymentService\\ThincaTcapClient\\URL1",
epay_tcap_url1_keys,
_countof(epay_tcap_url1_keys));
return hr;
}
HRESULT epay_hook_init(const struct epay_config *cfg) {
HRESULT hr;
assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
hr = epay_apply_registry_hooks();
if (FAILED(hr)){
return hr;
}
dprintf("EPay: Registry initialized\n");
// HACK:(?) the DLLs are loaded dynamically so we just preload it and apply DNS and VFS hooks to it
HMODULE thincahttpclient = LoadLibraryA("thincahttpclient.dll");
if (thincahttpclient != NULL){
dns_hook_apply_hooks(thincahttpclient);
path_hook_insert_hooks(thincahttpclient);
}
HMODULE thincapayment = LoadLibraryA("ThincaPayment.dll");
if (thincapayment != NULL){
path_hook_insert_hooks(thincapayment);
}
HMODULE thincatcapclient = LoadLibraryA("thincatcapclient.dll");
if (thincatcapclient != NULL){
path_hook_insert_hooks(thincatcapclient);
}
if (cfg->hook) {
hook_table_apply(
NULL,
"ThincaPayment.dll",
epay_syms,
_countof(epay_syms));
thinca_stub = (struct thinca_main *) malloc(sizeof(struct thinca_main));
thinca_stub->impl1 = (struct thinca_impl *) malloc(sizeof(struct thinca_impl));
thinca_stub->impl1->unk8 = thinca_unk8;
thinca_stub->impl1->initialize = thinca_initialize;
thinca_stub->impl1->dispose = thinca_dispose;
thinca_stub->impl1->setResource = thinca_set_resource;
thinca_stub->impl1->setThincaPaymentLog = thinca_set_pay_log;
thinca_stub->impl1->setThincaEventInterface = thinca_set_evt_handler;
thinca_stub->impl1->setIcasClientLog = thinca_set_client_log;
thinca_stub->impl1->setIcasClientConfig = thinca_set_client_cfg;
thinca_stub->impl1->setGoodsCode = thinca_set_goods_code;
thinca_stub->impl1->setTerminalSerial = thinca_set_serial;
thinca_stub->impl1->setClientCertificate = thinca_set_cert;
thinca_stub->impl1->checkDeal = thinca_check_deal;
thinca_stub->impl1->cancelRequest = thinca_cancel;
thinca_stub->impl1->selectButton = thinca_select;
thinca_stub->impl1->unk220 = thinca_unk;
thinca_stub->impl1->unk228 = thinca_unk;
dprintf("Epay: Hooks initialized\n");
}
return hr;
}
static HRESULT misc_read_thinca_adapter(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"aime_rw_adapterMD.dll");
}
static HRESULT misc_read_ca_loc(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"ca.pem");
}
static HRESULT misc_read_ca_client_loc(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"thincatcapclient.dll");
}
static HRESULT misc_read_network_timeout(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 20000);
}
static HRESULT misc_read_pattern0(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L".*\\.jsp");
}
static HRESULT misc_read_network_timeout0(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 5000);
}
static HRESULT misc_read_pattern1(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L".*(closing|remove).*");
}
static HRESULT misc_read_network_timeout1(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 60000);
}
static uint64_t thinca_initialize(struct thinca_impl * self, uint64_t val)
{
dprintf("Epay: Thinca Initialize %lld\n", val);
return 0;
}
static uint64_t thinca_dispose(struct thinca_impl * self)
{
dprintf("Epay: Thinca Dispose\n");
return 0;
}
static uint64_t thinca_set_resource(struct thinca_impl * self, char * res)
{
dprintf("Epay: Thinca Set Resource %s\n", res);
return 0;
}
static uint64_t thinca_set_pay_log(struct thinca_impl * self, uint64_t val, char * log, uint64_t val2, const char * size_lim)
{
dprintf("Epay: Thinca Set Paylog %lld | %s | %lld | %s\n", val, log, val2, size_lim);
return 0;
}
static uint64_t thinca_set_client_log(struct thinca_impl * self, uint64_t val, char * log)
{
dprintf("Epay: Thinca Set ICAS Client log %lld | %s\n", val, log);
return 0;
}
static uint64_t thinca_set_client_cfg(struct thinca_impl * self, char * log, uint64_t val)
{
dprintf("Epay: Thinca Set ICAS Client Config %s | %lld\n", log, val);
return 0;
}
static uint64_t thinca_set_goods_code(struct thinca_impl * self, char * code)
{
dprintf("Epay: Thinca Set Goods Code %s\n", code);
return 0;
}
static uint64_t thinca_set_evt_handler(struct thinca_impl * self, void* handler)
{
dprintf("Epay: Thinca Set Event Handler %p\n", handler);
return 0;
}
static uint64_t thinca_set_cert(struct thinca_impl * self, char * cert, uint64_t val)
{
dprintf("Epay: Thinca Set Client Cert %s | %lld\n", cert, val);
return 0;
}
static uint64_t thinca_set_serial(struct thinca_impl * self, char * cert)
{
dprintf("Epay: Thinca Set Terminal Serial %s\n", cert);
return 0;
}
static uint64_t thinca_check_deal(struct thinca_impl * self, void* deal)
{
dprintf("Epay: Thinca Check Deal %p\n", deal);
return 0;
}
static uint64_t thinca_cancel(struct thinca_impl * self)
{
dprintf("Epay: Thinca Cancel\n");
return 0;
}
static uint64_t thinca_select(struct thinca_impl * self)
{
dprintf("Epay: Thinca Select\n");
return 0;
}
static uint64_t thinca_unk(struct thinca_impl * self, uint64_t val)
{
dprintf("Epay: Thinca Unknown 220/228 %lld\n", val);
return 0;
}
static void thinca_unk8(struct thinca_impl * self)
{
dprintf("Epay: Thinca Unknown 8\n");
}
static uint64_t my_ThincaPaymentGetVersion()
{
return 0x1040B00;
}
static struct thinca_main* my_ThincaPaymentGetInstance(uint64_t ver)
{
dprintf("Epay: my_ThincaPaymentGetInstance hit!\n");
return thinca_stub;
}
+66
View File
@@ -0,0 +1,66 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#pragma pack(push,1)
struct epay_config {
bool enable;
bool hook;
};
/* The functions in these structs are how clients like amdaemon interface
* with thinca. We can simply replace these functions with our own stubs
* to bypass errors and such. Currently this DOES NOT allow for epay, and
* trying to do so will most likely just lead to misery. My goal isn't to
* reimplement epay, just to give amdaemon SOMETHING so we can boot properly.
*/
struct thinca_impl {
uint64_t* unk0;
void (*unk8)(struct thinca_impl *);
uint64_t (*initialize)(struct thinca_impl *, uint64_t);
uint64_t (*dispose)(struct thinca_impl *);
uint64_t (*setResource)(struct thinca_impl *, char *);
uint64_t (*setThincaPaymentLog)(struct thinca_impl *, uint64_t, char *, uint64_t, const char *);
uint64_t (*setIcasClientLog)(struct thinca_impl *, uint64_t, char *);
uint64_t (*setIcasClientConfig)(struct thinca_impl *, char *, uint64_t);
uint64_t* unk40;
uint64_t* unk48;
uint64_t (*setClientCertificate)(struct thinca_impl *, char *, uint64_t);
uint64_t (*setTerminalSerial)(struct thinca_impl *, char *);
uint64_t (*setGoodsCode)(struct thinca_impl *, char *);
uint64_t unk68;
uint64_t (*setThincaEventInterface)(struct thinca_impl *, void*); // probably a struct
uint64_t unkGap78[7];
uint64_t (*checkDeal)(struct thinca_impl *, void *); // probably a struct
uint64_t unkGapB8[41];
uint64_t (*cancelRequest)(struct thinca_impl *);
uint64_t (*selectButton)(struct thinca_impl *);
uint64_t unkGap210[2];
uint64_t (*unk220)(struct thinca_impl *, uint64_t);
uint64_t (*unk228)(struct thinca_impl *, uint64_t);
};
/* I believe the actual struct is 0x310 bytes, so for now I'm just
* implementing what I need and hoping the rest don't cause issues
* later. AMDaemon seems to only care about impl1 and deal_thing,
* at least from what I can tell
*/
struct thinca_main {
struct thinca_impl* impl1;
struct thinca_impl* impl2;
HANDLE* mutex1;
HANDLE* mutex2;
HANDLE* mutex3;
uint64_t* unk28;
uint64_t* unk30;
uint64_t* unk38;
uint64_t* unk40;
uint64_t* deal_thing;
uint64_t filler[88];
};
#pragma pack(pop)
HRESULT epay_hook_init(const struct epay_config *cfg);
HRESULT epay_apply_registry_hooks();
+107
View File
@@ -0,0 +1,107 @@
#include <windows.h>
#include <assert.h>
#include <winioctl.h>
#include <string.h>
#include "hook/iohook.h"
#include "platform/hwmon.h"
#include "util/dprintf.h"
#include "util/str.h"
#define HWMON_IOCTL_READ_CPU_TEMP CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)
static HRESULT hwmon_handle_irp(struct irp *irp);
static HRESULT hwmon_handle_open(struct irp *irp);
static HRESULT hwmon_handle_close(struct irp *irp);
static HRESULT hwmon_handle_ioctl(struct irp *irp);
static HRESULT hwmon_ioctl_read_cpu_temp(struct irp *irp);
static HANDLE hwmon_fd;
HRESULT hwmon_hook_init(const struct hwmon_config *cfg)
{
HRESULT hr;
assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
hr = iohook_open_nul_fd(&hwmon_fd);
if (FAILED(hr)) {
return hr;
}
hr = iohook_push_handler(hwmon_handle_irp);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
static HRESULT hwmon_handle_irp(struct irp *irp)
{
assert(irp != NULL);
if (irp->op != IRP_OP_OPEN && irp->fd != hwmon_fd) {
return iohook_invoke_next(irp);
}
switch (irp->op) {
case IRP_OP_OPEN: return hwmon_handle_open(irp);
case IRP_OP_CLOSE: return hwmon_handle_close(irp);
case IRP_OP_IOCTL: return hwmon_handle_ioctl(irp);
default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
}
}
static HRESULT hwmon_handle_open(struct irp *irp)
{
if (!wstr_ieq(irp->open_filename, L"\\\\.\\sghwmonitor")) {
return iohook_invoke_next(irp);
}
dprintf("Hwmon: Opened device\n");
irp->fd = hwmon_fd;
return S_OK;
}
static HRESULT hwmon_handle_close(struct irp *irp)
{
dprintf("Hwmon: Closed device\n");
return S_OK;
}
static HRESULT hwmon_handle_ioctl(struct irp *irp)
{
switch (irp->ioctl) {
case HWMON_IOCTL_READ_CPU_TEMP:
return hwmon_ioctl_read_cpu_temp(irp);
default:
dprintf("Hwmon: Unknown ioctl %08x, write %i read %i\n",
irp->ioctl,
(int) irp->write.nbytes,
(int) irp->read.nbytes);
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
}
}
static HRESULT hwmon_ioctl_read_cpu_temp(struct irp *irp)
{
/* Assuming this is Celsius. It also seems to be biased by -23 (based on the
caller code) despite being a 32-bit int. */
return iobuf_write_le32(&irp->read, 52);
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct hwmon_config {
bool enable;
};
HRESULT hwmon_hook_init(const struct hwmon_config *cfg);
+106
View File
@@ -0,0 +1,106 @@
#include <windows.h>
#include <assert.h>
#include <winioctl.h>
#include <string.h>
#include "hook/iohook.h"
#include "platform/hwreset.h"
#include "util/dprintf.h"
#include "util/str.h"
#define HWRESET_IOCTL_RESTART CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
static HRESULT hwreset_handle_irp(struct irp *irp);
static HRESULT hwreset_handle_open(struct irp *irp);
static HRESULT hwreset_handle_close(struct irp *irp);
static HRESULT hwreset_handle_ioctl(struct irp *irp);
static HRESULT hwreset_ioctl_restart(struct irp *irp);
static HANDLE hwreset_fd;
HRESULT hwreset_hook_init(const struct hwreset_config *cfg)
{
HRESULT hr;
assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
hr = iohook_open_nul_fd(&hwreset_fd);
if (FAILED(hr)) {
return hr;
}
hr = iohook_push_handler(hwreset_handle_irp);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
static HRESULT hwreset_handle_irp(struct irp *irp)
{
assert(irp != NULL);
if (irp->op != IRP_OP_OPEN && irp->fd != hwreset_fd) {
return iohook_invoke_next(irp);
}
switch (irp->op) {
case IRP_OP_OPEN: return hwreset_handle_open(irp);
case IRP_OP_CLOSE: return hwreset_handle_close(irp);
case IRP_OP_IOCTL: return hwreset_handle_ioctl(irp);
default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
}
}
static HRESULT hwreset_handle_open(struct irp *irp)
{
if (!wstr_ieq(irp->open_filename, L"\\\\.\\sghwreset")) {
return iohook_invoke_next(irp);
}
dprintf("Hwreset: Opened device\n");
irp->fd = hwreset_fd;
return S_OK;
}
static HRESULT hwreset_handle_close(struct irp *irp)
{
dprintf("Hwreset: Closed device\n");
return S_OK;
}
static HRESULT hwreset_handle_ioctl(struct irp *irp)
{
switch (irp->ioctl) {
case HWRESET_IOCTL_RESTART:
return hwreset_ioctl_restart(irp);
default:
dprintf("Hwreset: Unknown ioctl %08x, write %i read %i\n",
irp->ioctl,
(int) irp->write.nbytes,
(int) irp->read.nbytes);
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
}
}
static HRESULT hwreset_ioctl_restart(struct irp *irp)
{
dprintf("Hwreset: Reset requested\n");
return iobuf_write_le32(&irp->read, 1);
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct hwreset_config {
bool enable;
};
HRESULT hwreset_hook_init(const struct hwreset_config *cfg);
+39
View File
@@ -0,0 +1,39 @@
platform_lib = static_library(
'platform',
include_directories : inc,
implicit_include_directories : false,
dependencies : [
capnhook.get_variable('hook_dep'),
shlwapi_lib,
],
sources : [
'amvideo.c',
'amvideo.h',
'clock.c',
'clock.h',
'config.c',
'config.h',
'dns.c',
'dns.h',
'epay.c',
'epay.h',
'hwmon.c',
'hwmon.h',
'hwreset.c',
'hwreset.h',
'misc.c',
'misc.h',
'netenv.c',
'netenv.h',
'nusec.c',
'nusec.h',
'pcbid.c',
'pcbid.h',
'platform.c',
'platform.h',
'vfs.c',
'vfs.h',
'system.c',
'system.h',
],
)
+173
View File
@@ -0,0 +1,173 @@
#include <windows.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "hook/table.h"
#include "hooklib/reg.h"
#include "platform/misc.h"
#include "util/dprintf.h"
static BOOL WINAPI misc_ExitWindowsEx(unsigned int flags, uint32_t reason);
static HRESULT misc_read_os_version(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_app_loader_count(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_cpu_temp_error(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_cpu_temp_warning(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_platform_id(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_platform_name(void *bytes, uint32_t *nbytes);
static const struct hook_symbol misc_syms[] = {
{
.name = "ExitWindowsEx",
.patch = misc_ExitWindowsEx,
}
};
static const struct reg_hook_val misc_root_keys[] = {
{
.name = L"OSVersion",
.read = misc_read_os_version,
.type = REG_SZ,
}
};
static const struct reg_hook_val misc_master_keys[] = {
{
.name = L"AppLoaderCount",
.read = misc_read_app_loader_count,
.type = REG_DWORD,
}, {
/* Black-hole val, list it here so we don't get a warning msg */
.name = L"NextProcess",
.type = REG_SZ,
}, {
/* ditto */
.name = L"SystemError",
.type = REG_SZ,
}
};
static const struct reg_hook_val misc_static_keys[] = {
{
.name = L"CpuTempError",
.read = misc_read_cpu_temp_error,
.type = REG_DWORD,
}, {
.name = L"CpuTempWarning",
.read = misc_read_cpu_temp_warning,
.type = REG_DWORD,
}, {
.name = L"PlatformId",
.read = misc_read_platform_id,
.type = REG_SZ,
}, {
.name = L"PlatformName",
.read = misc_read_platform_name,
.type = REG_SZ,
}
};
static wchar_t misc_platform_id[5];
HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id)
{
HRESULT hr;
assert(cfg != NULL);
assert(platform_id != NULL && strlen(platform_id) == 4);
if (!cfg->enable) {
return S_FALSE;
}
/* Set platform ID registry value */
mbstowcs_s(
NULL,
misc_platform_id,
_countof(misc_platform_id),
platform_id,
_countof(misc_platform_id) - 1);
/* Add hardcoded dummy keys */
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty",
misc_root_keys,
_countof(misc_root_keys));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty\\static",
misc_static_keys,
_countof(misc_static_keys));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty\\Master",
misc_master_keys,
_countof(misc_master_keys));
if (FAILED(hr)) {
return hr;
}
/* Apply function hooks */
hook_table_apply(NULL, "user32.dll", misc_syms, _countof(misc_syms));
return S_OK;
}
static BOOL WINAPI misc_ExitWindowsEx(unsigned int flags, uint32_t reason)
{
dprintf("Misc: Blocked system reboot\n");
return TRUE;
}
static HRESULT misc_read_os_version(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"0_0_0");
}
static HRESULT misc_read_app_loader_count(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 1);
}
static HRESULT misc_read_cpu_temp_error(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 100);
}
static HRESULT misc_read_cpu_temp_warning(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 95);
}
static HRESULT misc_read_platform_id(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, misc_platform_id);
}
static HRESULT misc_read_platform_name(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"ALLS MX2.1"); // TODO: Dynamic
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <windows.h>
struct misc_config {
bool enable;
};
HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id);
+508
View File
@@ -0,0 +1,508 @@
#include <windows.h>
#include <winsock2.h>
#include <iphlpapi.h>
#include <iptypes.h>
#include <winternl.h>
#include <assert.h>
#include <icmpapi.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "hook/table.h"
#include "platform/netenv.h"
#include "platform/nusec.h"
#include "util/dprintf.h"
struct netenv {
IP_ADAPTER_ADDRESSES head;
char name[64];
wchar_t dns_suffix[64];
wchar_t description[64];
wchar_t friendly_name[64];
IP_ADAPTER_PREFIX prefix;
IP_ADAPTER_UNICAST_ADDRESS iface;
IP_ADAPTER_GATEWAY_ADDRESS router;
IP_ADAPTER_DNS_SERVER_ADDRESS dns;
struct sockaddr_in prefix_sa;
struct sockaddr_in iface_sa;
struct sockaddr_in router_sa;
struct sockaddr_in dns_sa;
};
/* Hook functions */
static uint32_t WINAPI hook_GetAdaptersAddresses(
uint32_t Family,
uint32_t Flags,
void *Reserved,
IP_ADAPTER_ADDRESSES *AdapterAddresses,
uint32_t *SizePointer);
static uint32_t WINAPI hook_GetAdaptersInfo(
IP_ADAPTER_INFO *AdapterInfo,
uint32_t *SizePointer);
static uint32_t WINAPI hook_GetBestRoute(
uint32_t src_ip,
uint32_t dest_ip,
MIB_IPFORWARDROW *route);
static uint32_t WINAPI hook_GetIfTable(
MIB_IFTABLE *pIfTable,
uint32_t *pdwSize,
BOOL bOrder);
static uint32_t WINAPI hook_IcmpSendEcho2(
HANDLE IcmpHandle,
HANDLE Event,
PIO_APC_ROUTINE ApcRoutine,
void *ApcContext,
uint32_t DestinationAddress,
void *RequestData,
uint16_t RequestSize,
IP_OPTION_INFORMATION *RequestOptions,
void *ReplyBuffer,
uint32_t ReplySize,
uint32_t Timeout);
/* Link pointers */
static uint32_t (WINAPI *next_GetAdaptersAddresses)(
uint32_t Family,
uint32_t Flags,
void *Reserved,
IP_ADAPTER_ADDRESSES *AdapterAddresses,
uint32_t *SizePointer);
static uint32_t (WINAPI *next_GetAdaptersInfo)(
IP_ADAPTER_INFO *AdapterInfo,
uint32_t *SizePointer);
static uint32_t (WINAPI *next_GetBestRoute)(
uint32_t src_ip,
uint32_t dest_ip,
MIB_IPFORWARDROW *route);
static uint32_t (WINAPI *next_GetIfTable)(
MIB_IFTABLE *pIfTable,
uint32_t *pdwSize,
BOOL bOrder);
static uint32_t (WINAPI *next_IcmpSendEcho2)(
HANDLE IcmpHandle,
HANDLE Event,
PIO_APC_ROUTINE ApcRoutine,
void *ApcContext,
uint32_t DestinationAddress,
void *RequestData,
uint16_t RequestSize,
IP_OPTION_INFORMATION *RequestOptions,
void *ReplyBuffer,
uint32_t ReplySize,
uint32_t Timeout);
static const struct hook_symbol netenv_hook_syms[] = {
{
.name = "GetAdaptersAddresses",
.patch = hook_GetAdaptersAddresses,
.link = (void **) &next_GetAdaptersAddresses,
}, {
.name = "GetAdaptersInfo",
.patch = hook_GetAdaptersInfo,
.link = (void **) &next_GetAdaptersInfo,
}, {
.name = "GetBestRoute",
.patch = hook_GetBestRoute,
.link = (void **) &next_GetBestRoute,
}, {
.name = "GetIfTable",
.patch = hook_GetIfTable,
.link = (void **) &next_GetIfTable,
}, {
.name = "IcmpSendEcho2",
.patch = hook_IcmpSendEcho2,
.link = (void **) &next_IcmpSendEcho2,
}
};
static uint32_t netenv_ip_prefix;
static uint32_t netenv_ip_iface;
static uint32_t netenv_ip_router;
static uint8_t netenv_mac_addr[6];
HRESULT netenv_hook_init(
const struct netenv_config *cfg,
const struct nusec_config *kc_cfg)
{
assert(cfg != NULL);
assert(kc_cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
if (!kc_cfg->enable) {
dprintf("Netenv: Keychip emu is off? Disabling Netenv emu.\n");
return S_FALSE;
}
netenv_ip_prefix = kc_cfg->subnet;
netenv_ip_iface = kc_cfg->subnet | cfg->addr_suffix;
netenv_ip_router = kc_cfg->subnet | cfg->router_suffix;
memcpy(netenv_mac_addr, cfg->mac_addr, sizeof(netenv_mac_addr));
hook_table_apply(
NULL,
"iphlpapi.dll",
netenv_hook_syms,
_countof(netenv_hook_syms));
return S_OK;
}
static uint32_t WINAPI hook_GetAdaptersAddresses(
uint32_t Family,
uint32_t Flags,
void *Reserved,
IP_ADAPTER_ADDRESSES *AdapterAddresses,
uint32_t *SizePointer)
{
/* This hook errs on the side of caution and returns a lot more
information than the ALLNET lib cares about. MSVC mangles the main
call site for this API quite aggressively, so by the time we decompile
the code in question it's a little difficult to tell which pieces the
ALLNET lib pays attention to. */
uint32_t nbytes;
struct netenv *env;
if (Reserved != NULL || SizePointer == NULL) {
return ERROR_INVALID_PARAMETER;
}
nbytes = *SizePointer;
*SizePointer = sizeof(*env);
if (AdapterAddresses == NULL || nbytes < sizeof(*env)) {
return ERROR_BUFFER_OVERFLOW;
}
env = CONTAINING_RECORD(AdapterAddresses, struct netenv, head);
memset(env, 0, sizeof(*env));
env->head.Length = sizeof(env->head);
env->head.IfIndex = 1;
env->head.AdapterName = env->name;
env->head.FirstUnicastAddress = &env->iface;
env->head.FirstDnsServerAddress = &env->dns;
env->head.DnsSuffix = env->dns_suffix;
env->head.Description = env->description;
env->head.FriendlyName = env->friendly_name;
memcpy( env->head.PhysicalAddress,
netenv_mac_addr,
sizeof(netenv_mac_addr));
env->head.PhysicalAddressLength = sizeof(netenv_mac_addr);
env->head.Flags = IP_ADAPTER_DHCP_ENABLED | IP_ADAPTER_IPV4_ENABLED;
env->head.Mtu = 4200; /* idk what's typical here */
env->head.IfType = IF_TYPE_ETHERNET_CSMACD;
env->head.OperStatus = IfOperStatusUp;
env->head.FirstPrefix = &env->prefix;
env->head.FirstGatewayAddress = &env->router;
strcpy_s(
env->name,
_countof(env->name),
"{00000000-0000-0000-0000-000000000000}");
wcscpy_s(
env->dns_suffix,
_countof(env->dns_suffix),
L"local");
wcscpy_s(
env->description,
_countof(env->description),
L"Interface Description");
wcscpy_s(
env->friendly_name,
_countof(env->friendly_name),
L"Fake Ethernet");
env->iface.Length = sizeof(env->iface);
env->iface.Flags = 0;
env->iface.Address.lpSockaddr = (struct sockaddr *) &env->iface_sa;
env->iface.Address.iSockaddrLength = sizeof(env->iface_sa);
env->iface.PrefixOrigin = IpPrefixOriginDhcp;
env->iface.SuffixOrigin = IpSuffixOriginDhcp;
env->iface.DadState = IpDadStatePreferred;
env->iface.ValidLifetime = UINT32_MAX;
env->iface.PreferredLifetime = UINT32_MAX;
env->iface.LeaseLifetime = 86400;
env->iface.OnLinkPrefixLength = 24;
env->prefix.Length = sizeof(env->prefix);
env->prefix.Address.lpSockaddr = (struct sockaddr *) &env->prefix_sa;
env->prefix.Address.iSockaddrLength = sizeof(env->prefix_sa);
env->prefix.PrefixLength = 24;
env->router.Length = sizeof(env->router);
env->router.Address.lpSockaddr = (struct sockaddr *) &env->router_sa;
env->router.Address.iSockaddrLength = sizeof(env->router_sa);
env->dns.Length = sizeof(env->dns);
env->dns.Address.lpSockaddr = (struct sockaddr *) &env->dns_sa;
env->dns.Address.iSockaddrLength = sizeof(env->dns_sa);
env->prefix_sa.sin_family = AF_INET;
env->prefix_sa.sin_addr.s_addr = _byteswap_ulong(netenv_ip_prefix);
env->iface_sa.sin_family = AF_INET;
env->iface_sa.sin_addr.s_addr = _byteswap_ulong(netenv_ip_iface);
env->router_sa.sin_family = AF_INET;
env->router_sa.sin_addr.s_addr = _byteswap_ulong(netenv_ip_router);
env->dns_sa.sin_family = AF_INET;
env->dns_sa.sin_addr.s_addr = _byteswap_ulong(netenv_ip_router);
return ERROR_SUCCESS;
}
static uint32_t WINAPI hook_GetAdaptersInfo(
IP_ADAPTER_INFO *ai,
uint32_t *nbytes_inout)
{
IP_ADDR_STRING iface;
IP_ADDR_STRING router;
uint32_t nbytes;
if (nbytes_inout == NULL) {
return ERROR_INVALID_PARAMETER;
}
nbytes = *nbytes_inout;
*nbytes_inout = sizeof(*ai);
if (ai == NULL || nbytes < sizeof(*ai)) {
return ERROR_BUFFER_OVERFLOW;
}
dprintf("Netenv: GetAdaptersInfo: Virtualized LAN configuration:\n");
dprintf("Netenv: Interface IP : %3i.%3i.%3i.%3i\n",
(uint8_t) (netenv_ip_iface >> 24),
(uint8_t) (netenv_ip_iface >> 16),
(uint8_t) (netenv_ip_iface >> 8),
(uint8_t) (netenv_ip_iface ));
dprintf("Netenv: Router IP : %3i.%3i.%3i.%3i\n",
(uint8_t) (netenv_ip_router >> 24),
(uint8_t) (netenv_ip_router >> 16),
(uint8_t) (netenv_ip_router >> 8),
(uint8_t) (netenv_ip_router ));
dprintf("Netenv: MAC Address : %02x:%02x:%02x:%02x:%02x:%02x\n",
netenv_mac_addr[0],
netenv_mac_addr[1],
netenv_mac_addr[2],
netenv_mac_addr[3],
netenv_mac_addr[4],
netenv_mac_addr[5]);
memset(&iface, 0, sizeof(iface));
memset(&router, 0, sizeof(router));
sprintf_s(
iface.IpAddress.String,
_countof(iface.IpAddress.String),
"%i.%i.%i.%i",
(uint8_t) (netenv_ip_iface >> 24),
(uint8_t) (netenv_ip_iface >> 16),
(uint8_t) (netenv_ip_iface >> 8),
(uint8_t) (netenv_ip_iface ));
strcpy_s(
iface.IpMask.String,
_countof(iface.IpMask.String),
"255.255.255.0");
sprintf_s(
router.IpAddress.String,
_countof(iface.IpAddress.String),
"%i.%i.%i.%i",
(uint8_t) (netenv_ip_router >> 24),
(uint8_t) (netenv_ip_router >> 16),
(uint8_t) (netenv_ip_router >> 8),
(uint8_t) (netenv_ip_router ));
strcpy_s(
router.IpMask.String,
_countof(router.IpMask.String),
"255.255.255.0");
memset(ai, 0, sizeof(*ai));
strcpy_s(
ai->AdapterName,
_countof(ai->AdapterName),
"Fake Ethernet");
strcpy_s(ai->Description,
_countof(ai->Description),
"Adapter Description");
ai->AddressLength = sizeof(netenv_mac_addr);
memcpy(ai->Address, netenv_mac_addr, sizeof(netenv_mac_addr));
ai->Index = 1;
ai->Type = MIB_IF_TYPE_ETHERNET;
ai->DhcpEnabled = 1;
memcpy(&ai->IpAddressList, &iface, sizeof(iface));
memcpy(&ai->GatewayList, &router, sizeof(router));
memcpy(&ai->DhcpServer, &router, sizeof(router));
ai->LeaseObtained = time(NULL) - 3600;
ai->LeaseExpires = time(NULL) + 86400;
return ERROR_SUCCESS;
}
static uint32_t WINAPI hook_GetBestRoute(
uint32_t src_ip,
uint32_t dest_ip,
MIB_IPFORWARDROW *route)
{
if (route == NULL) {
return ERROR_INVALID_PARAMETER;
}
uint32_t src_addr = _byteswap_ulong(src_ip);
uint32_t dest_addr = _byteswap_ulong(dest_ip);
dprintf("Netenv: GetBestRoute ip4 %u.%u.%u.%u -> ip4 %u.%u.%u.%u\n",
(src_addr >> 24) & 0xff, (src_addr >> 16) & 0xff, (src_addr >> 8) & 0xff, src_addr & 0xff,
(dest_addr >> 24) & 0xff, (dest_addr >> 16) & 0xff, (dest_addr >> 8) & 0xff, dest_addr & 0xff);
memset(route, 0, sizeof(*route));
/* This doesn't seem to get read? It just needs to succeed. */
route->dwForwardDest = 0x00000000;
route->dwForwardMask = 0xFFFFFFFF;
route->dwForwardPolicy = 0; /* idk */
route->dwForwardNextHop = _byteswap_ulong(netenv_ip_router);
route->dwForwardIfIndex = 1;
route->dwForwardType = MIB_IPROUTE_TYPE_INDIRECT;
route->dwForwardProto = MIB_IPPROTO_NETMGMT;
return ERROR_SUCCESS;
}
static uint32_t WINAPI hook_GetIfTable(
MIB_IFTABLE *pIfTable,
uint32_t *pdwSize,
BOOL bOrder)
{
MIB_IFROW *row;
uint32_t nbytes;
if (pdwSize == NULL) {
return ERROR_INVALID_PARAMETER;
}
nbytes = *pdwSize;
*pdwSize = sizeof(*row) + sizeof(DWORD);
if (pIfTable == NULL || nbytes < sizeof(*row) + sizeof(DWORD)) {
return ERROR_BUFFER_OVERFLOW;
}
pIfTable->dwNumEntries = 1;
row = pIfTable->table;
memset(row, 0, sizeof(*row));
wcscpy_s(row->wszName, _countof(row->wszName), L"Fake Ethernet");
row->dwIndex = 1; /* Should match other IF_INDEX fields we return */
row->dwType = IF_TYPE_ETHERNET_CSMACD;
row->dwMtu = 4200; /* I guess? */
row->dwSpeed = 1000000000;
row->dwPhysAddrLen = sizeof(netenv_mac_addr);
memcpy(row->bPhysAddr, netenv_mac_addr, sizeof(netenv_mac_addr));
row->dwAdminStatus = 1;
row->dwOperStatus = IF_OPER_STATUS_OPERATIONAL;
return ERROR_SUCCESS;
}
static uint32_t WINAPI hook_IcmpSendEcho2(
HANDLE IcmpHandle,
HANDLE Event,
PIO_APC_ROUTINE ApcRoutine,
void *ApcContext,
uint32_t DestinationAddress,
void *RequestData,
uint16_t RequestSize,
IP_OPTION_INFORMATION *RequestOptions,
void *ReplyBuffer,
uint32_t ReplySize,
uint32_t Timeout)
{
ICMP_ECHO_REPLY *pong;
BOOL ok;
if (IcmpHandle == NULL || IcmpHandle == INVALID_HANDLE_VALUE) {
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (ApcRoutine != NULL) {
dprintf("%s: Got APC routine...\n", __func__);
SetLastError(ERROR_NOT_SUPPORTED);
return 0;
}
if (ReplyBuffer == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (ReplySize < sizeof(ICMP_ECHO_REPLY)) {
SetLastError(IP_BUF_TOO_SMALL);
return 0;
}
uint32_t addr = _byteswap_ulong(DestinationAddress);
dprintf("Netenv: Virtualized ICMP Ping to ip4 %u.%u.%u.%u\n",
(addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
pong = (ICMP_ECHO_REPLY *) ReplyBuffer;
memset(pong, 0, sizeof(*pong));
pong->Address = DestinationAddress;
pong->Status = IP_SUCCESS;
pong->RoundTripTime = 1;
pong->DataSize = 0;
pong->Reserved = 1; /* Number of ICMP_ECHO_REPLY structs in ReplyBuffer */
pong->Data = NULL;
if (Event != NULL) {
ok = SetEvent(Event);
if (ok) {
SetLastError(ERROR_IO_PENDING);
}
return 0;
}
dprintf("%s: Unexpected synchronous call...\n", __func__);
SetLastError(ERROR_SUCCESS);
return 1;
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "platform/nusec.h"
struct netenv_config {
bool enable;
uint8_t addr_suffix;
uint8_t router_suffix;
uint8_t mac_addr[6];
};
HRESULT netenv_hook_init(
const struct netenv_config *cfg,
const struct nusec_config *kc_cfg);
+660
View File
@@ -0,0 +1,660 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <winioctl.h>
#include "hook/iohook.h"
#include "hooklib/reg.h"
#include "platform/nusec.h"
#include "util/dprintf.h"
#include "util/dump.h"
#include "util/str.h"
#define NUSEC_IOCTL_PING CTL_CODE(0x22, 0x845, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_GET_PLAY_COUNT CTL_CODE(0x22, 0x854, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_ADD_PLAY_COUNT CTL_CODE(0x22, 0x855, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_SET_IV CTL_CODE(0x22, 0x856, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_ENCRYPT CTL_CODE(0x22, 0x857, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_DECRYPT CTL_CODE(0x22, 0x858, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_TD_RESTORE CTL_CODE(0x22, 0x861, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_ERASE_TRACE_LOG CTL_CODE(0x22, 0x862, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_TD_ERASE_USED CTL_CODE(0x22, 0x863, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_PUT_TRACE_LOG_DATA CTL_CODE(0x22, 0x864, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_GET_TRACE_LOG_DATA CTL_CODE(0x22, 0x865, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_GET_TRACE_LOG_STATE CTL_CODE(0x22, 0x866, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_GET_NVRAM_AVAILABLE CTL_CODE(0x22, 0x867, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_TD_ERASE_ALL CTL_CODE(0x22, 0x869, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_GET_BILLING_CA_CERT CTL_CODE(0x22, 0x871, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_GET_BILLING_PUBKEY CTL_CODE(0x22, 0x872, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_GET_PLAY_LIMIT CTL_CODE(0x22, 0x881, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_PUT_PLAY_LIMIT CTL_CODE(0x22, 0x882, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_GET_NEARFULL CTL_CODE(0x22, 0x883, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_PUT_NEARFULL CTL_CODE(0x22, 0x884, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_NVRAM_READ CTL_CODE(0x22, 0x891, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_NVRAM_WRITE CTL_CODE(0x22, 0x892, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_GET_NVRAM_GEOMETRY CTL_CODE(0x22, 0x893, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_AL_AUTH_START CTL_CODE(0x22, 0x8B0, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_AL_AUTH_PACKET CTL_CODE(0x22, 0x8B2, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
// Referenced in AMDaemon but seemingly not used, maybe leftovers?
#define NUSEC_IOCTL_UNK_843 CTL_CODE(0x22, 0x843, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_UNK_844 CTL_CODE(0x22, 0x844, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_UNK_894 CTL_CODE(0x22, 0x894, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define NUSEC_IOCTL_UNK_895 CTL_CODE(0x22, 0x895, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
struct nusec_log_record {
uint8_t unknown[60];
};
static HRESULT nusec_handle_irp(struct irp *irp);
static HRESULT nusec_handle_open(struct irp *irp);
static HRESULT nusec_handle_close(struct irp *irp);
static HRESULT nusec_handle_ioctl(struct irp *irp);
static HRESULT nusec_ioctl_ping(struct irp *irp);
static HRESULT nusec_ioctl_erase_trace_log(struct irp *irp);
static HRESULT nusec_ioctl_td_erase_used(struct irp *irp);
static HRESULT nusec_ioctl_add_play_count(struct irp *irp);
static HRESULT nusec_ioctl_get_billing_ca_cert(struct irp *irp);
static HRESULT nusec_ioctl_get_billing_pubkey(struct irp *irp);
static HRESULT nusec_ioctl_get_nearfull(struct irp *irp);
static HRESULT nusec_ioctl_get_nvram_available(struct irp *irp);
static HRESULT nusec_ioctl_get_nvram_geometry(struct irp *irp);
static HRESULT nusec_ioctl_get_play_count(struct irp *irp);
static HRESULT nusec_ioctl_get_play_limit(struct irp *irp);
static HRESULT nusec_ioctl_get_trace_log_data(struct irp *irp);
static HRESULT nusec_ioctl_get_trace_log_state(struct irp *irp);
static HRESULT nusec_ioctl_put_nearfull(struct irp *irp);
static HRESULT nusec_ioctl_put_play_limit(struct irp *irp);
static HRESULT nusec_ioctl_put_trace_log_data(struct irp *irp);
static HRESULT nusec_reg_read_game_id(void *bytes, uint32_t *nbytes);
static HRESULT nusec_reg_read_keychip_id(void *bytes, uint32_t *nbytes);
static HRESULT nusec_reg_read_model_type(void *bytes, uint32_t *nbytes);
static HRESULT nusec_reg_read_platform_id(void *bytes, uint32_t *nbytes);
static HRESULT nusec_reg_read_region(void *bytes, uint32_t *nbytes);
static HRESULT nusec_reg_read_server_ip_ipv4(void *bytes, uint32_t *nbytes);
static HRESULT nusec_reg_read_server_ip_ipv6(void *bytes, uint32_t *nbytes);
static HRESULT nusec_reg_read_system_flag(void *bytes, uint32_t *nbytes);
static const struct reg_hook_val nusec_reg_vals[] = {
{
.name = L"gameId",
.read = nusec_reg_read_game_id,
.type = REG_BINARY,
}, {
.name = L"keychipId",
.read = nusec_reg_read_keychip_id,
.type = REG_BINARY,
}, {
.name = L"modelType",
.read = nusec_reg_read_model_type,
.type = REG_DWORD,
}, {
.name = L"platformId",
.read = nusec_reg_read_platform_id,
.type = REG_BINARY,
}, {
.name = L"region",
.read = nusec_reg_read_region,
.type = REG_DWORD,
}, {
.name = L"serverIpIpv4",
.read = nusec_reg_read_server_ip_ipv4,
.type = REG_BINARY,
}, {
.name = L"serverIpIpv6",
.read = nusec_reg_read_server_ip_ipv6,
.type = REG_BINARY,
}, {
.name = L"systemFlag",
.read = nusec_reg_read_system_flag,
.type = REG_DWORD,
}
};
static HANDLE nusec_fd;
static uint32_t nusec_nearfull;
static uint32_t nusec_play_count;
static uint32_t nusec_play_limit;
static struct nusec_log_record nusec_log[7154];
static size_t nusec_log_head;
static size_t nusec_log_tail;
static struct nusec_config nusec_cfg;
HRESULT nusec_hook_init(
const struct nusec_config *cfg,
const char *game_id,
const char *platform_id)
{
HRESULT hr;
assert(cfg != NULL);
assert(game_id != NULL && strlen(game_id) == sizeof(cfg->game_id));
assert(platform_id != NULL && strlen(platform_id) == sizeof(cfg->platform_id));
if (!cfg->enable) {
return S_FALSE;
}
memcpy(&nusec_cfg, cfg, sizeof(*cfg));
if (nusec_cfg.game_id[0] == '\0') {
memcpy(nusec_cfg.game_id, game_id, sizeof(nusec_cfg.game_id));
}
if (nusec_cfg.platform_id[0] == '\0') {
memcpy(nusec_cfg.platform_id, platform_id, sizeof(nusec_cfg.platform_id));
}
// High 16 bits is billing type, low is actual playlimit
nusec_nearfull = (nusec_cfg.billing_type << 16) + 512;
nusec_play_count = 0;
nusec_play_limit = 1024;
hr = iohook_open_nul_fd(&nusec_fd);
if (FAILED(hr)) {
return hr;
}
hr = iohook_push_handler(nusec_handle_irp);
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty\\keychip",
nusec_reg_vals,
_countof(nusec_reg_vals));
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
static HRESULT nusec_handle_irp(struct irp *irp)
{
assert(irp != NULL);
if (irp->op != IRP_OP_OPEN && irp->fd != nusec_fd) {
return iohook_invoke_next(irp);
}
switch (irp->op) {
case IRP_OP_OPEN: return nusec_handle_open(irp);
case IRP_OP_CLOSE: return nusec_handle_close(irp);
case IRP_OP_IOCTL: return nusec_handle_ioctl(irp);
default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
}
}
static HRESULT nusec_handle_open(struct irp *irp)
{
if (!wstr_ieq(irp->open_filename, L"\\??\\FddDriver")) {
return iohook_invoke_next(irp);
}
dprintf("Security: Opened handle\n");
irp->fd = nusec_fd;
return S_OK;
}
static HRESULT nusec_handle_close(struct irp *irp)
{
dprintf("Security: Closed handle\n");
return S_OK;
}
static HRESULT nusec_handle_ioctl(struct irp *irp)
{
switch (irp->ioctl) {
case NUSEC_IOCTL_PING:
return nusec_ioctl_ping(irp);
case NUSEC_IOCTL_ERASE_TRACE_LOG:
return nusec_ioctl_erase_trace_log(irp);
case NUSEC_IOCTL_TD_ERASE_USED:
return nusec_ioctl_td_erase_used(irp);
case NUSEC_IOCTL_ADD_PLAY_COUNT:
return nusec_ioctl_add_play_count(irp);
case NUSEC_IOCTL_GET_BILLING_CA_CERT:
return nusec_ioctl_get_billing_ca_cert(irp);
case NUSEC_IOCTL_GET_BILLING_PUBKEY:
return nusec_ioctl_get_billing_pubkey(irp);
case NUSEC_IOCTL_GET_NEARFULL:
return nusec_ioctl_get_nearfull(irp);
case NUSEC_IOCTL_GET_NVRAM_AVAILABLE:
return nusec_ioctl_get_nvram_available(irp);
case NUSEC_IOCTL_GET_NVRAM_GEOMETRY:
return nusec_ioctl_get_nvram_geometry(irp);
case NUSEC_IOCTL_GET_PLAY_COUNT:
return nusec_ioctl_get_play_count(irp);
case NUSEC_IOCTL_GET_PLAY_LIMIT:
return nusec_ioctl_get_play_limit(irp);
case NUSEC_IOCTL_GET_TRACE_LOG_DATA:
return nusec_ioctl_get_trace_log_data(irp);
case NUSEC_IOCTL_GET_TRACE_LOG_STATE:
return nusec_ioctl_get_trace_log_state(irp);
case NUSEC_IOCTL_PUT_NEARFULL:
return nusec_ioctl_put_nearfull(irp);
case NUSEC_IOCTL_PUT_PLAY_LIMIT:
return nusec_ioctl_put_play_limit(irp);
case NUSEC_IOCTL_PUT_TRACE_LOG_DATA:
return nusec_ioctl_put_trace_log_data(irp);
default:
dprintf("Security: Unknown ioctl %#08x, write %i read %i\n",
irp->ioctl,
(int) irp->write.nbytes,
(int) irp->read.nbytes);
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
}
}
static HRESULT nusec_ioctl_ping(struct irp *irp)
{
return S_OK;
}
static HRESULT nusec_ioctl_erase_trace_log(struct irp *irp)
{
uint32_t count;
size_t avail;
HRESULT hr;
hr = iobuf_read_le32(&irp->write, &count);
if (FAILED(hr)) {
return hr;
}
dprintf("Security: %s(count=%i)\n", __func__, count);
avail = nusec_log_head - nusec_log_tail;
if (count < avail) {
count = avail;
}
nusec_log_tail += count;
return S_OK;
}
static HRESULT nusec_ioctl_td_erase_used(struct irp *irp)
{
dprintf("Security: %s\n", __func__);
nusec_log_head = 0;
nusec_log_tail = 0;
return S_OK;
}
static HRESULT nusec_ioctl_add_play_count(struct irp *irp)
{
uint32_t delta;
HRESULT hr;
hr = iobuf_read_le32(&irp->write, &delta);
if (FAILED(hr)) {
return hr;
}
dprintf("Security: Add play count: %i + %i = %i\n",
nusec_play_count,
delta,
nusec_play_count + delta);
nusec_play_count += delta;
return iobuf_write_le32(&irp->read, nusec_play_count);
}
static HRESULT nusec_ioctl_get_billing_ca_cert(struct irp *irp)
{
HANDLE fd;
HRESULT hr;
dprintf("Security: %s\n", __func__);
fd = CreateFileW(
nusec_cfg.billing_ca,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fd == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Error opening CA cert file: %x\n", (int) hr);
return hr;
}
/* Transform this ioctl into a read from that file and pass the IRP on */
irp->op = IRP_OP_READ;
irp->fd = fd;
dprintf(">>> %p:%i/%i\n",
irp->read.bytes,
(int) irp->read.pos,
(int) irp->read.nbytes);
hr = iohook_invoke_next(irp);
dprintf("<<< %p:%i/%i\n",
irp->read.bytes,
(int) irp->read.pos,
(int) irp->read.nbytes);
if (FAILED(hr)) {
dprintf("ReadFile transformation failed: %x\n", (int) hr);
}
CloseHandle(fd);
return hr;
}
static HRESULT nusec_ioctl_get_billing_pubkey(struct irp *irp)
{
HANDLE fd;
HRESULT hr;
dprintf("Security: %s\n", __func__);
fd = CreateFileW(
nusec_cfg.billing_pub,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fd == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Error opening billing pubkey file: %x\n", (int) hr);
return hr;
}
irp->op = IRP_OP_READ;
irp->fd = fd;
dprintf(">>> %p:%i/%i\n",
irp->read.bytes,
(int) irp->read.pos,
(int) irp->read.nbytes);
hr = iohook_invoke_next(irp);
dprintf("<<< %p:%i/%i\n",
irp->read.bytes,
(int) irp->read.pos,
(int) irp->read.nbytes);
if (FAILED(hr)) {
dprintf("ReadFile transformation failed: %x\n", (int) hr);
}
CloseHandle(fd);
return hr;
}
static HRESULT nusec_ioctl_get_nearfull(struct irp *irp)
{
dprintf("Security: %s\n", __func__);
return iobuf_write_le32(&irp->read, nusec_nearfull);
}
static HRESULT nusec_ioctl_get_nvram_available(struct irp *irp)
{
size_t used;
size_t avail;
used = nusec_log_head - nusec_log_tail;
avail = _countof(nusec_log) - used;
dprintf("Security: %s: used=%i avail=%i\n", __func__,
(int) used,
(int) avail);
return iobuf_write_le32(&irp->read, avail);
}
static HRESULT nusec_ioctl_get_nvram_geometry(struct irp *irp)
{
HRESULT hr;
dprintf("Security: %s\n", __func__);
iobuf_write_le32(&irp->read, 10); /* Num NVRAMs */
hr = iobuf_write_le32(&irp->read, 4096); /* Num addresses (per NVRAM?) */
return hr;
}
static HRESULT nusec_ioctl_get_play_count(struct irp *irp)
{
dprintf("Security: %s\n", __func__);
return iobuf_write_le32(&irp->read, nusec_play_count);
}
static HRESULT nusec_ioctl_get_play_limit(struct irp *irp)
{
dprintf("Security: %s\n", __func__);
return iobuf_write_le32(&irp->read, nusec_play_limit);
}
static HRESULT nusec_ioctl_get_trace_log_data(struct irp *irp)
{
uint32_t pos;
uint32_t count;
size_t avail;
HRESULT hr;
dprintf("Security: %s\n", __func__);
hr = iobuf_read_le32(&irp->write, &pos);
if (FAILED(hr)) {
return hr;
}
hr = iobuf_read_le32(&irp->write, &count);
if (FAILED(hr)) {
return hr;
}
dprintf(" Params: %i %i Buf: %i\n", pos, count, (int) irp->read.nbytes);
avail = irp->read.nbytes - irp->read.pos;
if (avail < count * sizeof(struct nusec_log_record)) {
dprintf("\tError: Insufficient buffer\n");
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
while (count > 0 && pos != nusec_log_head) {
memcpy( &irp->read.bytes[irp->read.pos],
&nusec_log[pos % _countof(nusec_log)],
sizeof(struct nusec_log_record));
irp->read.pos += sizeof(struct nusec_log_record);
pos++;
count--;
}
return S_OK;
}
static HRESULT nusec_ioctl_get_trace_log_state(struct irp *irp)
{
HRESULT hr;
dprintf("Security: %s H: %i T: %i\n",
__func__,
(int) nusec_log_head,
(int) nusec_log_tail);
iobuf_write_le32(&irp->read, nusec_log_head - nusec_log_tail);
hr = iobuf_write_le32(&irp->read, nusec_log_tail);
return hr;
}
static HRESULT nusec_ioctl_put_nearfull(struct irp *irp)
{
dprintf("Security: %s\n", __func__);
dump_const_iobuf(&irp->write);
return iobuf_read_le32(&irp->write, &nusec_nearfull);
}
static HRESULT nusec_ioctl_put_play_limit(struct irp *irp)
{
dprintf("Security: %s\n", __func__);
dump_const_iobuf(&irp->write);
return iobuf_read_le32(&irp->write, &nusec_play_limit);
}
static HRESULT nusec_ioctl_put_trace_log_data(struct irp *irp)
{
dprintf("Security: %s\n", __func__);
dump_const_iobuf(&irp->write);
if (irp->write.nbytes != sizeof(struct nusec_log_record)) {
dprintf(" Log record size is incorrect\n");
return E_INVALIDARG;
}
if (nusec_log_head - nusec_log_tail >= _countof(nusec_log)) {
dprintf(" Log buffer is full!\n");
return HRESULT_FROM_WIN32(ERROR_DISK_FULL);
}
memcpy( &nusec_log[nusec_log_head % _countof(nusec_log)],
irp->write.bytes,
sizeof(struct nusec_log_record));
nusec_log_head++;
dprintf(" H: %i T: %i\n", (int) nusec_log_head, (int) nusec_log_tail);
return S_OK;
}
static HRESULT nusec_reg_read_game_id(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_bin(
bytes,
nbytes,
&nusec_cfg.game_id,
sizeof(nusec_cfg.game_id));
}
static HRESULT nusec_reg_read_keychip_id(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_bin(
bytes,
nbytes,
&nusec_cfg.keychip_id,
sizeof(nusec_cfg.keychip_id));
}
static HRESULT nusec_reg_read_model_type(void *bytes, uint32_t *nbytes)
{
uint32_t u32;
char c;
c = nusec_cfg.platform_id[3];
if (c >= '0' && c <= '9') {
u32 = c - '0';
} else {
u32 = 0;
}
return reg_hook_read_u32(bytes, nbytes, u32);
}
static HRESULT nusec_reg_read_platform_id(void *bytes, uint32_t *nbytes)
{
/* Fourth byte is a separate registry val (modelType).
We store it in the same config field for ease of configuration. */
return reg_hook_read_bin(
bytes,
nbytes,
&nusec_cfg.platform_id,
3);
}
static HRESULT nusec_reg_read_region(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, nusec_cfg.region);
}
static HRESULT nusec_reg_read_server_ip_ipv4(void *bytes, uint32_t *nbytes)
{
uint32_t subnet;
subnet = _byteswap_ulong(nusec_cfg.subnet);
return reg_hook_read_bin(bytes, nbytes, &subnet, sizeof(subnet));
}
static HRESULT nusec_reg_read_server_ip_ipv6(void *bytes, uint32_t *nbytes)
{
uint8_t subnet[16];
memset(subnet, 0, sizeof(subnet));
return reg_hook_read_bin(bytes, nbytes, subnet, sizeof(subnet));
}
static HRESULT nusec_reg_read_system_flag(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, nusec_cfg.system_flag);
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
struct nusec_config {
bool enable;
char keychip_id[16];
char game_id[4];
char platform_id[4];
uint8_t region;
uint8_t system_flag;
uint32_t subnet;
uint16_t billing_type;
wchar_t billing_ca[MAX_PATH];
wchar_t billing_pub[MAX_PATH];
};
HRESULT nusec_hook_init(
const struct nusec_config *cfg,
const char *game_id,
const char *platform_id);
+70
View File
@@ -0,0 +1,70 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "hook/table.h"
#include "platform/pcbid.h"
#include "util/dprintf.h"
static BOOL WINAPI pcbid_GetComputerNameA(char *dest, uint32_t *len);
static struct pcbid_config pcbid_cfg;
static const struct hook_symbol pcbid_syms[] = {
{
.name = "GetComputerNameA",
.patch = pcbid_GetComputerNameA,
}
};
HRESULT pcbid_hook_init(const struct pcbid_config *cfg)
{
assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
if (wcslen(cfg->serial_no) != 15) {
dprintf("Pcbid: ERROR: Must be 15 chars! ex: ACAE01A99999999\n");
return E_INVALIDARG;
}
memcpy(&pcbid_cfg, cfg, sizeof(*cfg));
hook_table_apply(NULL, "kernel32.dll", pcbid_syms, _countof(pcbid_syms));
return S_OK;
}
static BOOL WINAPI pcbid_GetComputerNameA(char *dest, uint32_t *len)
{
size_t required;
if (dest == NULL || len == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
wcstombs_s(&required, NULL, 0, pcbid_cfg.serial_no, 0);
if (required > *len) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
dprintf("Pcbid: Get PCB serial\n");
wcstombs_s(NULL, dest, *len, pcbid_cfg.serial_no, *len - 1);
SetLastError(ERROR_SUCCESS);
*len = required - 1;
return TRUE;
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
struct pcbid_config {
bool enable;
wchar_t serial_no[17];
};
HRESULT pcbid_hook_init(const struct pcbid_config *cfg);
+98
View File
@@ -0,0 +1,98 @@
#include <windows.h>
#include <assert.h>
#include "platform/amvideo.h"
#include "platform/clock.h"
#include "platform/dns.h"
#include "platform/epay.h"
#include "platform/hwmon.h"
#include "platform/misc.h"
#include "platform/netenv.h"
#include "platform/nusec.h"
#include "platform/pcbid.h"
#include "platform/platform.h"
#include "platform/vfs.h"
#include "platform/system.h"
HRESULT platform_hook_init(
const struct platform_config *cfg,
const char *game_id,
const char *platform_id,
HMODULE redir_mod)
{
HRESULT hr;
assert(cfg != NULL);
assert(game_id != NULL);
assert(platform_id != NULL);
assert(redir_mod != NULL);
hr = amvideo_hook_init(&cfg->amvideo, redir_mod);
if (FAILED(hr)) {
return hr;
}
hr = clock_hook_init(&cfg->clock);
if (FAILED(hr)) {
return hr;
}
hr = dns_platform_hook_init(&cfg->dns);
if (FAILED(hr)) {
return hr;
}
hr = hwmon_hook_init(&cfg->hwmon);
if (FAILED(hr)) {
return hr;
}
hr = misc_hook_init(&cfg->misc, platform_id);
if (FAILED(hr)) {
return hr;
}
hr = netenv_hook_init(&cfg->netenv, &cfg->nusec);
if (FAILED(hr)) {
return hr;
}
hr = nusec_hook_init(&cfg->nusec, game_id, platform_id);
if (FAILED(hr)) {
return hr;
}
hr = pcbid_hook_init(&cfg->pcbid);
if (FAILED(hr)) {
return hr;
}
hr = vfs_hook_init(&cfg->vfs, game_id);
if (FAILED(hr)) {
return hr;
}
hr = system_init(&cfg->system, &cfg->vfs);
if (FAILED(hr)) {
return hr;
}
hr = epay_hook_init(&cfg->epay);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include <windows.h>
#include "platform/amvideo.h"
#include "platform/clock.h"
#include "platform/dns.h"
#include "platform/epay.h"
#include "platform/hwmon.h"
#include "platform/hwreset.h"
#include "platform/misc.h"
#include "platform/netenv.h"
#include "platform/nusec.h"
#include "platform/pcbid.h"
#include "platform/vfs.h"
#include "platform/system.h"
struct platform_config {
struct amvideo_config amvideo;
struct clock_config clock;
struct dns_config dns;
struct epay_config epay;
struct hwmon_config hwmon;
struct hwreset_config hwreset;
struct misc_config misc;
struct pcbid_config pcbid;
struct netenv_config netenv;
struct nusec_config nusec;
struct vfs_config vfs;
struct system_config system;
};
HRESULT platform_hook_init(
const struct platform_config *cfg,
const char *game_id,
const char *platform_id,
HMODULE redir_mod);
+183
View File
@@ -0,0 +1,183 @@
#include "platform/system.h"
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "platform/vfs.h"
#include "util/crc.h"
#include "util/dprintf.h"
#include "util/str.h"
#define DATA_SIZE 503
#define BLOCK_SIZE (sizeof(uint32_t) + 4 + 1 + DATA_SIZE)
#pragma pack(push, 1)
typedef struct {
uint32_t checksum;
char padding[6];
uint8_t freeplay;
char data[DATA_SIZE - 2];
} CreditBlock;
typedef struct {
uint32_t checksum;
char padding[4];
uint8_t dip_switches;
char data[DATA_SIZE];
} DipSwBlock;
typedef struct {
CreditBlock credit_block;
DipSwBlock dip_switch_block;
char *data;
} SystemInfo;
#pragma pack(pop)
static SystemInfo system_info;
static struct system_config system_config;
static struct vfs_config vfs_config;
static void system_read_sysfile(const wchar_t *sys_file);
static void system_save_sysfile(const wchar_t *sys_file);
HRESULT system_init(const struct system_config *cfg,
const struct vfs_config *vfs_cfg) {
HRESULT hr;
wchar_t sys_file_path[MAX_PATH];
assert(cfg != NULL);
assert(vfs_cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
memcpy(&system_config, cfg, sizeof(*cfg));
sys_file_path[0] = L'\0';
// concatenate vfs_config.amfs with L"sysfile.dat"
wcsncpy(sys_file_path, vfs_cfg->amfs, MAX_PATH);
wcsncat(sys_file_path, L"\\sysfile.dat", MAX_PATH);
system_read_sysfile(sys_file_path);
// now write the system_config.system to the dip_switch_block
system_save_sysfile(sys_file_path);
return S_OK;
}
static void system_read_sysfile(const wchar_t *sys_file) {
FILE *f = _wfopen(sys_file, L"r");
if (f == NULL) {
dprintf(
"System: First run detected, system settings can only be applied "
"AFTER the first run\n");
return;
}
fseek(f, 0, SEEK_END);
long file_size = ftell(f);
fseek(f, 0, SEEK_SET);
if (file_size != 0x6000) {
dprintf("System: Invalid sysfile.dat file size\n");
fclose(f);
return;
}
system_info.data = malloc(file_size);
fread(system_info.data, 1, file_size, f);
fclose(f);
// copy the credit_block and dip_switch_block from the sysfile.dat
memcpy(&system_info.credit_block, system_info.data, BLOCK_SIZE);
memcpy(&system_info.dip_switch_block, system_info.data + 0x2800,
BLOCK_SIZE);
}
static void system_save_sysfile(const wchar_t *sys_file) {
char block[BLOCK_SIZE];
DWORD sysfile_bytes_written = 0;
uint8_t system = 0;
uint8_t freeplay = 0;
HANDLE h_sysfile = CreateFileW(sys_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, 0, 0, NULL);
if (h_sysfile == INVALID_HANDLE_VALUE) {
return;
}
// write the system_config.system to the dip_switch_block
for (int i = 0; i < 8; i++) {
// print the dip switch configuration with labels if present
if (system_config.dipsw_config[i].label[0] != L'\0') {
dprintf("System: %ls: %ls\n", system_config.dipsw_config[i].label,
system_config.dipsw[i] ? system_config.dipsw_config[i].on
: system_config.dipsw_config[i].off);
} else if (system_config.dipsw[i]) {
dprintf("System: DipSw%d=1 set\n", i + 1);
}
// set the system variable to the dip switch configuration
if (system_config.dipsw[i]) {
system |= (1 << i);
}
}
if (system_config.freeplay) {
// print that freeplay is enabled
dprintf("System: Freeplay enabled\n");
freeplay = 1;
}
// set the new credit block
system_info.credit_block.freeplay = freeplay;
// set the new dip_switch_block
system_info.dip_switch_block.dip_switches = system;
// calculate the new checksum, skip the old crc32 value
// which is at the beginning of the block, thats's why the +4
// conver the struct to chars in order for the crc32 calculation to work
system_info.credit_block.checksum =
crc32((char *)&system_info.credit_block + 4, BLOCK_SIZE - 4, 0);
system_info.dip_switch_block.checksum =
crc32((char *)&system_info.dip_switch_block + 4, BLOCK_SIZE - 4, 0);
// build the new credit block
memcpy(block, (char *)&system_info.credit_block, BLOCK_SIZE);
memcpy(system_info.data, block, BLOCK_SIZE);
memcpy(system_info.data + 0x3000, block, BLOCK_SIZE);
// build the new dip switch block
memcpy(block, (char *)&system_info.dip_switch_block, BLOCK_SIZE);
memcpy(system_info.data + 0x2800, block, BLOCK_SIZE);
memcpy(system_info.data + 0x5800, block, BLOCK_SIZE);
// print the dip_switch_block in hex
/*
dprintf("System Block: ");
for (size_t i = 0; i < BLOCK_SIZE; i++)
{
dprintf("%02X ", ((uint8_t *)&system_info.dip_switch_block)[i]);
}
dprintf("\n");
*/
WriteFile(h_sysfile, system_info.data, 0x6000, &sysfile_bytes_written, NULL);
CloseHandle(h_sysfile);
if (sysfile_bytes_written != 0x6000) {
dprintf("System: Only 0x%04lX bytes written out of 0x6000!\n", sysfile_bytes_written);
}
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "platform/vfs.h"
struct dipsw_config
{
wchar_t label[MAX_PATH];
wchar_t on[MAX_PATH];
wchar_t off[MAX_PATH];
};
struct system_config {
bool enable;
bool freeplay;
bool dipsw[8];
struct dipsw_config dipsw_config[8];
};
HRESULT system_init(const struct system_config *cfg, const struct vfs_config *vfs_cfg);
+543
View File
@@ -0,0 +1,543 @@
#include <windows.h>
#include <shlwapi.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "hooklib/path.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "hook/table.h"
#include "platform/vfs.h"
#include "util/dprintf.h"
static void vfs_fixup_path(wchar_t *path, size_t max_count);
static HRESULT vfs_mkdir_rec(const wchar_t *path);
static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count);
static HRESULT vfs_path_hook_nthome(
const wchar_t *src,
wchar_t *dest,
size_t *count);
static HRESULT vfs_path_hook_w10home(
const wchar_t *src,
wchar_t *dest,
size_t *count);
static HRESULT vfs_path_hook_option(
const wchar_t *src,
wchar_t *dest,
size_t *count);
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes);
static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes);
static wchar_t* hook_System_getAppRootPath();
static wchar_t* (*next_System_getAppRootPath)();
static wchar_t* hook_AppImage_getOptionMountRootPath();
static wchar_t* (*next_AppImage_getOptionMountRootPath)();
static const struct hook_symbol amdaemon_syms[] = {
{
.name = "System_getAppRootPath",
.patch = hook_System_getAppRootPath,
.link = (void **) &next_System_getAppRootPath,
},
{
.name = "AppImage_getOptionMountRootPath",
.patch = hook_AppImage_getOptionMountRootPath,
.link = (void **) &next_AppImage_getOptionMountRootPath,
},
};
static wchar_t game[5] = {0};
static wchar_t vfs_nthome_real[MAX_PATH];
static const wchar_t vfs_nthome[] = L"C:\\Documents and Settings\\AppUser";
static const size_t vfs_nthome_len = _countof(vfs_nthome) - 1;
static const wchar_t vfs_w10home[] = L"C:\\Users\\AppUser";
static const size_t vfs_w10home_len = _countof(vfs_w10home) - 1;
static const wchar_t vfs_option[] = L"C:\\Mount\\Option";
static const size_t vfs_option_len = _countof(vfs_option) - 1;
static const struct reg_hook_val vfs_reg_vals[] = {
{
.name = L"AMFS",
.read = vfs_reg_read_amfs,
.type = REG_SZ,
}, {
.name = L"APPDATA",
.read = vfs_reg_read_appdata,
.type = REG_SZ
},
};
static struct vfs_config vfs_config;
HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id)
{
wchar_t temp[MAX_PATH];
size_t nthome_len;
DWORD home_ok;
HRESULT hr;
assert(config != NULL);
if (!config->enable) {
return S_FALSE;
}
mbstowcs(game, game_id, 4);
if (config->amfs[0] == L'\0') {
dprintf("Vfs: FATAL: AMFS path not specified in INI file\n");
return E_FAIL;
}
if (config->appdata[0] == L'\0') {
dprintf("Vfs: FATAL: APPDATA path not specified in INI file\n");
return E_FAIL;
}
if (config->option[0] == L'\0') {
dprintf("Vfs: WARNING: OPTION path not specified in INI file\n");
} else if (!PathFileExistsW(config->option)) {
dprintf("Vfs: FATAL: OPTION path does not exist\n");
dprintf(" Configured: \"%ls\"\n", config->option);
GetFullPathNameW(config->option, _countof(temp), temp, NULL);
dprintf(" Expanded: \"%ls\"\n", temp);
return E_FAIL;
} else if (!(GetFileAttributesW(config->option) & FILE_ATTRIBUTE_DIRECTORY)) {
dprintf("Vfs: FATAL: OPTION path doesn't point to a directory\n");
dprintf(" Configured: \"%ls\"\n", config->option);
GetFullPathNameW(config->option, _countof(temp), temp, NULL);
dprintf(" Expanded: \"%ls\"\n", temp);
return E_FAIL;
}
home_ok = GetEnvironmentVariableW(
L"USERPROFILE",
vfs_nthome_real,
_countof(vfs_nthome_real));
if (!home_ok) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Vfs: Failed to query %%USERPROFILE%% env var: %x\n",
(int) hr);
return hr;
}
memcpy(&vfs_config, config, sizeof(*config));
vfs_fixup_path(vfs_nthome_real, _countof(vfs_nthome_real));
vfs_fixup_path(vfs_config.amfs, _countof(vfs_config.amfs));
vfs_fixup_path(vfs_config.appdata, _countof(vfs_config.appdata));
if (vfs_config.option[0] != L'\0') {
vfs_fixup_path(vfs_config.option, _countof(vfs_config.option));
}
hr = vfs_mkdir_rec(vfs_config.amfs);
if (FAILED(hr)) {
dprintf("Vfs: Failed to create AMFS dir %S: %x\n",
config->amfs,
(int) hr);
}
hr = vfs_mkdir_rec(vfs_config.appdata);
if (FAILED(hr)) {
dprintf("Vfs: Failed to create APPDATA dir %S: %x\n",
config->appdata,
(int) hr);
dprintf("Vfs: NOTE: SEGA Y: drive APPDATA, not Windows %%APPDATA%%.\n");
}
/* Need to create the temp subdirectory, not just nthome itself */
nthome_len = wcslen(vfs_nthome_real);
wcscpy_s(temp, _countof(temp), vfs_nthome_real);
wcscpy_s(temp + nthome_len, _countof(temp) - nthome_len, L"temp");
hr = vfs_mkdir_rec(temp);
if (FAILED(hr)) {
dprintf("Vfs: Failed to create %S: %x\n", temp, (int) hr);
}
/* Not auto-creating option directory as it is normally a read-only mount */
hr = path_hook_push(vfs_path_hook);
if (FAILED(hr)) {
return hr;
}
hr = path_hook_push(vfs_path_hook_nthome);
if (FAILED(hr)) {
return hr;
}
hr = path_hook_push(vfs_path_hook_w10home);
if (FAILED(hr)) {
return hr;
}
if (vfs_config.option[0] != L'\0') {
hr = path_hook_push(vfs_path_hook_option);
if (FAILED(hr)) {
return hr;
}
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\SEGA\\SystemProperty\\mount",
vfs_reg_vals,
_countof(vfs_reg_vals));
if (FAILED(hr)) {
return hr;
}
proc_addr_table_push(
NULL,
"amdaemon_api.dll",
amdaemon_syms,
_countof(amdaemon_syms)
);
return S_OK;
}
static void vfs_fixup_path(wchar_t *path, size_t max_count)
{
size_t count;
wchar_t abspath[MAX_PATH];
assert(path != NULL);
/* Requirement for PathIsRelativeW */
assert(max_count <= MAX_PATH);
if (PathIsRelativeW(path)) {
count = GetFullPathNameW(path, _countof(abspath), abspath, NULL);
/* GetFullPathName's length return value is tricky, because it includes
the NUL terminator on failure, but doesn't on success.
Check if it fits the temp buf (else it's a failure and includes NUL
anyway), then if it fits the target buf, NUL included. */
if (count == 0 || count > _countof(abspath) || count >= max_count) {
goto fail;
}
wcscpy_s(path, max_count, abspath);
} else {
count = wcslen(path);
}
if (path_is_separator_w(path[count - 1])) {
return;
}
if (count + 2 > max_count) {
goto fail;
}
path[count + 0] = L'\\';
path[count + 1] = L'\0';
return;
fail:
dprintf("Vfs: FATAL: Path too long: %S\n", path);
abort();
}
static HRESULT vfs_mkdir_rec(const wchar_t *path)
{
wchar_t *copy;
wchar_t *pos;
wchar_t wc;
HRESULT hr;
DWORD attr;
BOOL ok;
assert(path != NULL);
copy = _wcsdup(path);
if (copy == NULL) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto end;
}
pos = copy;
do {
wc = *pos;
if (wc == L'\0' || wc == L'/' || wc == L'\\') {
*pos = L'\0';
attr = GetFileAttributesW(copy);
if (attr == INVALID_FILE_ATTRIBUTES) {
ok = CreateDirectoryW(copy, NULL);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
}
*pos = wc;
}
pos++;
} while (wc != L'\0');
hr = S_OK;
end:
free(copy);
return hr;
}
static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count)
{
const wchar_t *redir;
size_t required;
size_t redir_len;
size_t src_len;
assert(src != NULL);
assert(count != NULL);
if (src[0] == L'\0' || src[1] != L':') {
return S_FALSE;
}
switch (src[0]) {
case L'e':
case L'E':
redir = vfs_config.amfs;
break;
case L'y':
case L'Y':
redir = vfs_config.appdata;
break;
default:
return S_FALSE;
}
/* GetFileAttributesW would request the src "E:", so fix the src_len in
in order to redirect the drive letter successfully */
src_len = path_is_separator_w(src[2]) ? 3 : 2;
/* Cut off <prefix>\, replace with redir path, count NUL terminator */
redir_len = wcslen(redir);
required = wcslen(src) - src_len + redir_len + 1;
if (dest != NULL) {
if (required > *count) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
wcscpy_s(dest, *count, redir);
wcscpy_s(dest + redir_len, *count - redir_len, src + src_len);
}
*count = required;
return S_OK;
}
static HRESULT vfs_path_hook_nthome(
const wchar_t *src,
wchar_t *dest,
size_t *count)
{
size_t required;
size_t redir_len;
size_t shift;
assert(src != NULL);
assert(count != NULL);
/* Case-insensitive check to see if src starts with vfs_nthome */
if (path_compare_w(src, vfs_nthome, vfs_nthome_len) != 0) {
return S_FALSE;
}
/* Check if the character after vfs_nthome is a separator or the end of
the string */
if (!path_is_separator_w(src[vfs_nthome_len]) &&
src[vfs_nthome_len] != L'\0')
{
return S_FALSE;
}
/* Cut off the matched <prefix>\, add the replaced prefix, count NUL */
shift = path_is_separator_w(src[vfs_nthome_len]) ? 1 : 0;
redir_len = wcslen(vfs_nthome_real);
required = wcslen(src) - vfs_nthome_len - shift + redir_len + 1;
if (dest != NULL) {
if (required > *count) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
wcscpy_s(dest, *count, vfs_nthome_real);
wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_nthome_len + shift);
}
*count = required;
return S_OK;
}
static HRESULT vfs_path_hook_w10home(
const wchar_t *src,
wchar_t *dest,
size_t *count)
{
size_t required;
size_t redir_len;
size_t shift;
assert(src != NULL);
assert(count != NULL);
/* Case-insensitive check to see if src starts with vfs_w10home */
if (path_compare_w(src, vfs_w10home, vfs_w10home_len) != 0) {
return S_FALSE;
}
/* Check if the character after vfs_w10home is a separator or the end of
the string */
if (!path_is_separator_w(src[vfs_w10home_len]) &&
src[vfs_w10home_len] != L'\0')
{
return S_FALSE;
}
/* Cut off the matched <prefix>\, add the replaced prefix, count NUL */
shift = path_is_separator_w(src[vfs_w10home_len]) ? 1 : 0;
redir_len = wcslen(vfs_nthome_real);
required = wcslen(src) - vfs_w10home_len - shift + redir_len + 1;
if (dest != NULL) {
if (required > *count) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
wcscpy_s(dest, *count, vfs_nthome_real);
wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_w10home_len + shift);
}
*count = required;
return S_OK;
}
static HRESULT vfs_path_hook_option(
const wchar_t *src,
wchar_t *dest,
size_t *count)
{
size_t required;
size_t redir_len;
size_t shift;
assert(src != NULL);
assert(count != NULL);
/* Case-insensitive check to see if src starts with vfs_option */
if (path_compare_w(src, vfs_option, vfs_option_len) != 0) {
return S_FALSE;
}
/* Check if the character after vfs_nthome is a separator or the end of
the string */
if (!path_is_separator_w(src[vfs_option_len]) &&
src[vfs_option_len] != L'\0')
{
return S_FALSE;
}
/* Cut off the matched <prefix>\, add the replaced prefix, count NUL */
shift = path_is_separator_w(src[vfs_option_len]) ? 1 : 0;
redir_len = wcslen(vfs_config.option);
required = wcslen(src) - vfs_option_len - shift + redir_len + 1;
if (dest != NULL) {
if (required > *count) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
wcscpy_s(dest, *count, vfs_config.option);
wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_option_len + shift);
}
*count = required;
return S_OK;
}
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"E:\\");
}
static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"Y:\\");
}
static wchar_t* hook_System_getAppRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.appdata);
wcscat_s(path, MAX_PATH, game);
wcscat_s(path, MAX_PATH, L"\\");
return path;
}
static wchar_t* hook_AppImage_getOptionMountRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.option);
return path;
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
struct vfs_config {
bool enable;
wchar_t amfs[MAX_PATH];
wchar_t appdata[MAX_PATH];
wchar_t option[MAX_PATH];
};
HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id);