Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement XDVD security challenge responses #1659

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

Ryzee119
Copy link
Contributor

@Ryzee119 Ryzee119 commented Jun 10, 2024

This PR implements the DVD security challenge/response mechanism to the best of my knowledge used on a Xbox console between the DVD drive and the Xbox.

This allows loading retail Xbox titles both in 'redump' format and xiso format with an unmodified retail BIOS.

For this to work, you need the security info from your original game disk. There is two methods:

  1. SS.bin from a compatible 'Kreon' drive using the method on redump.org http://wiki.redump.org/index.php?title=Microsoft_Xbox_and_Xbox_360_Dumping_Guide,
  2. dvd_layout.bin from your Xbox and original disk using these homebrew apps https://github.com/Ryzee119/OGX_CHALLENGE_TABLE/releases/tag/build-202310141001 or https://gist.github.com/Ernegien/bef054a43213c52c2bef8d94bf9f51cb

These two dumps have the same info but in a slightly different format. As I understand SS.bin represents what is physically on the DVD, dvd_layout.bin is the packet that is formatted and sent by the DVD drive firmware. This PR detects either and converts accordingly.

To provide this file to xemu, there is currently no GUI element and is done by command line

xemu.exe -dvd_security_path "SS.bin". The same arg is used for "dvd_layout.bin".

Notes from my testing:

  1. I had pretty good success using one common SS.bin/.dvd_layout.bin dump with many games. However later bioses seemed to only accept SS.bin dumps from that specific title. This could be improved security or an issue in my implementation?
  2. The Xbox didn't seem to care if the DVD is 'redump' or XISO style aslong as the challenges are responded to correctly. Your mileage may vary.
  3. Using a retail BIOS comes with the restrictions of the retail system such as region restrictions, and you wont be able to run homebrew (FTP etc)

References

  1. https://gist.github.com/Ernegien/bef054a43213c52c2bef8d94bf9f51cb (Major shout out the xbox7887 for this!)
  2. https://wayback-api.archive.org/web/20240316195746/https://multimedia.cx/eggs/xbox-sphinx-protocol/
  3. https://xboxdevwiki.net/DVD_Drive
  4. https://xboxdevwiki.net/Xbox_Game_Disc

This process just follows the text book challenge response process from the Xbox. There may be some unusual aspects that are missed and I only have a smallish selection of disks to test with. I pulled in some public domain RC4 and SHA1 libraries, not sure if they should live in the xdvd folder or not.

Future work suggestions

  1. Generate SS.bin automatically?
  2. Add to GUI and somehow link to specific titles

image

Copy link
Member

@mborgerson mborgerson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, nice work! Left some initial comments.

hw/ide/atapi.c Show resolved Hide resolved
hw/ide/atapi.c Outdated Show resolved Hide resolved
hw/xbox/xdvd/WjCryptLib_Rc4.c Outdated Show resolved Hide resolved
#ifdef XBOX
// We need to prevent small XISOs from being reported as CD-ROMs
// If the DVD security path is set, we can assume it is an XISO.
const char *dvd_security_path = g_config.sys.files.dvd_security_path;
Copy link
Member

@mborgerson mborgerson Jun 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably make this a qdev property, like disc file path, media type, etc. so it can play nicely with other systems

hw/ide/atapi.c Outdated Show resolved Hide resolved
hw/xbox/xdvd/xdvd.c Outdated Show resolved Hide resolved
@@ -457,6 +461,11 @@ struct IDEState {
uint8_t *smart_selftest_data;
/* AHCI */
int ncq_queues;
#ifdef XBOX
uint8_t xdvd_challenges_encrypted[XDVD_STRUCTURE_LEN];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do any of these fields need to be saved in vm state to support snapshots?


bool xdvd_is_redump(uint64_t total_sectors)
{
return total_sectors == XDVD_REDUMP_SECTOR_CNT;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for this to misclassify if a non-redump image happens to be this same size?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Youre right it would be technically possible; although I dont think any retail xbox title in XISO format could possibly have this size but maybe some homebrew could have.

Any suggestions? I presume there is some magic bytes in the redump style image somewhere, Id need to research it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to check the volume descriptor magic fields at the appropriate offset: https://xboxdevwiki.net/XDVDFS#Volume_descriptor

hw/xbox/xdvd/xdvd.c Outdated Show resolved Hide resolved
bool xdvd_get_encrypted_challenge_table(uint8_t *xdvd_challenge_table_encrypted)
{
bool status = true;
// Check if we have already read it from the file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this checked?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the comment. Left over from old code

hw/ide/atapi.c Outdated Show resolved Hide resolved
hw/ide/atapi.c Outdated Show resolved Hide resolved
Copy link

@PatrickvL PatrickvL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few code review remarks

@@ -1206,8 +1338,12 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf)
}
}

// Still some important info we don't want to lose so we disable clearing

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is one of the few "not defined" conditionals, it's perhaps nicer to change it into an #ifdef XBOX then the comment lines, and then an #else line before the code that has to be skipped under an Xbox build.

#endif

// #define DEBUG
#ifdef DEBUG

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be moved inside the above #ifdef XBOX conditional block.


ide_atapi_cmd_reply(s, sizeof(XBOX_DVD_SECURITY), max_len);
#endif
break;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This break must reside in the above conditional block, not after it.


bool xdvd_get_encrypted_challenge_table(uint8_t *xdvd_challenge_table_encrypted)
{
bool status = true;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either move this variable declaration down to reside along with the others below (since it's not referred to until after those), or (if this project still requires all variable declarations to reside at the start of a function) move all below variable declarations towards here.

(PXBOX_DVD_CHALLENGE)(&xdvd_challenge_table_decrypted[CR_ENTRIES_OFFSET]);

for (int i = 0; i < challengeEntryCount; i++) {
if (challenges[i].type != 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • We should name this constant. Supposedly 1 means the entry is valid.
  • nit: Braces around condition body, same with below

memcpy(xdvd_security, &s, sizeof(s));
};

bool xdvd_is_redump(uint64_t total_sectors)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to avoid using the term "redump" in xemu code. Although we use the term informally to mean a complete disc backup, it really is just the name of the metadata archival project and not tied directly to the layout of the disc image. So instead of using the term redump here, let's use something like xdvd_has_video_partition.

if (max_len == XDVD_SECURITY_PAGE_LEN && buf[8] == MODE_PAGE_XBOX_SECURITY)
{
memcpy(&s->xdvd_security, buf, XDVD_SECURITY_PAGE_LEN);
XBOX_DPRINTF("Authenticated: %d, Partition: %d\n", s->xdvd_security.page.Authenticated, s->xdvd_security.page.Partition);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that the drive requires the full unlock challenge-response sequence to happen before it will unlock and we are not yet fully modeling this state machine. If this is correct and you don't plan to implement this, can you leave a comment so we can remember to come back to it?

uint32_t xdvd_get_challenge_response(const uint8_t *xdvd_challenge_table_decrypted,
uint8_t challenge_id)
{
int challengeEntryCount =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: challenge_entry_count

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants