forked from TeamTofuShop/segatools
APMv3: add hook (#73)
This adds support for APMv3 I/O, menus and the launcher. * Added a apm3hook dll and I/O based on the usual layout. * Added C:\Mount\Apm to vfs. * Added the relevant .dlls to unityhook. * Added a hook for apmmount.dll that uses `CreateDosDevice` to mount decrypted data to the locations the launcher and games expect files to be. This will conflict with anything that is already at W:\ and X:\, but I do not have better solutions for this. * `launch.bat` is a bit more involved as it simulates the launcher loop. It can be broken by alt+f4ing or closing the launcher with "X". * An extra export was added, so rundll32 can be used to get rid of the dosdevices after the launcher was killed. * Since all the games do everything via `X:\lib\apm.dll`, no game hooks were needed in testing, therefore, `game.bat` files can be used as is. * Path hooks are applied correctly, so you can go correctly between games, launcher, sub system test mode and game test modes. A setup guide (some stuff specific to my server) can be found here: https://gmg.hopto.org:82/gmg/wiki/index.php/All.Net_P-ras_Multi_Menu Tested with the 2 APM sample apps, Blazblue, Puyo, Guilty Gear and some weird unity puzzle game whose name I forgot.   Reviewed-on: TeamTofuShop/segatools#73 Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com> Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
This commit is contained in:
@ -17,7 +17,6 @@
|
||||
|
||||
static void path_hook_init(void);
|
||||
static BOOL path_transform_a(char **out, const char *src);
|
||||
static BOOL path_transform_w(wchar_t **out, const wchar_t *src);
|
||||
|
||||
/* API hooks */
|
||||
|
||||
@ -490,7 +489,7 @@ end:
|
||||
return ok;
|
||||
}
|
||||
|
||||
static BOOL path_transform_w(wchar_t **out, const wchar_t *src)
|
||||
BOOL path_transform_w(wchar_t **out, const wchar_t *src)
|
||||
{
|
||||
BOOL ok;
|
||||
HRESULT hr;
|
||||
|
@ -13,6 +13,7 @@ typedef HRESULT (*path_hook_t)(
|
||||
HRESULT path_hook_push(path_hook_t hook);
|
||||
void path_hook_insert_hooks(HMODULE target);
|
||||
int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count);
|
||||
BOOL path_transform_w(wchar_t **out, const wchar_t *src);
|
||||
|
||||
static inline bool path_is_separator_w(wchar_t c)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ platform_lib = static_library(
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
shlwapi_lib,
|
||||
bcrypt_lib,
|
||||
],
|
||||
sources : [
|
||||
'amvideo.c',
|
||||
@ -31,6 +32,8 @@ platform_lib = static_library(
|
||||
'pcbid.h',
|
||||
'platform.c',
|
||||
'platform.h',
|
||||
'security.c',
|
||||
'security.h',
|
||||
'vfs.c',
|
||||
'vfs.h',
|
||||
'system.c',
|
||||
|
210
common/platform/security.c
Normal file
210
common/platform/security.c
Normal file
@ -0,0 +1,210 @@
|
||||
#include <windows.h>
|
||||
#include <bcrypt.h>
|
||||
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000)
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "platform/security.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
static NTSTATUS __stdcall my_BCryptDecrypt(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags);
|
||||
|
||||
static NTSTATUS (__stdcall *next_BCryptDecrypt)(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags);
|
||||
|
||||
static NTSTATUS __stdcall my_BCryptEncrypt(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags);
|
||||
|
||||
static NTSTATUS (__stdcall *next_BCryptEncrypt)(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags);
|
||||
|
||||
static const struct hook_symbol security_hook_syms[] = {
|
||||
{
|
||||
.name = "BCryptDecrypt",
|
||||
.patch = my_BCryptDecrypt,
|
||||
.link = (void **) &next_BCryptDecrypt,
|
||||
},
|
||||
{
|
||||
.name = "BCryptEncrypt",
|
||||
.patch = my_BCryptEncrypt,
|
||||
.link = (void **) &next_BCryptEncrypt,
|
||||
}
|
||||
};
|
||||
|
||||
void LogKeyProperties(BCRYPT_KEY_HANDLE hKey) {
|
||||
NTSTATUS status;
|
||||
ULONG cbData;
|
||||
|
||||
// Retrieve the algorithm name
|
||||
WCHAR algorithmName[256];
|
||||
ULONG cbAlgorithmName = sizeof(algorithmName);
|
||||
|
||||
status = BCryptGetProperty(hKey, BCRYPT_ALGORITHM_NAME, (PUCHAR) algorithmName, cbAlgorithmName * sizeof(WCHAR),
|
||||
&cbData, 0);
|
||||
if (status == STATUS_SUCCESS) {
|
||||
dprintf("Algorithm: %ls\n", algorithmName);
|
||||
} else {
|
||||
dprintf("Algorithm: failed to retrieve algorithm name (0x%08lX)\n", status);
|
||||
}
|
||||
|
||||
// Export the key value
|
||||
DWORD keyBlobSize = 0;
|
||||
status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, NULL, 0, &keyBlobSize, 0);
|
||||
if (status != STATUS_SUCCESS) {
|
||||
dprintf("KEY: failed to determine key blob size (0x%08lX)\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
PUCHAR keyBlob = (PUCHAR) malloc(keyBlobSize);
|
||||
if (!keyBlob) {
|
||||
dprintf("KEY: failed the memory allocation for key blob.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, keyBlob, keyBlobSize, &keyBlobSize, 0);
|
||||
if (status == STATUS_SUCCESS) {
|
||||
int headerSize = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER);
|
||||
dprintf("KEY (%lu bytes):\n", keyBlobSize - headerSize);
|
||||
dump(keyBlob + headerSize, keyBlobSize - headerSize);
|
||||
} else {
|
||||
dprintf("KEY: failed to export key value (0x%08lX)\n", status);
|
||||
}
|
||||
|
||||
free(keyBlob);
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS __stdcall my_BCryptDecrypt(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags) {
|
||||
dprintf("BCrypt: Decrypt\n");
|
||||
dprintf("FLAGS: %lx\n", dwFlags);
|
||||
LogKeyProperties(hKey);
|
||||
dprintf("IV:\n");
|
||||
dump(pbIV, cbIV);
|
||||
dprintf("ENCRYPTED:\n");
|
||||
dump(pbInput, cbInput);
|
||||
NTSTATUS ret = next_BCryptDecrypt(hKey, pbInput, cbInput, pPaddingInfo, pbIV, cbIV, pbOutput, cbOutput, pcbResult,
|
||||
dwFlags);
|
||||
dprintf("Operation Result: %lx\n", ret);
|
||||
if (ret == 0) {
|
||||
dprintf("Decrypted (%ld, max buf: %ld):\n", *pcbResult, cbOutput);
|
||||
if (pbOutput != NULL) {
|
||||
dump(pbOutput, *pcbResult);
|
||||
} else {
|
||||
dprintf("pbOutput == NULL!\n");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS __stdcall my_BCryptEncrypt(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags) {
|
||||
dprintf("BCrypt: Encrypt\n");
|
||||
dprintf("FLAGS: %lx\n", dwFlags);
|
||||
LogKeyProperties(hKey);
|
||||
dprintf("IV:\n");
|
||||
dump(pbIV, cbIV);
|
||||
dprintf("Input data (%ld):\n", cbInput);
|
||||
dump(pbOutput, *pcbResult);
|
||||
NTSTATUS ret = next_BCryptEncrypt(hKey, pbInput, cbInput, pPaddingInfo, pbIV, cbIV, pbOutput, cbOutput, pcbResult,
|
||||
dwFlags);
|
||||
dprintf("Operation Result: %lx\n", ret);
|
||||
if (ret == 0) {
|
||||
dprintf("First 16 bytes of encrypted data (%ld, max buf: %ld):\n", *pcbResult, cbOutput);
|
||||
if (pbOutput != NULL) {
|
||||
dump(pbOutput, min(16, *pcbResult));
|
||||
} else {
|
||||
dprintf("pbOutput == NULL!\n");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
HRESULT security_hook_init() {
|
||||
security_hook_insert_hooks(NULL);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void security_hook_insert_hooks(HMODULE mod) {
|
||||
|
||||
char value[8];
|
||||
if (!GetEnvironmentVariableA("BCRYPT_DUMP_ENABLED", value, 8)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value[0] != '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("BCrypt: dumping enabled\n");
|
||||
|
||||
hook_table_apply(
|
||||
mod,
|
||||
"BCrypt.dll",
|
||||
security_hook_syms,
|
||||
_countof(security_hook_syms));
|
||||
}
|
9
common/platform/security.h
Normal file
9
common/platform/security.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
HRESULT security_hook_init();
|
||||
void security_hook_insert_hooks(HMODULE mod);
|
@ -31,6 +31,10 @@ static HRESULT vfs_path_hook_option(
|
||||
const wchar_t *src,
|
||||
wchar_t *dest,
|
||||
size_t *count);
|
||||
static HRESULT vfs_path_hook_apm(
|
||||
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);
|
||||
|
||||
@ -64,6 +68,9 @@ 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 wchar_t vfs_apm3[] = L"C:\\Mount\\Apm";
|
||||
static const size_t vfs_apm3_len = _countof(vfs_apm3) - 1;
|
||||
|
||||
static const struct reg_hook_val vfs_reg_vals[] = {
|
||||
{
|
||||
.name = L"AMFS",
|
||||
@ -199,6 +206,11 @@ HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id)
|
||||
if (vfs_config.option[0] != L'\0') {
|
||||
hr = path_hook_push(vfs_path_hook_option);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
hr = path_hook_push(vfs_path_hook_apm);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
@ -514,6 +526,53 @@ static HRESULT vfs_path_hook_option(
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT vfs_path_hook_apm(
|
||||
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_apm */
|
||||
|
||||
if (path_compare_w(src, vfs_apm3, vfs_apm3_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_apm3_len]) &&
|
||||
src[vfs_apm3_len] != L'\0')
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
/* Cut off the matched <prefix>\, add the replaced prefix, count NUL */
|
||||
|
||||
shift = path_is_separator_w(src[vfs_apm3_len]) ? 1 : 0;
|
||||
redir_len = wcslen(vfs_config.option);
|
||||
required = wcslen(src) - vfs_apm3_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_apm3_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:\\");
|
||||
|
@ -34,7 +34,11 @@ static const wchar_t *target_modules[] = {
|
||||
L"C300FWDLusb.dll",
|
||||
L"apmled.dll",
|
||||
L"HKBSys_api.dll",
|
||||
L"amptw.dll"
|
||||
L"amptw.dll",
|
||||
L"apmmount.dll",
|
||||
L"abaasgs.dll",
|
||||
L"AVProVideo.dll",
|
||||
L"Audio360.dll",
|
||||
};
|
||||
|
||||
static const size_t target_modules_len = _countof(target_modules);
|
||||
|
Reference in New Issue
Block a user