Xbox Virtual Disk
XVD/XVC files are used by the Xbox One as file containers, an evolution on the STFS container format used by the Xbox 360.
It is theorized that the .xvd format is a modification of either the .vhd / .vhdx formats, or the .wim / .esd format. A function included in the Xbox One SDK named "XsCreateConvertVhd2XVD" gives credit to this theory.
XVD File Header
(this structure doesn't include the 0x200 byte signature at the top of XVD files)
|0x00||0x08||string||File magic (msft-xvd)|
|0x18||0x08||int64||Unknown (seems to be important though)|
|0x30||0x10||bytes||Duplicate of Content ID?|
|0x40||0x20||bytes||Top Hash Table Hash|
|0x60||0x20||bytes||Unknown (hash of some kind?)|
|0x80||0x4||int32||Unknown (seems to get used to check if XVC data is shifted, unsure how its used tho)|
|0x88||0x4||int32||Unknown (seems important)|
|0x8C||0x4||int32||Unknown (seems important)|
|0x90||0x4||int32||XVC data size|
|0x94||0x4||int32||Block start offset (minus 0x4000 for header+hash table, divided by 0x400) (most likely incorrect in 99% of cases)|
|0x1AC||0x10||bytes||Build ID, changed with every XVD package creation|
|0x1C4||0xC3C||bytes||Rest of header, doesn't seem to be used|
|0xE00||0x2000||bytes||padding up to the hash table / XVC info|
Storage type is still a mystery, the following have been observed so far:
Top hash table hash is calculated using SHA2-256 over the hash table at 0x3000-0x4000 (in files with storage type 0x41/0x43)
That table is composed of SHA2-256 hashes of the tables at 0x4000, 0x5000, 0x6000, etc.
Those tables are composed of SHA-256 hashes of each data block.
The hashes in these tables (including the top table) are truncated to 0x18 bytes, the hashes in the header are kept at a full 0x20 bytes.
The 0x200 byte signature that comes before the header is a RSA(4096?) signature of the SHA2-256 hash of the header following it (0x200 - 0x1000)
Storage type 0x07 files can be mounted on Windows by using the xsapi.dll file:
typedef int(__cdecl* XvdOpenAdapter)(HANDLE* handle); typedef int(__cdecl* XvdCloseAdapter)(HANDLE handle); typedef int(__cdecl* XvdMount)(void* unknownReturnValue1, void* unknownReturnValue2, HANDLE xvdHandle, LPCWSTR filePath, int64_t setToZero, int64_t setToZero2, int32_t setToZero3); typedef int(__cdecl* XvdUnmountFile)(HANDLE xvdHandle, LPCWSTR filePath);
Type 0x43 files can also be modded into 0x07 files and mounted by removing the hash tables, nulling the signature and changing the storage type to 0x07.
XVC info is stored at block 0, you can find the offset by taking the block start offset from the header, multiply it by 0x400 and add 0x4000. (todo: find the proper way, because this isn't it) However, if the storage type is 0x07 then block 0 will always be at 0x3000.
You can retrieve XVC data by using xsapi.dll, by setting data to NULL you can retrieve the size of it in case it changes:
typedef int(__cdecl* XsReadXvcInfoXVD)(LPCWSTR filename, void* data, int32_t* dataSize);
The size of it is also defined in the XVD header, usually 0x2000. Although true XVD files such as the ones in system updates don't have XVC info and have the XVC data size set to 0.
|0x10||0x10||bytes||Encryption Key GUID|
|0x20||0x1FE0||bytes||Padding, may contain other encryption keys?|
Files with encryption key ID 33ec8436-5a0e-4f0d-b1ce-3f29c3955039 are encrypted with the devkit key (aka DevContentKeyEscrow_v1_Public?)
This key can be found inside xvdsign.exe.