From 47bfb4e9b2dc735899283a8a5feb0d1096895432 Mon Sep 17 00:00:00 2001 From: icex2 Date: Mon, 3 Apr 2023 01:17:19 +0200 Subject: [PATCH] chore(doc): Add dev journal about 10th style IO and security boot --- .../2023-04-03-d01-ezusb-io-boot-security.md | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 doc/dev/journal/2023-04-03-d01-ezusb-io-boot-security.md diff --git a/doc/dev/journal/2023-04-03-d01-ezusb-io-boot-security.md b/doc/dev/journal/2023-04-03-d01-ezusb-io-boot-security.md new file mode 100644 index 0000000..4abe6cc --- /dev/null +++ b/doc/dev/journal/2023-04-03-d01-ezusb-io-boot-security.md @@ -0,0 +1,289 @@ +# Beatmania IIDX 10th Style - D01 IO boot code and security init + +Date: 2023-04-03 +Author: icex2 + +Documenting decompiled and reverse engineered code snippets from the D01 JAE `bm2dx.exe`. These +helped me figuring out the two different security boot modes the game supports. + +In summary, it supports booting with the C02 IO with a C02 black dongle and a D01 IO board with +a D01 dongle. No other combination is valid because they didn't make sense back then. You either +had an upgraded old style/twinkle cabinet to C02 (GEC02) or you bought a new dedicated cabinet +(GQD01). With 10th style supporting the old C02 dongle, it appears that all owners of a C02 cabinet +with IO board and C02 dongle received a free software update/HDD. This might make sense considering +the short life span of C02 and the game being super buggy, especially in earlier/initial revisions. + +The game expects the following "configurations" from bemanitools: + +* Booting as upgraded C02 with free D01 upgrade + * `sec.boot_version=GEC02 ` + * `sec.boot_seeds=0:0:1` + * `sec.black_plug_mcode=GEC02JAA` + * "D01 IO pin" on IO board not active +* Booting as dedicated + * `sec.boot_version=GEC02 ` + * `sec.boot_seeds=0:1:1` + * `sec.black_plug_mcode=GQD01JAA` + * "D01 IO pin" on IO board ACTIVE + +All the above was derived from reading and understanding the documented code excerpts below. + +## io_boot - sub_402700 + +Called as the first step in the boot statemachine by the "boot" function `sub_40F9C0`. + +```c +// sub_402700 +int __stdcall io_boot(int a1) +{ + DWORD (__stdcall *timeGetTime)(); // esi + int usb_boot_security_res; // ebp + DWORD time_start; // ebx + int io_boot_state; // edi + DWORD now; // esi + BOOL is_d01_boot_mode; // esi + BOOL v7; // esi + int result; // eax + int v9; // [esp+10h] [ebp-2Ch] + int firmware_version; // [esp+14h] [ebp-28h] + int io_pad_read; // [esp+18h] [ebp-24h] BYREF + char v12[32]; // [esp+1Ch] [ebp-20h] BYREF + + dword_4D0768 = 0; + strcpy(byte_4D076C, &byte_4D0858); + timeGetTime = ::timeGetTime; + v9 = 0; + usb_boot_security_res = io_pad_read; + time_start = ::timeGetTime(); + io_boot_state = 0; + while ( 2 ) + { + switch ( io_boot_state ) + { + case 0: // "start io", firmware download and wait for reconnect + if ( usbStart(0) ) + { + if ( time_exceeded(time_start, 20000) ) + set_io_error_type(1, aErrorUsbioStar);// ERROR(USBIO START) + } + else + { + io_boot_state = 1; + } + goto reset_io_boot_state_label; + case 1: // check firmware + firmware_version = usbFirmResult(); + if ( firmware_version == 96 ) + { + if ( time_exceeded(time_start, 25000) ) + set_io_error_type(1, aErrorFm); // ERROR(FM), FM = firmware + } + else + { + io_boot_state = 2; + } + goto reset_io_boot_state_label; + case 2: + if ( !firmware_version ) + { + usbMute(1); + time_start = timeGetTime(); + now = timeGetTime(); + io_input_is_d01_ezusb = 0; + do + { + usbPadRead(&io_pad_read); + if ( (io_pad_read & 0x10) != 0 ) + io_input_is_d01_ezusb = 1; + } + while ( !time_exceeded(now, 2000) ); + io_boot_state = 3; +reset_io_boot_state_label: + timeGetTime = ::timeGetTime; + if ( io_boot_state != v9 ) + { + v9 = io_boot_state; + time_start = ::timeGetTime(); + } + continue; // retry + } + if ( firmware_version > 128 ) + { + if ( firmware_version == 254 ) + { + set_io_error_type(1, aErrorFmTrnsOut);// ERROR(FM TRNS-OUT) + return 1; + } + if ( firmware_version == 255 ) + { + set_io_error_type(1, aErrorFmTimeOut);// ERROR(FM TIME-OUT) + return 1; + } + } + else + { + switch ( firmware_version ) + { + case 128: + set_io_error_type(1, aErrorFmReadErr);// ERROR(FM READ-ERR) + return 1; + case -113: + set_io_error_type(1, aErrorFmDlErr);// ERROR(FM DL-ERR) + return 1; + case -33: + set_io_error_type(1, aErrorFmCmprReq);// ERROR(FM CMPR-REQ) + return 1; + } + } +UNKNOWN_ERROR_LABEL: + set_io_error_type(1, aErrorUnknown); // ERROR(UNKNOWN) + return 1; + case 3: + if ( io_init_security() ) + { + set_io_error_type(1, aErrorSqInit); // ERROR(SQ-INIT) + return 1; + } + io_boot_state = 4; + goto reset_io_boot_state_label; + case 4: + is_d01_boot_mode = io_input_is_d01_ezusb != 0; + if ( !strncmp(black_dongle_mcode, MCODE_GED01, 5u) ) + is_d01_boot_mode = 1; + usb_boot_security_res = usbBootSecurity(aGec02, 0, is_d01_boot_mode, 1); + if ( usb_boot_security_res == 96 ) + { + if ( time_exceeded(time_start, 20000) ) + set_io_error_type(1, aErrorBtSqInit);// ERROR(BT-SQ-INIT)' + } + else + { + io_boot_state = 5; + } + goto reset_io_boot_state_label; + case 5: + if ( usb_boot_security_res ) + { + switch ( usb_boot_security_res ) + { + case 0xFFFFFE9F: + set_io_error_type(1, aErrorSecurityE);// ERROR(SECURITY EEP) + result = 1; + break; + case 0xFFFFFECF: + case 0xFFFFFEDF: + set_io_error_type(1, aErrorSecurityI);// ERROR(SECURITY ID or EEP) + result = 1; + break; + case 0xFFFFFEEF: + set_io_error_type(1, aErrorSecurityN);// 'ERROR(SECURITY No Match)' + result = 1; + break; + case 0xFFFFFEFF: + io_boot_state = 6; + goto reset_io_boot_state_label; + default: + goto UNKNOWN_ERROR_LABEL; + } + } + else + { + Sleep(0x3E8u); + result = 0; + } + return result; + case 6: // security conversion/upgrading security dongles + v7 = io_input_is_d01_ezusb != 0; + if ( !usbGetSecurityKey(v12) ) + { + if ( usbSetupSecurityComplete(v12, 0, v7, 1) ) + set_io_error_type(1, aErrorSecurityC);// ERROR(SECURITY CONVERSION FAILED) + return 1; + } + set_io_error_type(1, aErrorSecurityC); // ERROR(SECURITY CONVERSION FAILED) + goto reset_io_boot_state_label; + default: + goto UNKNOWN_ERROR_LABEL; + } + } +} +``` + +## io_init_security - sub_402BB0 + +Sub-function of the [io_boot](#io_boot---sub_402700) function. + +```c +// sub_402BB0 +int io_init_security() +{ + int v0; // esi + int v2; // esi + int mcode_mismatch_cnt; // esi + char white_dongle_data_maybe[10]; // [esp+Ch] [ebp-20h] BYREF + + usbSecurityInit(); + v0 = 0; + while ( usbSecurityInitDone() ) + { + if ( v0 > 1200 ) + { + set_io_error_type(-1, aNgSi); + return -1; + } + ++v0; + Sleep(16u); + } + pcbid = 0; + dword_4D0754 = 0; + word_4D0758 = 0; + while ( usbGetPCBID(&pcbid) ) + Sleep(1u); + *(_DWORD *)black_dongle_mcode = 0; + v2 = 0; + *(_DWORD *)&black_dongle_mcode[4] = 0; + while ( usbGetSecurity(black_dongle_mcode) ) // black_dongle_mcode = GQD01JAA + { + if ( v2 > 1200 ) + { + set_io_error_type(-1, aNgPd); + return -1; + } + ++v2; + Sleep(16u); + } + set_cabinet_type(black_dongle_mcode[6]); + mcode_mismatch_cnt = black_dongle_mcode[0] != 'G'; + if ( io_input_is_d01_ezusb || !strncmp(black_dongle_mcode, MCODE_GED01, 5u) )// Path for using the D01 dongle with D01 IO board + { + if ( (unsigned __int8)black_dongle_mcode[2] != (char)MCODE_D01 ) + ++mcode_mismatch_cnt; + if ( (unsigned __int8)black_dongle_mcode[3] != SBYTE1(MCODE_D01) ) + ++mcode_mismatch_cnt; + if ( (unsigned __int8)black_dongle_mcode[4] != SBYTE2(MCODE_D01) ) + ++mcode_mismatch_cnt; + } + else // Path for using the C02 dongle with C02 IO board + { + if ( (unsigned __int8)black_dongle_mcode[2] != MCODE_C02[0] ) + ++mcode_mismatch_cnt; + if ( (unsigned __int8)black_dongle_mcode[3] != MCODE_C02[1] ) + ++mcode_mismatch_cnt; + if ( (unsigned __int8)black_dongle_mcode[4] != MCODE_C02[2] ) + ++mcode_mismatch_cnt; + if ( !usbGetSecurityKey(white_dongle_data_maybe) ) + { + black_dongle_mcode[6] = white_dongle_data_maybe[6]; + set_cabinet_type(white_dongle_data_maybe[6]); + } + } + if ( black_dongle_mcode[5] != 'J' ) + ++mcode_mismatch_cnt; + if ( black_dongle_mcode[6] != 'A' && black_dongle_mcode[6] != 'B' && black_dongle_mcode[6] != 'C' ) + ++mcode_mismatch_cnt; + if ( !mcode_mismatch_cnt ) + return 0; + set_io_error_type(-1, aNgSecurity); + return -1; +} +``` \ No newline at end of file