1
0
mirror of https://github.com/djhackersdev/bemanitools.git synced 2024-12-18 17:25:53 +01:00
bemanitools/doc/dev/journal/2023-04-03-d01-ezusb-io-boot-security.md

8.8 KiB

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.

// 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 function.

// 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;
}