On Sept. 22, 2016, Capcom released an update for Street Fighter 5 that included what was detailed as a new anti-crack solution to prevent people from unscrupulously obtaining rewards in-game without completing the required gameplay challenges. You can find their announcement post here.
Shortly after the update rolled out to end users, it was discovered that their implementation was actually installing a driver that was running user-mode code at elevated privileges. After reading an interesting post detailing the potential danger, I decided to pop open the .sys file and inspect it in IDA, to refresh my RE-ing skills (and it seemed straightforward enough that it would be a good experience to document for new learners).
Weighing in at 11 KB, this lightweight module was bound to have very few functions which makes our analysis easier. Upon popping open DriverEntry, there is very little going on. It creates a device and a symbolic link, but it uses an obfuscated string and decodes it to determine what name to use for both.
Here, we can see on the bottom right that the driver fills out four major functions, with the same function taking two of them. I assumed at this point that these were DispatchCreate/Close, DispatchDeviceControl, and DriverUnload routines.
The DispatchCreate / DispatchClose handler is very straightforward, nothing to really see here.
DriverUnload is the exact same way. The only interesting part is where it has to decode the string again to know what symbolic link to destroy.
Here is where things get interesting. The IOCTL handler has two control codes, 0xAA012044 and 0xAA013044. After reversing the input validation, it seems that the difference between the two is to let the driver know which bitness the user application was; one validates an input buffer size of 8 bytes, whereas the other validates an input size of 4 bytes.
After validating the IOCTL code, as well as input/output buffer sizes, the handler calls a subfunction with the passed in value from user-mode as the parameter.
Aaand… wow. Not only does it straight up call the passed in user address (without doing proper validation), it intentionally disables SMEP:
Also, it passes in the address of MmGetSystemRoutineAddress as a parameter. The only caveat here is that it inspects the 8 bytes in front of the address, to make sure that that also contains the address of the function; otherwise, it bails.
Not believing my eyes, I coded up a quick proof of concept. First, I needed the decoded device name. This was fairly straightforward, as I simply started the driver and fired up WinObj and eyed it down in about 30 seconds:
Now I had the device name, and I could attempt to open it. After some trial and error, I built the following proof of concept, which opens the device, and sends it an ioctl with the proper code, with one of our own function pointers in userspace as the argument. I had to patch the preceding 8 bytes of my UserFunction in order to get past the final validation as well.
While super quick and dirty, I threw it onto my VM and fired it off. Lo and behold:
Jeez… Capcom really dropped the ball on this one. I’m very glad that they immediately rolled this back the day after it released, and that no one is actually forced to run this driver if they want to play Street Fighter.