From e5c555c2a70711f43c5c66b1645921da6e4e9017 Mon Sep 17 00:00:00 2001 From: Xpl0itU <24777100+Xpl0itU@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:48:22 +0200 Subject: [PATCH] WIP Support for Wii's TMD --- certificate.go | 2 +- decryption.go | 74 ++++++++++---------------------------------------- tmd.go | 48 ++++++++++++++++++++++++++------ 3 files changed, 54 insertions(+), 70 deletions(-) diff --git a/certificate.go b/certificate.go index ae9d1ac..8cd351e 100644 --- a/certificate.go +++ b/certificate.go @@ -10,7 +10,7 @@ import ( var cetkData []byte -func getCert(tmdData []byte, id int, numContents uint16) ([]byte, error) { +func getCert(tmdData []byte, id int, numContents uint16) ([]byte, error) { // TODO: Add support for Wii's TMD var certSlice []byte if len(tmdData) == int((0x0B04+0x30*numContents+0xA00)-0x300) { certSlice = tmdData[0x0B04+0x30*numContents : 0x0B04+0x30*numContents+0xA00-0x300] diff --git a/decryption.go b/decryption.go index 2cae75c..399120d 100644 --- a/decryption.go +++ b/decryption.go @@ -412,69 +412,21 @@ func DecryptContents(path string, progressReporter ProgressReporter, deleteEncry return err } - // find title id and content id - var titleID []byte - var contentCount uint16 - tmd, err := os.Open(tmdPath) + tmdData, err := os.ReadFile(tmdPath) if err != nil { return err } - defer tmd.Close() - - tmd.Seek(0x18C, io.SeekStart) - titleID = make([]byte, 8) - if _, err := io.ReadFull(tmd, titleID); err != nil { + tmd, err := parseTMD(tmdData) + if err != nil { return err } - tmd.Seek(0x1DE, io.SeekStart) - if err := binary.Read(tmd, binary.BigEndian, &contentCount); err != nil { - return err - } - - tmd.Seek(0x204, io.SeekStart) - tmdIndex := make([]byte, 2) - if _, err := io.ReadFull(tmd, tmdIndex); err != nil { - return err - } - - contents := make([]Content, contentCount) - - for c := uint16(0); c < contentCount; c++ { - offset := 2820 + (48 * c) - tmd.Seek(int64(offset), io.SeekStart) - if err := binary.Read(tmd, binary.BigEndian, &contents[c].ID); err != nil { - return err - } - - tmd.Seek(0xB08+(0x30*int64(c)), io.SeekStart) - contents[c].Index = make([]byte, 2) - if _, err := io.ReadFull(tmd, contents[c].Index); err != nil { - return err - } - - tmd.Seek(0xB0A+(0x30*int64(c)), io.SeekStart) - if err := binary.Read(tmd, binary.BigEndian, &contents[c].Type); err != nil { - return err - } - - tmd.Seek(0xB0C+(0x30*int64(c)), io.SeekStart) - if err := binary.Read(tmd, binary.BigEndian, &contents[c].Size); err != nil { - return err - } - - tmd.Seek(0xB14+(0x30*int64(c)), io.SeekStart) - contents[c].Hash = make([]byte, 0x14) - if _, err := io.ReadFull(tmd, contents[c].Hash); err != nil { - return err - } - - contents[c].CIDStr = fmt.Sprintf("%08X", contents[c].ID) - - _, err := os.Stat(filepath.Join(path, contents[c].CIDStr+".app")) + // Check if all contents are present and how they are named + for i := 0; i < len(tmd.Contents); i++ { + _, err := os.Stat(filepath.Join(path, tmd.Contents[i].CIDStr+".app")) if err != nil { - contents[c].CIDStr = fmt.Sprintf("%08x", contents[c].ID) - _, err = os.Stat(filepath.Join(path, contents[c].CIDStr+".app")) + tmd.Contents[i].CIDStr = fmt.Sprintf("%08x", tmd.Contents[i].ID) + _, err = os.Stat(filepath.Join(path, tmd.Contents[i].CIDStr+".app")) if err != nil { return errors.New("content not found") } @@ -500,7 +452,9 @@ func DecryptContents(path string, progressReporter ProgressReporter, deleteEncry return err } - cbc := cipher.NewCBCDecrypter(c, append(titleID, make([]byte, 8)...)) + titleIDBytes := make([]byte, 8) + binary.BigEndian.PutUint64(titleIDBytes, tmd.TitleID) + cbc := cipher.NewCBCDecrypter(c, append(titleIDBytes, make([]byte, 8)...)) decryptedTitleKey := make([]byte, len(encryptedTitleKey)) cbc.CryptBlocks(decryptedTitleKey, encryptedTitleKey) @@ -510,14 +464,14 @@ func DecryptContents(path string, progressReporter ProgressReporter, deleteEncry return fmt.Errorf("failed to create AES cipher: %w", err) } - fstEncFile, err := os.Open(filepath.Join(path, contents[0].CIDStr+".app")) + fstEncFile, err := os.Open(filepath.Join(path, tmd.Contents[0].CIDStr+".app")) if err != nil { return err } defer fstEncFile.Close() decryptedBuffer := bytes.Buffer{} - if err := decryptContentToBuffer(fstEncFile, &decryptedBuffer, cipherHashTree, contents[0]); err != nil { + if err := decryptContentToBuffer(fstEncFile, &decryptedBuffer, cipherHashTree, tmd.Contents[0]); err != nil { return err } fst := FSTData{FSTReader: bytes.NewReader(bytes.Clone(decryptedBuffer.Bytes())), FSTEntries: make([]FEntry, 0), EntryCount: 0, Entries: 0, NamesOffset: 0} @@ -560,7 +514,7 @@ func DecryptContents(path string, progressReporter ProgressReporter, deleteEncry contentOffset <<= 5 } if fst.FSTEntries[i].Type&0x80 == 0 { - matchingContent := contents[fst.FSTEntries[i].ContentID] + matchingContent := tmd.Contents[fst.FSTEntries[i].ContentID] tmdFlags := matchingContent.Type srcFile, err := os.Open(filepath.Join(path, matchingContent.CIDStr+".app")) if err != nil { diff --git a/tmd.go b/tmd.go index dfa2942..31b69ab 100644 --- a/tmd.go +++ b/tmd.go @@ -13,6 +13,7 @@ const ( ) type TMD struct { + TitleID uint64 Version byte TitleVersion uint16 ContentCount uint16 @@ -30,6 +31,11 @@ func parseTMD(data []byte) (*TMD, error) { switch tmd.Version { case TMD_VERSION_WII: + reader.Seek(0x18C, io.SeekStart) + if err := binary.Read(reader, binary.BigEndian, &tmd.TitleID); err != nil { + return nil, err + } + reader.Seek(0x1DC, io.SeekStart) if err := binary.Read(reader, binary.BigEndian, &tmd.TitleVersion); err != nil { @@ -50,7 +56,11 @@ func parseTMD(data []byte) (*TMD, error) { return nil, err } - reader.Seek(2, io.SeekCurrent) + reader.Seek(0x1E8+(0x24*int64(i)), io.SeekStart) + tmd.Contents[i].Index = make([]byte, 2) + if _, err := io.ReadFull(reader, tmd.Contents[i].Index); err != nil { + return nil, err + } if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Type); err != nil { return nil, err @@ -59,8 +69,17 @@ func parseTMD(data []byte) (*TMD, error) { if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Size); err != nil { return nil, err } + + tmd.Contents[i].Hash = make([]byte, 0x14) + if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Hash); err != nil { + return nil, err + } } case TMD_VERSION_WIIU: + reader.Seek(0x18C, io.SeekStart) + if err := binary.Read(reader, binary.BigEndian, &tmd.TitleID); err != nil { + return nil, err + } reader.Seek(0x1DC, io.SeekStart) if err := binary.Read(reader, binary.BigEndian, &tmd.TitleVersion); err != nil { @@ -73,21 +92,32 @@ func parseTMD(data []byte) (*TMD, error) { tmd.Contents = make([]Content, tmd.ContentCount) - for i := 0; i < int(tmd.ContentCount); i++ { - offset := 0xB04 + (0x30 * i) - + for c := uint16(0); c < tmd.ContentCount; c++ { + offset := 2820 + (48 * c) reader.Seek(int64(offset), io.SeekStart) - if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].ID); err != nil { + if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[c].ID); err != nil { return nil, err } - reader.Seek(2, io.SeekCurrent) - - if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Type); err != nil { + reader.Seek(0xB08+(0x30*int64(c)), io.SeekStart) + tmd.Contents[c].Index = make([]byte, 2) + if _, err := io.ReadFull(reader, tmd.Contents[c].Index); err != nil { return nil, err } - if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[i].Size); err != nil { + reader.Seek(0xB0A+(0x30*int64(c)), io.SeekStart) + if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[c].Type); err != nil { + return nil, err + } + + reader.Seek(0xB0C+(0x30*int64(c)), io.SeekStart) + if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[c].Size); err != nil { + return nil, err + } + + reader.Seek(0xB14+(0x30*int64(c)), io.SeekStart) + tmd.Contents[c].Hash = make([]byte, 0x14) + if err := binary.Read(reader, binary.BigEndian, &tmd.Contents[c].Hash); err != nil { return nil, err } }