mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2024-12-22 11:15:53 +01:00
103 lines
6.6 KiB
Markdown
103 lines
6.6 KiB
Markdown
|
# Follow-up (14th August 2019)
|
||
|
After publishing this post-mortem, I got messaged by a user on sows who was able to shed some more light on this issue.
|
||
|
The user was experiencing the same symptoms on a Win7 setup: blue screen once the firmware was flashed to the C02 IO.
|
||
|
This user's solutions was to use the USB2 ports on the PC instead of the USB3 ones. This is good to know and kinda
|
||
|
aligns with the weird things happening in the driver (see below).
|
||
|
|
||
|
# Post-mortem: C02 IO kernel module crash on Windows 7 (29th July 2019) by icex2
|
||
|
## Background
|
||
|
The original ezusbsys.sys kernel module, which is required to run the C02 IO, was compiled for Windows XP 32-bit, only.
|
||
|
There is a newer driver by Cypress, cyusb3.sys, which could be used to IO2 boards on newer Windows platforms, but does
|
||
|
not work with the C02 IO in combination with Konami's propriatery firmware. Thus, it was not possible to run the C02 IO
|
||
|
on anything than Windows XP 32-bit. But, with newer IIDX games running on Windows 7 64-bit, the C02 IO wasn't usable
|
||
|
anymore. Leaving aside, that the newer games actually require a BIO2 board and do not support C02 nor IO2 boards
|
||
|
anymore.
|
||
|
|
||
|
## The goal
|
||
|
I still wanted to use my cabinet with a C02 board on newer games which is possible with BT5 adding an emulation layer
|
||
|
and an interface (iidxio). This IO interface can be used to implement a driver that talks to a real IO again. Thus,
|
||
|
implementing a ezusb iidxio driver library, we can run newer games with an C02 IO as well.
|
||
|
|
||
|
However, there was no ezusbsys.sys driver that works on newer platforms required to run the newer games. But, Cypress
|
||
|
was nice and included the source code of the ezusbsys kernel module. With a few tweaks and a very recent version of
|
||
|
visual studio, it was quite easy to build this driver for newer platforms, including Windows 7, 8 and 10 in both
|
||
|
32-bit and 64-bit variants.
|
||
|
|
||
|
## The problem
|
||
|
But, when using this driver on certain combinations of newer hardware (max. 1-2 years old) and Windows 7, the kernel
|
||
|
module might crash after the Konami C02 firmware got flashed to the ezusb board. The result was a bluescreen and reboot.
|
||
|
|
||
|
However, the hardware was fine and the kernel module worked fine on another piece of hardware, the stock PC that was
|
||
|
used with iidx 20 to 24. However, this hardware is not powerful enough to run iidx 25 and newer without stuttering
|
||
|
issues.
|
||
|
|
||
|
## The analysis/debugging
|
||
|
Note: The full source code can be found in the bemanitools-supplement package.
|
||
|
|
||
|
Setup:
|
||
|
* Native hardware with Windows 7 that was crashing
|
||
|
* Vmware with Windows 10 and Visual Studio 2019 to compile the kernel module. Target platform Windows 7 64-bit
|
||
|
* Booting Windows 7 in test mode to allow unsigned kernel modules to run and with debug output turned on
|
||
|
* dbgview on Windows 7 machine to get local kernel dbg output
|
||
|
|
||
|
Because I wanted to stick to Windows 7 in the beginning (refer to the solution section), I started debugging the kernel
|
||
|
module by enabling the debug message output that was already available in the code. However, since kernel debug message
|
||
|
printing can be very delayed, the kernel could not print various messages before the kernel crashed.
|
||
|
|
||
|
Thus, I started stripping the kernel module step by step to narrow down the possible spots causing the crash. After a
|
||
|
few hours, I got the (first) issue tracked down:
|
||
|
|
||
|
After the firmware was flashed, the device had to re-enumerate. When this happens, the function *Ezusb_PnPAddDevice*
|
||
|
is called to create a new instance of the device. Since this kernel module is acting as a filter driver, it has to
|
||
|
trap this call, and add a filter device before the real device in the device stack. Thus, each call to the ezusb device
|
||
|
hits the filter device first and the filter device calls the real device after doing some magic.
|
||
|
|
||
|
*Ezusb_PnPAddDevice* calls *Ezusb_CreateDeviceObject*. Afterwards, it checks the status of the call to
|
||
|
*Ezusb_CreateDeviceObject* and if successful, it tries attaching the device to the device stack. However, instead of
|
||
|
using *IoAttachDeviceToDeviceStackSafe* it uses the unsafe variant *IoAttachDeviceToDeviceStack* which can lead to a
|
||
|
race condition on newer Windows Systems. Furthermore, all initialization of further variables of the *deviceObject*
|
||
|
needs to happen BEFORE doing that. Again, this is a race condition.
|
||
|
|
||
|
Next issue: Once the kernel calls *Ezusb_StartDevice* -> *Ezusb_ConfigureDevice* -> *Ezusb_SelectInterfaces*, it tries
|
||
|
to use *USBD_ParseConfigurationDescriptorEx* to get the interface from the configuration descriptor. However, that
|
||
|
fails for some unknown reason. I checked the data structure and it is perfectly fine and everything is there. Thus,
|
||
|
I wrote my own version *Ezusb_GetInterfaceFromConfigurationDescriptor* which does all the magic required to get this
|
||
|
part fixed:
|
||
|
```
|
||
|
PUSB_INTERFACE_DESCRIPTOR Ezusb_GetInterfaceFromConfigurationDescriptor(
|
||
|
IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor
|
||
|
)
|
||
|
{
|
||
|
if (!ConfigurationDescriptor) {
|
||
|
Ezusb_KdPrint(("ERROR Ezusb_GetInterfaceFromConfigurationDescriptor NULL configuration desc"));
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (ConfigurationDescriptor->wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_CONFIGURATION_DESCRIPTOR)) {
|
||
|
Ezusb_KdPrint(("ERROR Ezusb_GetInterfaceFromConfigurationDescriptor configuration descriptor too small to have space for interface descriptor"));
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// hardcoding this to a single interface because we only care about the ezusb used with IIDX (C02 IO)
|
||
|
if (ConfigurationDescriptor->bNumInterfaces < 1) {
|
||
|
Ezusb_KdPrint(("ERROR Ezusb_GetInterfaceFromConfigurationDescriptor num interfaces 0"));
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// when retrieving the configuration descriptor from the usb device, the interface is located right next to it
|
||
|
return (PUSB_INTERFACE_DESCRIPTOR) (((unsigned char*) ConfigurationDescriptor) + sizeof(USB_CONFIGURATION_DESCRIPTOR));
|
||
|
}
|
||
|
```
|
||
|
|
||
|
And next issue is just up ahead: Following the above, we have to call *Ezusb_USBD_CreateConfigurationRequestEx* to
|
||
|
create a USB configuration request to set the interface we want to use. This is executed with a *Ezusb_CallUSBD* call
|
||
|
which sends request to the real hardware. However, this request always fails. The call *IoCallDriver* inside
|
||
|
*Ezusb_CallUSBD* always returns an NTSTATUS code that is not documented anywhere (can't find the exact status code
|
||
|
anymore, but once you get it, try to find it in the header file).
|
||
|
|
||
|
At this point, I had to give up. I already wasted too many hours and this is clearly a dead end.
|
||
|
|
||
|
## The solution
|
||
|
Once I realized that I got stuck with Windows 7 and I didn't want to buy (more) new hardware, I gave Windows 10 a try.
|
||
|
Surprisingly, this solved all the issues and the kernel module runs fine. The C02 board is flashable without crashing
|
||
|
and works with newer IIDX games.
|