From 0f8ce56d96a90bded5319e91d4f019d60afc0efe Mon Sep 17 00:00:00 2001 From: Farewell_ Date: Tue, 30 May 2023 16:54:35 +0200 Subject: [PATCH] Added support for SmartCard readers Added configuration file; Fixed a possible race condition; Cleaned up the mess that used to be my console logs; --- .gitignore | 4 +- README.md | 16 +- build64.bat | 3 +- dist/cardreader.toml | 8 + meson.build | 4 +- src/cardio_plugin/dllmain.c | 162 --------- src/{cardio.def => cardreader.def} | 2 +- .../CardIO}/cardio.c | 251 ++++++++----- .../CardIO}/cardio.h | 2 +- src/cardreader_plugin/SmartCard/scard.c | 334 ++++++++++++++++++ src/cardreader_plugin/SmartCard/scard.h | 44 +++ src/cardreader_plugin/dllmain.c | 226 ++++++++++++ src/helpers.h | 139 ++++---- src/meson.build | 13 +- 14 files changed, 867 insertions(+), 341 deletions(-) create mode 100644 dist/cardreader.toml delete mode 100644 src/cardio_plugin/dllmain.c rename src/{cardio.def => cardreader.def} (72%) rename src/{cardio_plugin => cardreader_plugin/CardIO}/cardio.c (69%) rename src/{cardio_plugin => cardreader_plugin/CardIO}/cardio.h (98%) create mode 100644 src/cardreader_plugin/SmartCard/scard.c create mode 100644 src/cardreader_plugin/SmartCard/scard.h create mode 100644 src/cardreader_plugin/dllmain.c diff --git a/.gitignore b/.gitignore index 2dc95e2..c6d6ec9 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,6 @@ dkms.conf .vscode/ subprojects/minhook -subprojects/tomlc99 \ No newline at end of file +subprojects/tomlc99 + +bin/* \ No newline at end of file diff --git a/README.md b/README.md index 02b15d0..b005c33 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# TAL CardIO +# TAL CardReader -This is a plugin that allows you to use USB HID card readers (cardio) with [TaikoArcadeLoader](https://github.com/BroGamer4256/TaikoArcadeLoader) +This is a plugin that allows you to use USB HID card (cardio) and SmartCard readers with [TaikoArcadeLoader](https://github.com/BroGamer4256/TaikoArcadeLoader) # Acknowledgments This is a plugin destined to be used with [BroGamer4256](https://github.com/BroGamer4256)'s TaikoArcadeLoader. -CardIO implementation taken from [Spicetools](https://github.com/spicetools/spicetools) and adapted with the help of [CrazyRedMachine](https://github.com/CrazyRedMachine) (Huge thanks!) +CardIO and SmartCard implementation taken from [Spicetools](https://github.com/spicetools/spicetools) and adapted with the help of [CrazyRedMachine](https://github.com/CrazyRedMachine) (Huge thanks!) Initial project setup done with the help of [Stepland](https://github.com/Stepland) @@ -19,4 +19,12 @@ To build this, you'll need two things : Once you've edited your build64.bat file to point to your local installation of the VS2022 build tools, you should be good to go. -Copy the cardio.dll to your TAL's plugin folder. You should be able to see the plugin initializing in the game's console. +Copy cardreader.dll to your TAL's plugin folder. You should be able to see the plugin initializing in the game's console. + +# Settings + +- using_smartcard (default : false) + _Reader type defaults to CardIO, if you want to use a SmartCard reader, set this to true_. + +- read_cooldown (default : 20) + _Cooldown between each read attempt, a low value (below 15) can lead to game crashes, a high value (above 500) will break SmartCard readers_. diff --git a/build64.bat b/build64.bat index 221b9ca..0ca68da 100644 --- a/build64.bat +++ b/build64.bat @@ -7,5 +7,6 @@ meson configure build64 ninja -C build64 mkdir bin -copy build64\src\cardio.dll bin\cardio.dll +copy build64\src\cardreader.dll bin\cardreader.dll +copy dist\cardreader.toml bin\cardreader.toml pause \ No newline at end of file diff --git a/dist/cardreader.toml b/dist/cardreader.toml new file mode 100644 index 0000000..42c5e07 --- /dev/null +++ b/dist/cardreader.toml @@ -0,0 +1,8 @@ +# Smart Card Reader ================== +using_smartcard = false + +# Card Read Cooldown ================= +read_cooldown = 20 +# Default is 20. Set to a higher value if you notice any weird behaviour. +# Too small of a value can lead to weird behaviour and game crashes. +# Too high of a value will result in an unresponsive card reader. \ No newline at end of file diff --git a/meson.build b/meson.build index e0729a9..c9582b1 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( - 'TAL_CardIO', + 'TAL_CardReader', 'c', - version : '0.1.0' + version : '0.2.0' ) inc = include_directories('.') diff --git a/src/cardio_plugin/dllmain.c b/src/cardio_plugin/dllmain.c deleted file mode 100644 index 96052e4..0000000 --- a/src/cardio_plugin/dllmain.c +++ /dev/null @@ -1,162 +0,0 @@ -#include "cardio.h" -char module[] = "TAL HID"; - -static bool CARDIO_RUNNER_INITIALIZED = false; - -static HANDLE CARDIO_POLL_THREAD; -static bool CARDIO_POLL_STOP_FLAG; -static bool waitingForTouch = false; -static bool HasCard = false; - -typedef void (*callbackTouch)(i32, i32, u8[168], u64); -callbackTouch touchCallback; -u64 touchData; -char AccessID[21] = "00000000000000000001"; -static u8 cardData[168] = {0x01, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x2E, 0x58, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x5C, 0x97, 0x44, 0xF0, 0x88, 0x04, 0x00, 0x43, 0x26, 0x2C, 0x33, 0x00, 0x04, - 0x06, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x42, 0x47, 0x49, 0x43, 0x36, - 0x00, 0x00, 0xFA, 0xE9, 0x69, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -void PollReader() -{ - if (HasCard) // A card scan is already in progress - { - Sleep(1000); - return; - } - - // update HID devices - EnterCriticalSection(&CARDIO_HID_CRIT_SECTION); - for (size_t device_no = 0; device_no < CARDIO_HID_CONTEXTS_LENGTH; device_no++) - { - struct cardio_hid_device *device = &CARDIO_HID_CONTEXTS[device_no]; - - // get status - cardio_hid_poll_value_t status = cardio_hid_device_poll(device); - if (status == HID_POLL_CARD_READY) - { - - // read card - if (cardio_hid_device_read(device) == HID_CARD_NONE) - continue; - - // if card not empty - if (*((uint64_t *)&device->u.usage_value[0]) > 0) - { - printWarning("%s (%s): Read card %02X%02X%02X%02X%02X%02X%02X%02X\n", __func__, module, device->u.usage_value[0], device->u.usage_value[1], device->u.usage_value[2], device->u.usage_value[3], device->u.usage_value[4], device->u.usage_value[5], device->u.usage_value[6], device->u.usage_value[7]); - - if (waitingForTouch) - { - // Properly format the AccessID - u64 ReversedAccessID; - for (int i = 0; i < 8; i++) - ReversedAccessID = (ReversedAccessID << 8) | device->u.usage_value[i]; - sprintf(AccessID, "%020llu", ReversedAccessID); - - HasCard = true; - } - } - } - } - LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION); - Sleep(15); -} - -static unsigned int __stdcall cardio_poll_thread_proc(void *ctx) -{ - while (!CARDIO_POLL_STOP_FLAG) - { - PollReader(); - } - return 0; -} - -void cardio_runner_start() -{ - // initialize - if (!CARDIO_RUNNER_INITIALIZED) - { - CARDIO_RUNNER_INITIALIZED = true; - printWarning("%s (%s): Initializing CARDIO\n", __func__, module); - - // initialize - if (!cardio_hid_init()) - { - printWarning("%s (%s): Couldn't init CARDIO HID\n", __func__, module); - return; - } - - // scan HID devices - if (!cardio_hid_scan()) - { - printWarning("%s (%s): Couldn't scan for CARDIO devices\n", __func__, module); - return; - } - } -} - -void cardio_runner_stop() -{ - // shutdown HID - cardio_hid_close(); - - // set initialized to false - CARDIO_RUNNER_INITIALIZED = false; -} - -void Init() -{ - printWarning("%s (%s): Starting HID Service...\n", __func__, module); - // Find and initialize reader(s) - cardio_runner_start(); - - // Start reader thread - CARDIO_POLL_STOP_FLAG = false; - CARDIO_POLL_THREAD = (HANDLE)_beginthreadex( - NULL, - 0, - cardio_poll_thread_proc, - NULL, - 0, - NULL); -} - -void Update() -{ - if (HasCard) - { - HasCard = false; - - // Generating a ChipID Derived from the AccessID - char ChipID[33] = "000000000000"; - strcat(ChipID, AccessID); - - // Insert card in game - printWarning("%s (%s): Inserting Card %s, with ChipID %s\n", __func__, module, AccessID, ChipID); - memcpy(cardData + 0x2C, ChipID, 33); - memcpy(cardData + 0x50, AccessID, 21); - touchCallback(0, 0, cardData, touchData); - } -} - -void WaitTouch(i32 (*callback)(i32, i32, u8[168], u64), u64 data) -{ - printWarning("%s (%s): Waiting for touch\n", __func__, module); - waitingForTouch = true; - touchCallback = callback; - touchData = data; -} - -void Exit() -{ - cardio_runner_stop(); - CARDIO_POLL_STOP_FLAG = true; - WaitForSingleObject(CARDIO_POLL_THREAD, INFINITE); - CloseHandle(CARDIO_POLL_THREAD); - CARDIO_POLL_THREAD = NULL; - CARDIO_POLL_STOP_FLAG = false; -} diff --git a/src/cardio.def b/src/cardreader.def similarity index 72% rename from src/cardio.def rename to src/cardreader.def index 340261d..8bcab10 100644 --- a/src/cardio.def +++ b/src/cardreader.def @@ -1,4 +1,4 @@ -LIBRARY cardio +LIBRARY cardreader EXPORTS Init diff --git a/src/cardio_plugin/cardio.c b/src/cardreader_plugin/CardIO/cardio.c similarity index 69% rename from src/cardio_plugin/cardio.c rename to src/cardreader_plugin/CardIO/cardio.c index f6328d1..f766776 100644 --- a/src/cardio_plugin/cardio.c +++ b/src/cardreader_plugin/CardIO/cardio.c @@ -38,7 +38,8 @@ CRITICAL_SECTION CARDIO_HID_CRIT_SECTION; struct cardio_hid_device *CARDIO_HID_CONTEXTS = NULL; size_t CARDIO_HID_CONTEXTS_LENGTH = 0; -void hid_ctx_init(struct cardio_hid_device *ctx) { +void hid_ctx_init(struct cardio_hid_device *ctx) +{ ctx->dev_path = NULL; ctx->dev_handle = INVALID_HANDLE_VALUE; ctx->initialized = FALSE; @@ -54,30 +55,36 @@ void hid_ctx_init(struct cardio_hid_device *ctx) { memset(&ctx->caps, 0, sizeof(HIDP_CAPS)); } -void hid_ctx_free(struct cardio_hid_device *ctx) { - if (ctx->dev_path != NULL) { +void hid_ctx_free(struct cardio_hid_device *ctx) +{ + if (ctx->dev_path != NULL) + { HeapFree(GetProcessHeap(), 0, ctx->dev_path); ctx->dev_path = NULL; } - if (ctx->dev_handle != INVALID_HANDLE_VALUE) { + if (ctx->dev_handle != INVALID_HANDLE_VALUE) + { CancelIo(ctx->dev_handle); CloseHandle(ctx->dev_handle); ctx->dev_handle = INVALID_HANDLE_VALUE; } - if (ctx->pp_data != NULL) { + if (ctx->pp_data != NULL) + { HidD_FreePreparsedData(ctx->pp_data); ctx->pp_data = NULL; } - if (ctx->collection != NULL) { + if (ctx->collection != NULL) + { HeapFree(GetProcessHeap(), 0, ctx->collection); ctx->collection = NULL; } } -void hid_ctx_reset(struct cardio_hid_device *ctx) { +void hid_ctx_reset(struct cardio_hid_device *ctx) +{ ctx->initialized = FALSE; ctx->io_pending = FALSE; ctx->read_size = 0; @@ -91,31 +98,37 @@ void hid_ctx_reset(struct cardio_hid_device *ctx) { memset(&ctx->caps, 0, sizeof(HIDP_CAPS)); } -BOOL cardio_hid_init() { +BOOL cardio_hid_init() +{ size_t i, contexts_size; InitializeCriticalSectionAndSpinCount(&CARDIO_HID_CRIT_SECTION, 0x00000400); contexts_size = DEFAULT_ALLOCATED_CONTEXTS * sizeof(struct cardio_hid_device); - CARDIO_HID_CONTEXTS = (struct cardio_hid_device *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, contexts_size); - if (CARDIO_HID_CONTEXTS == NULL) { + CARDIO_HID_CONTEXTS = (struct cardio_hid_device *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, contexts_size); + if (CARDIO_HID_CONTEXTS == NULL) + { return FALSE; } CARDIO_HID_CONTEXTS_LENGTH = DEFAULT_ALLOCATED_CONTEXTS; - for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) { + for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) + { hid_ctx_init(&CARDIO_HID_CONTEXTS[i]); } return TRUE; } -void cardio_hid_close() { +void cardio_hid_close() +{ size_t i; - if (CARDIO_HID_CONTEXTS_LENGTH > 0) { - for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) { + if (CARDIO_HID_CONTEXTS_LENGTH > 0) + { + for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) + { hid_ctx_free(&CARDIO_HID_CONTEXTS[i]); } @@ -127,14 +140,17 @@ void cardio_hid_close() { } } -BOOL cardio_hid_add_device(LPCWSTR device_path) { +BOOL cardio_hid_add_device(LPCWSTR device_path) +{ BOOL res = FALSE; size_t i; EnterCriticalSection(&CARDIO_HID_CRIT_SECTION); - for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) { - if (!CARDIO_HID_CONTEXTS[i].initialized) { + for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) + { + if (!CARDIO_HID_CONTEXTS[i].initialized) + { res = cardio_hid_scan_device(&CARDIO_HID_CONTEXTS[i], device_path); break; } @@ -145,16 +161,19 @@ BOOL cardio_hid_add_device(LPCWSTR device_path) { return res; } -BOOL cardio_hid_remove_device(LPCWSTR device_path) { +BOOL cardio_hid_remove_device(LPCWSTR device_path) +{ BOOL res = FALSE; size_t i; EnterCriticalSection(&CARDIO_HID_CRIT_SECTION); - for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) { + for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) + { // The device paths in `hid_scan` are partially lower-case, so perform a // case-insensitive comparison here - if (CARDIO_HID_CONTEXTS[i].initialized && (_wcsicmp(device_path, CARDIO_HID_CONTEXTS[i].dev_path) == 0)) { + if (CARDIO_HID_CONTEXTS[i].initialized && (_wcsicmp(device_path, CARDIO_HID_CONTEXTS[i].dev_path) == 0)) + { hid_ctx_reset(&CARDIO_HID_CONTEXTS[i]); res = TRUE; break; @@ -169,68 +188,78 @@ BOOL cardio_hid_remove_device(LPCWSTR device_path) { /* * Scan HID device to see if it is a HID reader */ -BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path) { +BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path) +{ NTSTATUS res; size_t dev_path_size = (wcslen(device_path) + 1) * sizeof(WCHAR); - ctx->dev_path = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, dev_path_size); - if (ctx->dev_path == NULL) { + ctx->dev_path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dev_path_size); + if (ctx->dev_path == NULL) + { return FALSE; } memcpy(ctx->dev_path, device_path, dev_path_size); ctx->dev_path[dev_path_size - 1] = '\0'; ctx->dev_handle = CreateFileW( - ctx->dev_path, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, - NULL); - if (ctx->dev_handle == INVALID_HANDLE_VALUE) { - printWarning ("%s (%s): could not open device: %ls\n", __func__, module, ctx->dev_path); + ctx->dev_path, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + if (ctx->dev_handle == INVALID_HANDLE_VALUE) + { + printError("%s (%s): Could not open device: %ls\n", __func__, module, ctx->dev_path); HeapFree(GetProcessHeap(), 0, ctx->dev_path); ctx->dev_path = NULL; ctx->dev_handle = INVALID_HANDLE_VALUE; return FALSE; } - if (!HidD_GetPreparsedData(ctx->dev_handle, &ctx->pp_data)) { + if (!HidD_GetPreparsedData(ctx->dev_handle, &ctx->pp_data)) + { goto end; } res = HidP_GetCaps(ctx->pp_data, &ctx->caps); - if (res != HIDP_STATUS_SUCCESS) { + if (res != HIDP_STATUS_SUCCESS) + { goto end; } // 0xffca is the card reader usage page ID - if (ctx->caps.UsagePage != CARD_READER_USAGE_PAGE) { + if (ctx->caps.UsagePage != CARD_READER_USAGE_PAGE) + { goto end; - } else if (ctx->caps.NumberInputValueCaps == 0) { + } + else if (ctx->caps.NumberInputValueCaps == 0) + { goto end; } ctx->collection_length = ctx->caps.NumberInputValueCaps; - ctx->collection = (HIDP_VALUE_CAPS *) HeapAlloc(GetProcessHeap(), 0, - ctx->collection_length * sizeof(HIDP_VALUE_CAPS)); - if (ctx->collection == NULL) { + ctx->collection = (HIDP_VALUE_CAPS *)HeapAlloc(GetProcessHeap(), 0, + ctx->collection_length * sizeof(HIDP_VALUE_CAPS)); + if (ctx->collection == NULL) + { goto end; } res = HidP_GetValueCaps( - HidP_Input, - ctx->collection, - &ctx->collection_length, - ctx->pp_data); - if (res != HIDP_STATUS_SUCCESS) { + HidP_Input, + ctx->collection, + &ctx->collection_length, + ctx->pp_data); + if (res != HIDP_STATUS_SUCCESS) + { goto end; } - printWarning ("%s (%s): detected reader: %ls\n", __func__, module, ctx->dev_path); + printInfo("%s (%s): Detected reader: %ls\n", __func__, module, ctx->dev_path); ctx->initialized = TRUE; return TRUE; - end: +end: hid_ctx_reset(ctx); return FALSE; } @@ -242,7 +271,8 @@ BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path) * Usage 0x41 => ISO_15693 * Usage 0x42 => ISO_18092 (FeliCa) */ -BOOL cardio_hid_scan() { +BOOL cardio_hid_scan() +{ BOOL res = TRUE; SP_DEVINFO_DATA devinfo_data; SP_DEVICE_INTERFACE_DATA device_interface_data; @@ -259,7 +289,8 @@ BOOL cardio_hid_scan() { // HID collection opening needs `DIGCF_DEVICEINTERFACE` and ignore // disconnected devices device_info_set = SetupDiGetClassDevs(&hid_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if (device_info_set == INVALID_HANDLE_VALUE) { + if (device_info_set == INVALID_HANDLE_VALUE) + { res = FALSE; goto end; } @@ -270,44 +301,51 @@ BOOL cardio_hid_scan() { // `SetupDiEnumDeviceInterfaces` must come before `SetupDiEnumDeviceInfo` // else `SetupDiEnumDeviceInterfaces` will fail with error 259 - while (SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, device_index, &device_interface_data)) { + while (SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, device_index, &device_interface_data)) + { // Get the required size - if (SetupDiGetDeviceInterfaceDetailW(device_info_set, &device_interface_data, NULL, 0, &dwSize, NULL)) { + if (SetupDiGetDeviceInterfaceDetailW(device_info_set, &device_interface_data, NULL, 0, &dwSize, NULL)) + { goto cont; } - device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *) HeapAlloc(GetProcessHeap(), 0, dwSize); - if (device_interface_detail_data == NULL) { + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)HeapAlloc(GetProcessHeap(), 0, dwSize); + if (device_interface_detail_data == NULL) + { goto cont; } device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); - if (!SetupDiGetDeviceInterfaceDetailW(device_info_set, &device_interface_data, device_interface_detail_data, dwSize, NULL, NULL)) { + if (!SetupDiGetDeviceInterfaceDetailW(device_info_set, &device_interface_data, device_interface_detail_data, dwSize, NULL, NULL)) + { goto cont; } - if (!SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data)) { + if (!SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data)) + { goto cont; } - if (!IsEqualGUID((const void *)&hidclass_guid, (const void *)&devinfo_data.ClassGuid)) { + if (!IsEqualGUID((const void *)&hidclass_guid, (const void *)&devinfo_data.ClassGuid)) + { goto cont; } EnterCriticalSection(&CARDIO_HID_CRIT_SECTION); - if (hid_devices == CARDIO_HID_CONTEXTS_LENGTH) { + if (hid_devices == CARDIO_HID_CONTEXTS_LENGTH) + { CARDIO_HID_CONTEXTS_LENGTH++; - CARDIO_HID_CONTEXTS = (struct cardio_hid_device *) HeapReAlloc( - GetProcessHeap(), - HEAP_ZERO_MEMORY, - CARDIO_HID_CONTEXTS, - CARDIO_HID_CONTEXTS_LENGTH * sizeof(struct cardio_hid_device) - ); - if (CARDIO_HID_CONTEXTS == NULL) { + CARDIO_HID_CONTEXTS = (struct cardio_hid_device *)HeapReAlloc( + GetProcessHeap(), + HEAP_ZERO_MEMORY, + CARDIO_HID_CONTEXTS, + CARDIO_HID_CONTEXTS_LENGTH * sizeof(struct cardio_hid_device)); + if (CARDIO_HID_CONTEXTS == NULL) + { LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION); HeapFree(GetProcessHeap(), 0, device_interface_detail_data); res = FALSE; @@ -317,14 +355,16 @@ BOOL cardio_hid_scan() { hid_ctx_init(&CARDIO_HID_CONTEXTS[hid_devices]); } - if (cardio_hid_scan_device(&CARDIO_HID_CONTEXTS[hid_devices], device_interface_detail_data->DevicePath)) { + if (cardio_hid_scan_device(&CARDIO_HID_CONTEXTS[hid_devices], device_interface_detail_data->DevicePath)) + { hid_devices++; } LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION); - cont: - if (device_interface_detail_data) { + cont: + if (device_interface_detail_data) + { HeapFree(GetProcessHeap(), 0, device_interface_detail_data); device_interface_detail_data = NULL; } @@ -332,30 +372,36 @@ BOOL cardio_hid_scan() { device_index++; } - end: - if (device_info_set != INVALID_HANDLE_VALUE) { +end: + if (device_info_set != INVALID_HANDLE_VALUE) + { SetupDiDestroyDeviceInfoList(device_info_set); } return res; } -cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx) { +cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx) +{ DWORD error = 0; - if (!ctx->initialized) { + if (!ctx->initialized) + { return HID_POLL_ERROR; } - if (ctx->io_pending) { + if (ctx->io_pending) + { // Do this if inside to not have more `ReadFile` overlapped I/O requests. // If there are more calls to `ReadFile` than `GetOverlappedResult` then // eventually the working set quota will run out triggering error 1426 // (ERROR_WORKING_SET_QUOTA). - if (HasOverlappedIoCompleted(&ctx->read_state)) { + if (HasOverlappedIoCompleted(&ctx->read_state)) + { ctx->io_pending = FALSE; - if (!GetOverlappedResult(ctx->dev_handle, &ctx->read_state, &ctx->read_size, FALSE)) { + if (!GetOverlappedResult(ctx->dev_handle, &ctx->read_state, &ctx->read_size, FALSE)) + { return HID_POLL_ERROR; } @@ -363,21 +409,29 @@ cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx) { return HID_POLL_CARD_READY; } - } else { + } + else + { if (!ReadFile( ctx->dev_handle, &ctx->report_buffer, sizeof(ctx->report_buffer), &ctx->read_size, - &ctx->read_state)) { + &ctx->read_state)) + { error = GetLastError(); - if (error == ERROR_IO_PENDING) { + if (error == ERROR_IO_PENDING) + { ctx->io_pending = TRUE; - } else { + } + else + { return HID_POLL_ERROR; } - } else { + } + else + { // The read completed right away return HID_POLL_CARD_READY; } @@ -386,51 +440,58 @@ cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx) { return HID_POLL_CARD_NOT_READY; } -cardio_hid_card_type_t cardio_hid_device_read(struct cardio_hid_device *hid_ctx) { +cardio_hid_card_type_t cardio_hid_device_read(struct cardio_hid_device *hid_ctx) +{ // check if not initialized - if (!hid_ctx->initialized) { + if (!hid_ctx->initialized) + { return HID_CARD_NONE; } // check if IO is pending - if (hid_ctx->io_pending) { + if (hid_ctx->io_pending) + { return HID_CARD_NONE; } // check if nothing was read - if (hid_ctx->read_size == 0) { + if (hid_ctx->read_size == 0) + { return HID_CARD_NONE; } // iterate collections - for (int i = 0; i < hid_ctx->collection_length; i++) { + for (int i = 0; i < hid_ctx->collection_length; i++) + { HIDP_VALUE_CAPS *item = &hid_ctx->collection[i]; // get usages NTSTATUS res = HidP_GetUsageValueArray( - HidP_Input, - CARD_READER_USAGE_PAGE, - 0, // LinkCollection - item->NotRange.Usage, - (PCHAR) &hid_ctx->u.usage_value, - sizeof(hid_ctx->u.usage_value), - hid_ctx->pp_data, - (PCHAR) &hid_ctx->report_buffer, - hid_ctx->read_size); + HidP_Input, + CARD_READER_USAGE_PAGE, + 0, // LinkCollection + item->NotRange.Usage, + (PCHAR)&hid_ctx->u.usage_value, + sizeof(hid_ctx->u.usage_value), + hid_ctx->pp_data, + (PCHAR)&hid_ctx->report_buffer, + hid_ctx->read_size); // loop through the collection to find the entry that handles this report ID - if (res == HIDP_STATUS_INCOMPATIBLE_REPORT_ID) { + if (res == HIDP_STATUS_INCOMPATIBLE_REPORT_ID) + { continue; } // check if failed - if (res != HIDP_STATUS_SUCCESS) { + if (res != HIDP_STATUS_SUCCESS) + { return HID_CARD_NONE; } // return card type - return (cardio_hid_card_type_t) item->NotRange.Usage; + return (cardio_hid_card_type_t)item->NotRange.Usage; } return HID_CARD_NONE; diff --git a/src/cardio_plugin/cardio.h b/src/cardreader_plugin/CardIO/cardio.h similarity index 98% rename from src/cardio_plugin/cardio.h rename to src/cardreader_plugin/CardIO/cardio.h index c333da8..fba0d41 100644 --- a/src/cardio_plugin/cardio.h +++ b/src/cardreader_plugin/CardIO/cardio.h @@ -26,7 +26,7 @@ #ifndef SPICETOOLS_CARDIO_HID_H #define SPICETOOLS_CARDIO_HID_H -#include "../helpers.h" +#include "../../helpers.h" #include #include diff --git a/src/cardreader_plugin/SmartCard/scard.c b/src/cardreader_plugin/SmartCard/scard.c new file mode 100644 index 0000000..fdaa478 --- /dev/null +++ b/src/cardreader_plugin/SmartCard/scard.c @@ -0,0 +1,334 @@ +/** + * MIT-License + * Copyright (c) 2018 by nolm + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Modified version. + */ + +#include "scard.h" +#include +#include +// #include +// #include + +extern char module[]; + +#define MAX_APDU_SIZE 255 +extern int readCooldown; +// set to detect all cards, reduce polling rate to 500ms. +// based off acr122u reader, see page 26 in api document. +// https://www.acs.com.hk/en/download-manual/419/API-ACR122U-2.04.pdf + +#define PICC_OPERATING_PARAMS 0xDFu +BYTE PICC_OPERATING_PARAM_CMD[5] = {0xFFu, 0x00u, 0x51u, PICC_OPERATING_PARAMS, 0x00u}; + +// return bytes from device +#define PICC_SUCCESS 0x90u +#define PICC_ERROR 0x63u + +static const BYTE UID_CMD[5] = {0xFFu, 0xCAu, 0x00u, 0x00u, 0x00u}; + +enum scard_atr_protocol +{ + SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03, + SCARD_ATR_PROTOCOL_ISO15693_PART3 = 0x0B, + SCARD_ATR_PROTOCOL_FELICA_212K = 0x11, + SCARD_ATR_PROTOCOL_FELICA_424K = 0x12, +}; + +// winscard_config_t WINSCARD_CONFIG; +SCARDCONTEXT hContext = 0; +SCARD_READERSTATE reader_states[2]; +LPTSTR reader_name_slots[2] = {NULL, NULL}; +int reader_count = 0; +LONG lRet = 0; + +void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no) +{ + printInfo("%s (%s): Update on reader : %s\n", __func__, module, reader_states[unit_no].szReader); + // Connect to the smart card. + LONG lRet = 0; + SCARDHANDLE hCard; + DWORD dwActiveProtocol; + for (int retry = 0; retry < 10; retry++) + { + if ((lRet = SCardConnect(_hContext, _readerName, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol)) == SCARD_S_SUCCESS) + break; + + Sleep(20); + } + + if (lRet != SCARD_S_SUCCESS) + { + printError("%s (%s): Error connecting to the card: 0x%08X\n", __func__, module, lRet); + return; + } + + // set the reader params + lRet = 0; + LPCSCARD_IO_REQUEST pci = dwActiveProtocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0; + DWORD cbRecv = MAX_APDU_SIZE; + BYTE pbRecv[MAX_APDU_SIZE]; + if ((lRet = SCardTransmit(hCard, pci, PICC_OPERATING_PARAM_CMD, sizeof(PICC_OPERATING_PARAM_CMD), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS) + { + printError("%s (%s): Error setting PICC params: 0x%08X\n", __func__, module, lRet); + return; + } + + if (cbRecv > 2 && pbRecv[0] != PICC_SUCCESS && pbRecv[1] != PICC_OPERATING_PARAMS) + { + printError("%s (%s): PICC params not valid 0x%02X != 0x%02X\n", __func__, module, pbRecv[1], PICC_OPERATING_PARAMS); + return; + } + + // Read ATR to determine card type. + TCHAR szReader[200]; + DWORD cchReader = 200; + BYTE atr[32]; + DWORD cByteAtr = 32; + lRet = SCardStatus(hCard, szReader, &cchReader, NULL, NULL, atr, &cByteAtr); + if (lRet != SCARD_S_SUCCESS) + { + printError("%s (%s): Error getting card status: 0x%08X\n", __func__, module, lRet); + return; + } + + // Only care about 20-byte ATRs returned by arcade-type smart cards + if (cByteAtr != 20) + { + printError("%s (%s): Ignoring card with len(%d) = %02X (%08X)\n", __func__, module, cByteAtr, atr, cByteAtr); + return; + } + + printInfo("%s (%s): atr Return: len(%d) = %02X (%08X)\n", __func__, module, cByteAtr, atr, cByteAtr); + + // Figure out if we should reverse the UID returned by the card based on the ATR protocol + BYTE cardProtocol = atr[12]; + BOOL shouldReverseUid = false; + if (cardProtocol == SCARD_ATR_PROTOCOL_ISO15693_PART3) + { + printWarning("%s (%s): Card protocol: ISO15693_PART3\n", __func__, module); + shouldReverseUid = true; + } + + else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3) + printWarning("%s (%s): Card protocol: ISO14443_PART3\n", __func__, module); + + else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K) + printWarning("%s (%s): Card protocol: FELICA_212K\n", __func__, module); + + else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_424K) + printWarning("%s (%s): Card protocol: FELICA_424K\n", __func__, module); + + else + { + printError("%s (%s): Unknown NFC Protocol: 0x%02X\n", __func__, module, cardProtocol); + return; + } + + // Read UID + cbRecv = MAX_APDU_SIZE; + if ((lRet = SCardTransmit(hCard, pci, UID_CMD, sizeof(UID_CMD), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS) + { + printError("%s (%s): Error querying card UID: 0x%08X\n", __func__, module, lRet); + return; + } + + if (cbRecv > 1 && pbRecv[0] == PICC_ERROR) + { + printError("%s (%s): UID query failed\n", __func__, module); + return; + } + + if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS) + printError("%s (%s): Failed SCardDisconnect: 0x%08X\n", __func__, module, lRet); + + if (cbRecv < 8) + { + printWarning("%s (%s): Padding card uid to 8 bytes\n", __func__, module); + memset(&pbRecv[cbRecv], 0, 8 - cbRecv); + } + else if (cbRecv > 8) + printWarning("%s (%s): taking first 8 bytes of len(uid) = %02X\n", __func__, module, cbRecv); + + // Copy UID to struct, reversing if necessary + card_info_t card_info; + if (shouldReverseUid) + for (DWORD i = 0; i < 8; i++) + card_info.uid[i] = pbRecv[7 - i]; + else + memcpy(card_info.uid, pbRecv, 8); + + for (int i = 0; i < 8; ++i) + buf[i] = card_info.uid[i]; +} + +void scard_clear(uint8_t unitNo) +{ + card_info_t empty_cardinfo; +} + +void scard_update(uint8_t *buf) +{ + if (reader_count < 1) + { + return; + } + + lRet = SCardGetStatusChange(hContext, readCooldown, reader_states, reader_count); + if (lRet == SCARD_E_TIMEOUT) + { + return; + } + else if (lRet != SCARD_S_SUCCESS) + { + printError("%s (%s): Failed SCardGetStatusChange: 0x%08X\n", __func__, module, lRet); + return; + } + + for (uint8_t unit_no = 0; unit_no < reader_count; unit_no++) + { + if (!(reader_states[unit_no].dwEventState & SCARD_STATE_CHANGED)) + continue; + + DWORD newState = reader_states[unit_no].dwEventState ^ SCARD_STATE_CHANGED; + bool wasCardPresent = (reader_states[unit_no].dwCurrentState & SCARD_STATE_PRESENT) > 0; + if (newState & SCARD_STATE_UNAVAILABLE) + { + printWarning("%s (%s): New card state: unavailable\n", __func__, module); + Sleep(readCooldown); + } + else if (newState & SCARD_STATE_EMPTY) + { + printWarning("%s (%s): New card state: empty\n", __func__, module); + scard_clear(unit_no); + } + else if (newState & SCARD_STATE_PRESENT && !wasCardPresent) + { + printInfo("%s (%s): New card state: present\n", __func__, module); + scard_poll(buf, hContext, reader_states[unit_no].szReader, unit_no); + } + + reader_states[unit_no].dwCurrentState = reader_states[unit_no].dwEventState; + } + + return; +} + +bool scard_init() +{ + if ((lRet = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext)) != SCARD_S_SUCCESS) + { + // log_warning("scard", "failed to establish SCard context: {}", bin2hex(&lRet, sizeof(LONG))); + return lRet; + } + + LPCTSTR reader = NULL; + + int readerNameLen = 0; + + // get list of readers + LPTSTR reader_list = NULL; + auto pcchReaders = SCARD_AUTOALLOCATE; + lRet = SCardListReaders(hContext, NULL, (LPTSTR)&reader_list, &pcchReaders); + + int slot0_idx = -1; + int slot1_idx = -1; + int readerCount = 0; + switch (lRet) + { + case SCARD_E_NO_READERS_AVAILABLE: + printError("%s (%s): No readers available\n", __func__, module); + return FALSE; + + case SCARD_S_SUCCESS: + + // So WinAPI has this terrible "multi-string" concept wherein you have a list + // of null-terminated strings, terminated by a double-null. + for (reader = reader_list; *reader; reader = reader + lstrlen(reader) + 1) + { + printInfo("%s (%s): Found reader: %s\n", __func__, module, reader); + readerCount++; + } + + // If we have at least two readers, assign readers to slots as necessary. + if (readerCount >= 2) + { + if (slot1_idx != 0) + slot0_idx = 0; + if (slot0_idx != 1) + slot1_idx = 1; + } + + // if the reader count is 1 and no reader was set, set first reader + if (readerCount == 1 && slot0_idx < 0 && slot1_idx < 0) + slot0_idx = 0; + + // If we somehow only found slot 1, promote slot 1 to slot 0. + if (slot0_idx < 0 && slot1_idx >= 0) + { + slot0_idx = slot1_idx; + slot1_idx = -1; + } + + // Extract the relevant names from the multi-string. + int i; + for (i = 0, reader = reader_list; *reader; reader = reader + lstrlen(reader) + 1, i++) + { + if (slot0_idx == i) + { + readerNameLen = lstrlen(reader); + reader_name_slots[0] = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(TCHAR) * (readerNameLen + 1)); + memcpy(reader_name_slots[0], &reader[0], (size_t)(readerNameLen + 1)); + } + if (slot1_idx == i) + { + readerNameLen = lstrlen(reader); + reader_name_slots[1] = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(TCHAR) * (readerNameLen + 1)); + memcpy(reader_name_slots[1], &reader[0], (size_t)(readerNameLen + 1)); + } + } + + if (reader_name_slots[0]) + printInfo("%s (%s): Using reader slot 0: %s\n", __func__, module, reader_name_slots[0]); + + if (reader_name_slots[1]) + printInfo("%s (%s): Using reader slot 1: %s\n", __func__, module, reader_name_slots[1]); + + reader_count = reader_name_slots[1] ? 2 : 1; + + memset(&reader_states[0], 0, sizeof(SCARD_READERSTATE)); + reader_states[0].szReader = reader_name_slots[0]; + + memset(&reader_states[1], 0, sizeof(SCARD_READERSTATE)); + reader_states[1].szReader = reader_name_slots[1]; + return TRUE; + + default: + printWarning("%s (%s): Failed SCardListReaders: 0x%08X\n", __func__, module, lRet); + return FALSE; + } + + if (reader_list) + { + SCardFreeMemory(hContext, reader_list); + } +} \ No newline at end of file diff --git a/src/cardreader_plugin/SmartCard/scard.h b/src/cardreader_plugin/SmartCard/scard.h new file mode 100644 index 0000000..e9548f1 --- /dev/null +++ b/src/cardreader_plugin/SmartCard/scard.h @@ -0,0 +1,44 @@ +/** + * MIT-License + * Copyright (c) 2018 by nolm + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Modified version. + */ + +#pragma once + +// #include +#include "../../helpers.h" + +// cardinfo_t is a description of a card that was presented to a reader +typedef struct card_info +{ + int card_type; + uint8_t uid[8]; +} card_info_t; + +void scard_update(uint8_t *buf); + +void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no); + +void scard_clear(uint8_t unitNo); + +bool scard_init(); \ No newline at end of file diff --git a/src/cardreader_plugin/dllmain.c b/src/cardreader_plugin/dllmain.c new file mode 100644 index 0000000..555a9a6 --- /dev/null +++ b/src/cardreader_plugin/dllmain.c @@ -0,0 +1,226 @@ +#include "CardIO/cardio.h" +#include "SmartCard/scard.h" +char module[] = "CardReader"; + +// Reader Thread +static bool READER_RUNNER_INITIALIZED = false; +static HANDLE READER_POLL_THREAD; +static bool READER_POLL_STOP_FLAG; + +// SmartCard Specific + +// Game specific +static bool waitingForTouch = false; +static bool HasCard = false; + +// Misc +bool usingSmartCard = false; +int readCooldown = 200; + +typedef void (*callbackTouch)(i32, i32, u8[168], u64); +callbackTouch touchCallback; +u64 touchData; +char AccessID[21] = "00000000000000000001"; +static u8 cardData[168] = {0x01, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x2E, 0x58, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x5C, 0x97, 0x44, 0xF0, 0x88, 0x04, 0x00, 0x43, 0x26, 0x2C, 0x33, 0x00, 0x04, + 0x06, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x42, 0x47, 0x49, 0x43, 0x36, + 0x00, 0x00, 0xFA, 0xE9, 0x69, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static unsigned int __stdcall reader_poll_thread_proc(void *ctx) +{ + while (!READER_POLL_STOP_FLAG) + { + if (HasCard && !usingSmartCard) // A Cardio scan is already in progress, SmartCard doesn't need any extra cooldown. + { + Sleep(500); + return 0; + } + + uint8_t UID[8] = {0}; + + // update devices + if (!usingSmartCard) // CardIO + { + EnterCriticalSection(&CARDIO_HID_CRIT_SECTION); + for (size_t device_no = 0; device_no < CARDIO_HID_CONTEXTS_LENGTH; device_no++) + { + struct cardio_hid_device *device = &CARDIO_HID_CONTEXTS[device_no]; + + // get status + cardio_hid_poll_value_t status = cardio_hid_device_poll(device); + if (status == HID_POLL_CARD_READY) + { + + // read card + if (cardio_hid_device_read(device) == HID_CARD_NONE) + continue; + + // if card not empty + if (*((uint64_t *)&device->u.usage_value[0]) > 0) + for (int i = 0; i < 8; ++i) + UID[i] = device->u.usage_value[i]; + } + } + LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION); + Sleep(readCooldown); + } + else // SmartCard + { + scard_update(UID); + } + + if (UID[0] > 0) // If a card was read, format it properly and set HasCard to true so the game can insert it on next frame. + { + printWarning("%s (%s): Read card %02X%02X%02X%02X%02X%02X%02X%02X\n", __func__, module, UID[0], UID[1], UID[2], UID[3], UID[4], UID[5], UID[6], UID[7]); + + if (waitingForTouch) // Check if game is waiting for a card. + { + // Properly format the AccessID + u64 ReversedAccessID; + for (int i = 0; i < 8; i++) + ReversedAccessID = (ReversedAccessID << 8) | UID[i]; + sprintf(AccessID, "%020llu", ReversedAccessID); + + waitingForTouch = false; // We don't want to read more cards unless the game asks us to, this is a failsafe to avoid weird behaviour. + HasCard = true; + } + else // Game wasn't checking for cards yet. + printError("%s (%s): Card %02X%02X%02X%02X%02X%02X%02X%02X was rejected, still waiting for WaitTouch()\n", __func__, module, UID[0], UID[1], UID[2], UID[3], UID[4], UID[5], UID[6], UID[7]); + } + } + + return 0; +} + +bool reader_runner_start() +{ + // initialize + if (!READER_RUNNER_INITIALIZED) + { + READER_RUNNER_INITIALIZED = true; + + if (!usingSmartCard) + { // CardIO INIT + printWarning("%s (%s): Initializing CardIO\n", __func__, module); + + // Initialize + if (!cardio_hid_init()) + { + printError("%s (%s): Couldn't init CardIO\n", __func__, module); + return FALSE; + } + + // Scan HID devices + if (!cardio_hid_scan()) + { + printError("%s (%s): Couldn't scan for CardIO devices\n", __func__, module); + return FALSE; + } + } + else + { // SmartCard INIT + printWarning("%s (%s): Initializing SmartCard\n", __func__, module); + + if (!scard_init()) + { + // Initialize + printError("%s (%s): Couldn't init SmartCard\n", __func__, module); + return FALSE; + } + } + + return TRUE; // Init success + } + + return TRUE; +} + +void reader_runner_stop() +{ + // shutdown HID + if (!usingSmartCard) + { + cardio_hid_close(); + } + + // set initialized to false + READER_RUNNER_INITIALIZED = false; +} + +void Init() +{ + printWarning("%s (%s): Starting HID Service...\n", __func__, module); + + // Read config + toml_table_t *config = openConfig(configPath("plugins/cardreader.toml")); + if (config) + { + usingSmartCard = readConfigBool(config, "using_smartcard", false); + readCooldown = readConfigInt(config, "read_cooldown", usingSmartCard ? 500 : 50); + toml_free(config); + } + else + { + printError("%s (%s): Config file not found\n", __func__, module); + return; + } + + printInfo("%s (%s): Using %s as reader type\n", __func__, module, usingSmartCard ? "Smart Card" : "cardIO"); + printInfo("%s (%s): Card read cooldown set to %d ms\n", __func__, module, readCooldown); + + // Find and initialize reader(s) + if (!reader_runner_start()) + return; + + printWarning("%s (%s): Starting reader thread.\n", __func__, module); + + // Start reader thread + READER_POLL_STOP_FLAG = false; + READER_POLL_THREAD = (HANDLE)_beginthreadex( + NULL, + 0, + reader_poll_thread_proc, + NULL, + 0, + NULL); +} + +void Update() +{ + if (HasCard) + { + // Generating a ChipID Derived from the AccessID + char ChipID[33] = "000000000000"; + strcat(ChipID, AccessID); + + // Insert card in game + printInfo("%s (%s): Inserting Card %s, with ChipID %s\n", __func__, module, AccessID, ChipID); + memcpy(cardData + 0x2C, ChipID, 33); + memcpy(cardData + 0x50, AccessID, 21); + touchCallback(0, 0, cardData, touchData); + + HasCard = false; + } +} + +void WaitTouch(i32 (*callback)(i32, i32, u8[168], u64), u64 data) // This is called everytime the game expects a card to be read +{ + printInfo("%s (%s): Waiting for touch\n", __func__, module); + waitingForTouch = true; + touchCallback = callback; + touchData = data; +} + +void Exit() +{ + reader_runner_stop(); + READER_POLL_STOP_FLAG = true; + WaitForSingleObject(READER_POLL_THREAD, INFINITE); + CloseHandle(READER_POLL_THREAD); + READER_POLL_THREAD = NULL; + READER_POLL_STOP_FLAG = false; +} diff --git a/src/helpers.h b/src/helpers.h index ed402f4..a754c8d 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -19,100 +19,101 @@ typedef uint64_t u64; #undef DEFINE_GUID #endif -#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} #ifdef BASE_ADDRESS #define ASLR(address, handle) ((u64)handle + (u64)address - (u64)BASE_ADDRESS) #endif -#define FUNCTION_PTR(returnType, callingConvention, function, location, ...) \ - returnType (callingConvention *function) (__VA_ARGS__) = (returnType (callingConvention *) (__VA_ARGS__)) (location) +#define FUNCTION_PTR(returnType, callingConvention, function, location, ...) \ + returnType(callingConvention *function)(__VA_ARGS__) = (returnType(callingConvention *)(__VA_ARGS__))(location) -#define PROC_ADDRESS(libraryName, procName) GetProcAddress (LoadLibrary (TEXT (libraryName)), procName) +#define PROC_ADDRESS(libraryName, procName) GetProcAddress(LoadLibrary(TEXT(libraryName)), procName) -#define HOOK(returnType, callingConvention, functionName, location, ...) \ - typedef returnType callingConvention (*functionName) (__VA_ARGS__); \ - functionName original##functionName = NULL; \ - void *where##functionName = (void *)location; \ - returnType callingConvention implOf##functionName (__VA_ARGS__) +#define HOOK(returnType, callingConvention, functionName, location, ...) \ + typedef returnType callingConvention (*functionName)(__VA_ARGS__); \ + functionName original##functionName = NULL; \ + void *where##functionName = (void *)location; \ + returnType callingConvention implOf##functionName(__VA_ARGS__) -#define HOOK_DYNAMIC(returnType, callingConvention, functionName, ...) \ - typedef returnType callingConvention (*functionName) (__VA_ARGS__); \ - functionName original##functionName = NULL; \ - void *where##functionName = NULL; \ - returnType callingConvention implOf##functionName (__VA_ARGS__) +#define HOOK_DYNAMIC(returnType, callingConvention, functionName, ...) \ + typedef returnType callingConvention (*functionName)(__VA_ARGS__); \ + functionName original##functionName = NULL; \ + void *where##functionName = NULL; \ + returnType callingConvention implOf##functionName(__VA_ARGS__) -#define INSTALL_HOOK(functionName) \ - { \ - MH_Initialize (); \ - MH_CreateHook ((void *)where##functionName, (void *)implOf##functionName, (void **)(&original##functionName)); \ - MH_EnableHook ((void *)where##functionName); \ +#define INSTALL_HOOK(functionName) \ + { \ + MH_Initialize(); \ + MH_CreateHook((void *)where##functionName, (void *)implOf##functionName, (void **)(&original##functionName)); \ + MH_EnableHook((void *)where##functionName); \ } -#define INSTALL_HOOK_DYNAMIC(functionName, location) \ - { \ - where##functionName = (void *)location; \ - INSTALL_HOOK (functionName); \ +#define INSTALL_HOOK_DYNAMIC(functionName, location) \ + { \ + where##functionName = (void *)location; \ + INSTALL_HOOK(functionName); \ } -#define WRITE_MEMORY(location, type, ...) \ - { \ - const type data[] = { __VA_ARGS__ }; \ - DWORD oldProtect; \ - VirtualProtect ((void *)(location), sizeof (data), PAGE_EXECUTE_READWRITE, &oldProtect); \ - memcpy ((void *)(location), data, sizeof (data)); \ - VirtualProtect ((void *)(location), sizeof (data), oldProtect, &oldProtect); \ +#define WRITE_MEMORY(location, type, ...) \ + { \ + const type data[] = {__VA_ARGS__}; \ + DWORD oldProtect; \ + VirtualProtect((void *)(location), sizeof(data), PAGE_EXECUTE_READWRITE, &oldProtect); \ + memcpy((void *)(location), data, sizeof(data)); \ + VirtualProtect((void *)(location), sizeof(data), oldProtect, &oldProtect); \ } -#define WRITE_MEMORY_STRING(location, data, length) \ - { \ - DWORD oldProtect; \ - VirtualProtect ((void *)(location), length, PAGE_EXECUTE_READWRITE, &oldProtect); \ - memcpy ((void *)(location), data, length); \ - VirtualProtect ((void *)(location), length, oldProtect, &oldProtect); \ +#define WRITE_MEMORY_STRING(location, data, length) \ + { \ + DWORD oldProtect; \ + VirtualProtect((void *)(location), length, PAGE_EXECUTE_READWRITE, &oldProtect); \ + memcpy((void *)(location), data, length); \ + VirtualProtect((void *)(location), length, oldProtect, &oldProtect); \ } -#define WRITE_NOP(location, count) \ - { \ - DWORD oldProtect; \ - VirtualProtect ((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \ - for (size_t i = 0; i < (size_t)(count); i++) \ - *((uint8_t *)(location) + i) = 0x90; \ - VirtualProtect ((void *)(location), (size_t)(count), oldProtect, &oldProtect); \ +#define WRITE_NOP(location, count) \ + { \ + DWORD oldProtect; \ + VirtualProtect((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \ + for (size_t i = 0; i < (size_t)(count); i++) \ + *((uint8_t *)(location) + i) = 0x90; \ + VirtualProtect((void *)(location), (size_t)(count), oldProtect, &oldProtect); \ } -#define WRITE_NULL(location, count) \ - { \ - DWORD oldProtect; \ - VirtualProtect ((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \ - for (size_t i = 0; i < (size_t)(count); i++) \ - *((uint8_t *)(location) + i) = 0x00; \ - VirtualProtect ((void *)(location), (size_t)(count), oldProtect, &oldProtect); \ +#define WRITE_NULL(location, count) \ + { \ + DWORD oldProtect; \ + VirtualProtect((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \ + for (size_t i = 0; i < (size_t)(count); i++) \ + *((uint8_t *)(location) + i) = 0x00; \ + VirtualProtect((void *)(location), (size_t)(count), oldProtect, &oldProtect); \ } -#define COUNTOFARR(arr) sizeof (arr) / sizeof (arr[0]) -#define RETURN_FALSE(returnType, callingConvention, functionName, ...) \ - returnType callingConvention functionName (__VA_ARGS__) { return 0; } -#define RETURN_TRUE(returnType, callingConvention, functionName, ...) \ - returnType callingConvention functionName (__VA_ARGS__) { return 1; } +#define COUNTOFARR(arr) sizeof(arr) / sizeof(arr[0]) +#define RETURN_FALSE(returnType, callingConvention, functionName, ...) \ + returnType callingConvention functionName(__VA_ARGS__) { return 0; } +#define RETURN_TRUE(returnType, callingConvention, functionName, ...) \ + returnType callingConvention functionName(__VA_ARGS__) { return 1; } -#define INFO_COLOUR FOREGROUND_GREEN -#define WARNING_COLOUR (FOREGROUND_RED | FOREGROUND_GREEN) -#define ERROR_COLOUR FOREGROUND_RED -#define printInfo(format, ...) printColour (INFO_COLOUR, format, __VA_ARGS__) -#define printWarning(format, ...) printColour (WARNING_COLOUR, format, __VA_ARGS__) -#define printError(format, ...) printColour (ERROR_COLOUR, format, __VA_ARGS__) +#define INFO_COLOUR FOREGROUND_GREEN +#define WARNING_COLOUR (FOREGROUND_RED | FOREGROUND_GREEN) +#define ERROR_COLOUR FOREGROUND_RED +#define printInfo(format, ...) printColour(INFO_COLOUR, format, __VA_ARGS__) +#define printWarning(format, ...) printColour(WARNING_COLOUR, format, __VA_ARGS__) +#define printError(format, ...) printColour(ERROR_COLOUR, format, __VA_ARGS__) #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -char *configPath (char *name); -toml_table_t *openConfig (char *configFilePath); -toml_table_t *openConfigSection (toml_table_t *config, char *sectionName); -bool readConfigBool (toml_table_t *table, char *key, bool notFoundValue); -int64_t readConfigInt (toml_table_t *table, char *key, int64_t notFoundValue); -char *readConfigString (toml_table_t *table, char *key, char *notFoundValue); -void printColour (int colour, const char *format, ...); + char *configPath(char *name); + toml_table_t *openConfig(char *configFilePath); + toml_table_t *openConfigSection(toml_table_t *config, char *sectionName); + bool readConfigBool(toml_table_t *table, char *key, bool notFoundValue); + int64_t readConfigInt(toml_table_t *table, char *key, int64_t notFoundValue); + char *readConfigString(toml_table_t *table, char *key, char *notFoundValue); + void printColour(int colour, const char *format, ...); #ifdef __cplusplus } #endif diff --git a/src/meson.build b/src/meson.build index c7f0b6a..5394735 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,22 +1,25 @@ compiler = meson.get_compiler('c') hid_lib = compiler.find_library('hid') +winscard_lib = compiler.find_library('winscard') setupapi_lib = compiler.find_library('setupapi') deps = [ dependency('tomlc99'), dependency('minhook'), hid_lib, - setupapi_lib + winscard_lib, + setupapi_lib, ] shared_library( - 'cardio', + 'cardreader', implicit_include_directories : false, - vs_module_defs : 'cardio.def', + vs_module_defs : 'cardreader.def', sources: [ 'helpers.c', - 'cardio_plugin/dllmain.c', - 'cardio_plugin/cardio.c', + 'cardreader_plugin/dllmain.c', + 'cardreader_plugin/CardIO/cardio.c', + 'cardreader_plugin/SmartCard/scard.c', ], dependencies: deps, include_directories: [