FROM Software has been stepping up their game in hiding their data with each iteration of Dark Souls.
Here's some info about the files so far:
The root archives are composed of 2 files each: A .BHD file (header), and a .BDT file (data). Historically, these files are slightly different than the similarly named .BHD/.BDT files contained inside, however this hasn't been confirmed yet for DS3, since we haven't gotten full unpacking done yet.
Data0 seems to be unused by the game? At the very least, I haven't found any info about it.
For Data1, Data2, Data3, Data4, and Data5:
The .BHD is encrypted using 2048bit RSA. Each of these files have their own keys, and the keys are encrypted and hardcoded into the .exe file.
Data1:
Code: Select all
-----BEGIN RSA PUBLIC KEY-----
MIIBCwKCAQEA05hqyboW/qZaJ3GBIABFVt1X1aa0/sKINklvpkTRC+5Ytbxvp18L
M1gN6gjTgSJiPUgdlaMbptVa66MzvilEk60aHyVVEhtFWy+HzUZ3xRQm6r/2qsK3
8wXndgEU5JIT2jrBXZcZfYDCkUkjsGVkYqjBNKfp+c5jlnNwbieUihWTSEO+DA8n
aaCCzZD3e7rKhDQyLCkpdsGmuqBvl02Ou7QeehbPPno78mOYs2XkP6NGqbFFGQwa
swyyyXlQ23N15ZaFGRRR0xYjrX4LSe6OJ8Mx/Zkec0o7L28CgwCTmcD2wO8TEATE
AUbbV+1Su9uq2+wQxgnsAp+xzhn9og9hmwIEC35bSQ==
-----END RSA PUBLIC KEY-----
Data2:
Code: Select all
-----BEGIN RSA PUBLIC KEY-----
MIIBCwKCAQEAvCZAK9UfPdk5JaTlG7n1r0LSVzIan3h0BSLaMXQHOwO7tTGpvtdX
m2ZLY9y8SVmOxWTQqRq14aVGLTKDyH87hPuKd47Y0E5K5erTqBbXW6AD4El1eir2
VJz/pwHt73FVziOlAnao1A5MsAylZ9B5QJyzHJQG+LxzMzmWScyeXlQLOKudfiIG
0qFw/xhRMLNAI+iypkzO5NKblYIySUV5Dx7649XdsZ5UIwJUhxONsKuGS+MbeTFB
mTMehtNj5EwPxGdT4CBPAWdeyPhpoHJHCbgrtnN9akwQmpwdBBxT/sTD16Adn9B+
TxuGDQQALed4S4KvM+fadx27pQz8pP9VLwIEL67iCQ==
-----END RSA PUBLIC KEY-----
Data3:
Code: Select all
-----BEGIN RSA PUBLIC KEY-----
MIIBCwKCAQEAqLytWD20TSXPeAA1RGDwPW18nJwe2rBX+0HPtdzFmQc/KmQlWrP+
94k6KClK5f7m0xUHwT8+yFGLxPdRvUPyOhBEnRA6tkObVDSxij5y0Jh4h4ilAO73
I8VMcmscS71UKkck4444+eR4vVd+SPlzIu8VgqLefvEn/sX/pAevDp7w+gD0NgvO
e9U6iWEXKwTOPB97X+Y2uB03gSSognmV8h2dtUFJ4Ryn5jrpWmsuUbdvGp0CWBKH
CFruNXnfsG0hlf9LqbVmEzbFl/MhjBmbVjjtelorZsoLPK+OiPTHW5EcwwnPh1vH
FFGM7qRMc0yvHqJnniEWDsSz8Bvg+GxpgQIEC8XNVw==
-----END RSA PUBLIC KEY-----
Data4:
Code: Select all
-----BEGIN RSA PUBLIC KEY-----
MIIBCwKCAQEArfUaZWjYAUaZ0q+5znpX55GeyepawCZ5NnsMjIW9CA3vrOgUGRkh
6aAU9frlafQ81LQMRgAznOnQGE7K3ChfySDpq6b47SKm4bWPqd7Ulh2DTxIgi6QP
qm4UUJL2dkLaCnuoya/pGMOOvhT1LD/0CKo/iKwfBcYf/OAnwSnxMRC3SNRugyvF
ylCet9DEdL5L8uBEa4sV4U288ZxZSZLg2tB10xy5SHAsm1VNP4Eqw5iJbqHEDKZW
n2LJP5t5wpEJvV2ACiA4U5fyjQLDzRwtCKzeK7yFkKiZI95JJhU/3DnVvssjIxku
gYZkS9D3k9m+tkNe0VVrd4mBEmqVxg+V9wIEL6Y6tw==
-----END RSA PUBLIC KEY-----
Data5:
Code: Select all
-----BEGIN RSA PUBLIC KEY-----
MIIBCwKCAQEAvKTlU3nka4nQesRnYg1NWovCCTLhEBAnjmXwI69lFYfc4lvZsTrQ
E0Y25PtoP0ZddA3nzflJNz1rBwAkqfBRGTeeTCAyoNp/iel3EAkid/pKOt3JEkHx
rojRuWYSQ0EQawcBbzCfdLEjizmREepRKHIUSDWgu0HTmwSFHHeCFbpBA1h99L2X
izH5XFTOu0UIcUmBLsK6DYsIj5QGrWaxwwXcTJN/X+/syJ/TbQK9W/TCGaGiirGM
1u2wvZXSZ7uVM3CHwgNhAMiqLvqORygcDeNqxgq+dXDTxka43j7iPJWdHs8b25fy
aH3kbUxKlDGaEENNNyZQcQrgz8Q76jIE0QIEFUsz9w==
-----END RSA PUBLIC KEY-----
Once decrypted, the .BHD format is as follows:
Code: Select all
//--------------------------------------
//--- 010 Editor v3.1.2 Binary Template
//
// File: Dark Souls 3 .BHD (Archive Header)
// Author: Jed "Nyxojaele" Lang
// Revision: 1
//--------------------------------------
SetBackColor(cLtBlue);
typedef struct
{
ulong StringLength;
char String[StringLength] <bgcolor=0xffff55>;
} StringStruct <read=ReadStringStruct>;
string ReadStringStruct(StringStruct &ss)
{
return ss.String;
}
typedef struct
{
ulong Hash <format=hex, bgcolor=0xffff55>;
ulong BdtSize <format=hex>;
quad BdtOffset <format=hex>;
quad SaltedHashOffset <format=hex, bgcolor=0x22aaff>;
quad AesKeyOffset <format=hex, bgcolor=0x77aaff>; //Sometimes 0 (no encryption), 0x10 bytes each
quad DataSize <format=hex>; //Sometimes 0 (is compressed)
local ulong oldPos = FTell();
if (SaltedHashOffset != 0)
{
FSeek(SaltedHashOffset);
byte SaltedHash[1] <bgcolor=0x22aaff>; //Not actual size, just marking first byte
}
if (AesKeyOffset != 0)
{
FSeek(AesKeyOffset);
byte AesKey[0x10] <bgcolor=0x77aaff>;
}
FSeek(oldPos);
} EntryStruct <read=ReadEntryStruct>;
string ReadEntryStruct(EntryStruct &es)
{
if (es.AesKeyOffset != 0)
{
if (es.DataSize == 0)
return "Encrypted, Compressed";
else
return "Encrypted";
}
else
{
if (es.DataSize == 0)
return " Compressed";
}
return "";
}
typedef struct
{
ulong EntryCount;
ulong EntriesOffset <format=hex>;
} BucketStruct <read=ReadBucketStruct>;
string ReadBucketStruct(BucketStruct &bs)
{
string ret;
SPrintf(ret, "%d Entries", bs.EntryCount);
return ret;
}
byte Signature[4];
ulong Version;
ulong EndianCheck;
if (EndianCheck != 0x01)
BigEndian();
ulong ArchiveSize; //Doesn't match up perfectly? There may be padding at the end
ulong BucketCount;
ulong BucketsOffset <format=hex>;
StringStruct ArchiveName;
FSeek(BucketsOffset);
struct
{
BucketStruct Bucket[BucketCount] <optimize=false>;
} Buckets;
struct
{
local int i;
for (i = 0; i < BucketCount; i++)
{
if (Buckets.Bucket[i].EntryCount > 0)
{
FSeek(Buckets.Bucket[i].EntriesOffset);
EntryStruct Entry[Buckets.Bucket[i].EntryCount] <optimize=false>;
}
}
} Entries;
If an entry has an AESKeyOffset defined, it's data in the .BDT file is encrypted using that key. The size of the bytes that need decrypting is the entry's BdtSize. After decrypting, the data can be truncated to the entry's DataSize. (This is because the form of AES encrypting used requires data to be padded to a certain size)
If the entry has a 0 DataSize (whether encrypted or not), it's DCX compressed. Decryption must always happen before decompression. The DCX compression hasn't changed much since Dark Souls 1 -- it's basically a custom (Big-Endian!) header around a gzipped blob:
Code: Select all
byte DCXMagic[4];
ulong Version; //?
ulong DCXSize;
ulong DCXUnknown1[3];
byte DCSMagic[4];
ulong DCSUncompressedSize;
ulong DCSCompressedSize;
byte DCPMagic[4];
char DCPZlibCompSig[4];
ulong DCPSize;
ulong DCPLvl;
byte DCPUnknown1[12];
byte DCPFlags[4];
byte DCAMagic[4];
ulong DCASize;
byte[?] GZipBlob;
This GZipBlob is why some of you have had luck getting some files out of the archives, but it'll only work for compressed, non-encrypted, files.
As far as I know, this is pretty much everything anybody has figured out so far. Most of this I've put together myself, but I'd be lying if I said I did it all without some indirect help from Atvaark (author of BinderTool).
The current (rather large) issue that both my DS3Explore, and Atvaark's BinderTool exhibit is that while the encryption and compression data above is correct when used individually, any entry (this is most of them!) that is both encrypted AND compressed don't seem to work exactly as described above. Decryption seems to work fine (it generates a valid DCX header, all the way up until, and including, the 2 byte header for the GZipBlob). However, decompression fails with various internal errors. I believe the compressed data may be further encrypted, but I'm not 100% sure on this - I'm conducting further research into this at the moment.
Also worth noting is that there is no complete filename lookup dictionary available yet. This is going to be an ongoing process, as there's no simple way to just get all of the filenames.
Once these 2 issues are resolved, we'll have full unpacking of all of the main archives available, however internal archives (such as .BND, .TPF, .BHD/.BDT, etc..) may or may not work immediately. For all we know at this time, perhaps FROM Software has put further encryption on their internal file entries as well.