public CpzArchive(ArcView arc, ArchiveFormat impl, ICollection <Entry> dir, CpzHeader header, Cpz5Decoder decoder, ArchiveKey key) : base(arc, impl, dir) { Header = header; Decoder = decoder; Key = key; }
public override ArcFile TryOpen(ArcView file) { if (null == KnownSchemes) { throw new OperationCanceledException("Outdated encryption schemes database"); } var cpz = CpzHeader.Parse(file); if (null == cpz) { return(null); } var index = file.View.ReadBytes(cpz.IndexOffset, cpz.IndexSize); if (!cpz.VerifyIndex(index)) { return(null); } int file_table_size = cpz.DirEntriesSize + cpz.FileEntriesSize; if (cpz.IndexKeySize > 24) { var index_key = UnpackIndexKey(index, file_table_size, cpz.IndexKeySize); for (int i = 0; i < file_table_size; ++i) { index[i] ^= index_key[(i + 3) % 0x3FF]; } } ArchiveKey arc_key = null; if (cpz.Version > 6) { arc_key = FindArchiveKey(file.Name); } if (null == arc_key) { arc_key = new ArchiveKey(); } var index_copy = new CowArray <byte> (index, 0, file_table_size).ToArray(); var cmvs_md5 = cpz.CmvsMd5.Clone() as uint[]; foreach (var scheme in KnownSchemes.Values.Where(s => s.Version == cpz.Version)) { var arc = ReadIndex(file, scheme, cpz, index, arc_key); if (null != arc) { return(arc); } // both CmvsMd5 and index was altered by ReadIndex in decryption attempt Array.Copy(cmvs_md5, cpz.CmvsMd5, 4); Array.Copy(index, index_copy, file_table_size); } throw new UnknownEncryptionScheme(); }
public override ArcFile TryOpen(ArcView file) { if (null == KnownSchemes) { throw new OperationCanceledException("Outdated encryption schemes database"); } var header = file.View.ReadBytes(0, 0x3C); var checksum = file.View.ReadUInt32(0x3C); if (checksum != CheckSum(header, 0, 0x3C, 0x923A564Cu)) { return(null); } var cpz = new CpzHeader { DirCount = -0x1C5AC27 ^ LittleEndian.ToInt32(header, 4), DirEntriesSize = 0x37F298E7 ^ LittleEndian.ToInt32(header, 8), FileEntriesSize = 0x7A6F3A2C ^ LittleEndian.ToInt32(header, 0x0C), MasterKey = 0xAE7D39BF ^ LittleEndian.ToUInt32(header, 0x30), IsEncrypted = 0 != (0xFB73A955 ^ LittleEndian.ToUInt32(header, 0x34)), IsCompressed = 0 != (0x37ACF831 ^ LittleEndian.ToUInt32(header, 0x38)), }; var cmvs_md5 = new uint[] { 0x43DE7C19 ^ LittleEndian.ToUInt32(header, 0x20), 0xCC65F415 ^ LittleEndian.ToUInt32(header, 0x24), 0xD016A93C ^ LittleEndian.ToUInt32(header, 0x28), 0x97A3BA9A ^ LittleEndian.ToUInt32(header, 0x2C), }; int index_size = cpz.DirEntriesSize + cpz.FileEntriesSize; var index = file.View.ReadBytes(0x40, (uint)index_size); if (index.Length != index_size) { return(null); } using (var md5 = MD5.Create()) { var hash = md5.ComputeHash(index); if (!header.Skip(0x10).Take(0x10).SequenceEqual(hash)) { return(null); } } foreach (var scheme in KnownSchemes.Values) { // both CmvsMd5 and index will be altered by ReadIndex in decryption attempt Array.Copy(cmvs_md5, cpz.CmvsMd5, 4); var arc = ReadIndex(file, scheme, cpz, index); if (null != arc) { return(arc); } file.View.Read(0x40, index, 0, (uint)index_size); } throw new UnknownEncryptionScheme(); }
public override ArcFile TryOpen(ArcView file) { if (null == KnownSchemes) { throw new OperationCanceledException("Outdated encryption schemes database"); } var header = file.View.ReadBytes(0, 0x3C); var checksum = file.View.ReadUInt32(0x3C); if (checksum != CheckSum(header, 0, 0x3C, 0x923A564Cu)) { return(null); } int version = header[3] - '0'; CpzHeader cpz = 5 == version?ReadHeaderV5(header) : ReadHeaderV6(header); int index_size = cpz.DirEntriesSize + cpz.FileEntriesSize; var index = file.View.ReadBytes(0x40, (uint)index_size); if (index.Length != index_size) { return(null); } using (var md5 = MD5.Create()) { var hash = md5.ComputeHash(index); if (!header.Skip(0x10).Take(0x10).SequenceEqual(hash)) { return(null); } } var cmvs_md5 = cpz.CmvsMd5.Clone() as uint[]; foreach (var scheme in KnownSchemes.Values.Where(s => s.Version == version)) { var arc = ReadIndex(file, scheme, cpz, index); if (null != arc) { return(arc); } // both CmvsMd5 and index was altered by ReadIndex in decryption attempt Array.Copy(cmvs_md5, cpz.CmvsMd5, 4); file.View.Read(0x40, index, 0, (uint)index_size); } throw new UnknownEncryptionScheme(); }
CpzHeader ReadHeaderV6(byte[] header) { uint entry_key = 0x37ACF832 ^ LittleEndian.ToUInt32(header, 0x38); var cpz = new CpzHeader { DirCount = -0x1C5AC26 ^ LittleEndian.ToInt32(header, 4), DirEntriesSize = 0x37F298E8 ^ LittleEndian.ToInt32(header, 8), FileEntriesSize = 0x7A6F3A2D ^ LittleEndian.ToInt32(header, 0x0C), MasterKey = 0xAE7D39B7 ^ LittleEndian.ToUInt32(header, 0x30), IsEncrypted = 0 != (0xFB73A956 ^ LittleEndian.ToUInt32(header, 0x34)), EntryKey = 0x7DA8F173 * Binary.RotR(entry_key, 5) + 0x13712765, }; cpz.CmvsMd5[0] = 0x43DE7C1A ^ LittleEndian.ToUInt32(header, 0x20); cpz.CmvsMd5[1] = 0xCC65F416 ^ LittleEndian.ToUInt32(header, 0x24); cpz.CmvsMd5[2] = 0xD016A93D ^ LittleEndian.ToUInt32(header, 0x28); cpz.CmvsMd5[3] = 0x97A3BA9B ^ LittleEndian.ToUInt32(header, 0x2C); return(cpz); }
CpzHeader ReadHeaderV5(byte[] header) { uint entry_key = 0x37ACF831 ^ LittleEndian.ToUInt32(header, 0x38); var cpz = new CpzHeader { DirCount = -0x1C5AC27 ^ LittleEndian.ToInt32(header, 4), DirEntriesSize = 0x37F298E7 ^ LittleEndian.ToInt32(header, 8), FileEntriesSize = 0x7A6F3A2C ^ LittleEndian.ToInt32(header, 0x0C), MasterKey = 0xAE7D39BF ^ LittleEndian.ToUInt32(header, 0x30), IsEncrypted = 0 != (0xFB73A955 ^ LittleEndian.ToUInt32(header, 0x34)), EntryKey = 0, }; cpz.CmvsMd5[0] = 0x43DE7C19 ^ LittleEndian.ToUInt32(header, 0x20); cpz.CmvsMd5[1] = 0xCC65F415 ^ LittleEndian.ToUInt32(header, 0x24); cpz.CmvsMd5[2] = 0xD016A93C ^ LittleEndian.ToUInt32(header, 0x28); cpz.CmvsMd5[3] = 0x97A3BA9A ^ LittleEndian.ToUInt32(header, 0x2C); return(cpz); }
public static CpzHeader Parse(ArcView file) { var cpz = new CpzHeader { Version = file.View.ReadByte(3) - '0' }; int checksum_length; byte[] header; if (cpz.Version < 7) { header = file.View.ReadBytes(0, 0x40); if (cpz.Version < 6) { cpz.ParseV5(header); } else { cpz.ParseV6(header); } cpz.IndexOffset = 0x40; cpz.IndexSize = (uint)(cpz.DirEntriesSize + cpz.FileEntriesSize); cpz.Checksum = header.ToUInt32(0x3C); checksum_length = 0x3C; } else { header = file.View.ReadBytes(0, 0x48); cpz.ParseV7(header); cpz.IndexOffset = 0x48; cpz.IndexSize = (uint)(cpz.DirEntriesSize + cpz.FileEntriesSize + cpz.IndexKeySize); cpz.Checksum = header.ToUInt32(0x44); checksum_length = 0x40; } if (cpz.Checksum != CheckSum(header, 0, checksum_length, cpz.InitChecksum)) { return(null); } cpz.IndexMd5 = header.Skip(0x10).Take(0x10).ToArray(); return(cpz); }
ArcFile ReadIndex(ArcView file, CmvsScheme scheme, CpzHeader cpz, byte[] index) { var cmvs_md5 = Cmvs.MD5.Create(scheme.Md5Variant); cmvs_md5.Compute(cpz.CmvsMd5); DecryptIndexStage1(index, cpz.MasterKey ^ 0x3795B39A, scheme); var decoder = new Cpz5Decoder(scheme, cpz.MasterKey, cpz.CmvsMd5[1]); decoder.Decode(index, 0, cpz.DirEntriesSize, 0x3A); var key = new uint[4]; key[0] = cpz.CmvsMd5[0] ^ (cpz.MasterKey + 0x76A3BF29); key[1] = cpz.CmvsMd5[1] ^ cpz.MasterKey; key[2] = cpz.CmvsMd5[2] ^ (cpz.MasterKey + 0x10000000); key[3] = cpz.CmvsMd5[3] ^ cpz.MasterKey; DecryptIndexDirectory(index, cpz.DirEntriesSize, key); decoder.Init(cpz.MasterKey, cpz.CmvsMd5[2]); int base_offset = 0x40 + index.Length; int dir_offset = 0; var dir = new List <Entry>(); for (int i = 0; i < cpz.DirCount; ++i) { int dir_size = LittleEndian.ToInt32(index, dir_offset); if (dir_size <= 0x10 || dir_size > index.Length) { return(null); } int file_count = LittleEndian.ToInt32(index, dir_offset + 4); if (file_count >= 0x10000) { return(null); } int entries_offset = LittleEndian.ToInt32(index, dir_offset + 8); uint dir_key = LittleEndian.ToUInt32(index, dir_offset + 0xC); var dir_name = Binary.GetCString(index, dir_offset + 0x10, dir_size - 0x10); int next_entries_offset; if (i + 1 == cpz.DirCount) { next_entries_offset = cpz.FileEntriesSize; } else { next_entries_offset = LittleEndian.ToInt32(index, dir_offset + dir_size + 8); } int cur_entries_size = next_entries_offset - entries_offset; if (cur_entries_size <= 0) { return(null); } int cur_offset = cpz.DirEntriesSize + entries_offset; int cur_entries_end = cur_offset + cur_entries_size; decoder.Decode(index, cur_offset, cur_entries_size, 0x7E); for (int j = 0; j < 4; ++j) { key[j] = cpz.CmvsMd5[j] ^ (dir_key + scheme.DirKeyAddend[j]); } DecryptIndexEntry(index, cur_offset, cur_entries_size, key, scheme.IndexSeed); bool is_root_dir = dir_name == "root"; dir.Capacity = dir.Count + file_count; for (int j = 0; j < file_count; ++j) { int entry_size = LittleEndian.ToInt32(index, cur_offset); if (entry_size > index.Length || entry_size <= 0x18) { return(null); } var name = Binary.GetCString(index, cur_offset + 0x18, cur_entries_end - (cur_offset + 0x18)); if (!is_root_dir) { name = Path.Combine(dir_name, name); } var entry = FormatCatalog.Instance.Create <CpzEntry> (name); entry.Offset = LittleEndian.ToInt64(index, cur_offset + 4) + base_offset; entry.Size = LittleEndian.ToUInt32(index, cur_offset + 0xC); entry.CheckSum = LittleEndian.ToUInt32(index, cur_offset + 0x10); entry.Key = LittleEndian.ToUInt32(index, cur_offset + 0x14) + dir_key; if (!entry.CheckPlacement(file.MaxOffset)) { return(null); } dir.Add(entry); cur_offset += entry_size; } dir_offset += dir_size; } if (cpz.IsEncrypted) { decoder.Init(cpz.CmvsMd5[3], cpz.MasterKey); } return(new CpzArchive(file, this, dir, cpz, decoder)); }
public CpzArchive(ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, CpzHeader header, Cpz5Decoder decoder) : base(arc, impl, dir) { Header = header; Decoder = decoder; }
ArcFile ReadIndex(ArcView file, CmvsScheme scheme, CpzHeader cpz, byte[] index) { var cmvs_md5 = Cmvs.MD5.Create (scheme.Md5Variant); cmvs_md5.Compute (cpz.CmvsMd5); DecryptIndexStage1 (index, cpz.MasterKey ^ 0x3795B39A, scheme.Cpz5Secret); var decoder = new Cpz5Decoder (scheme, cpz.MasterKey, cpz.CmvsMd5[1]); decoder.Decode (index, 0, cpz.DirEntriesSize, 0x3A); var key = new uint[4]; key[0] = cpz.CmvsMd5[0] ^ (cpz.MasterKey + 0x76A3BF29); key[1] = cpz.CmvsMd5[1] ^ cpz.MasterKey; key[2] = cpz.CmvsMd5[2] ^ (cpz.MasterKey + 0x10000000); key[3] = cpz.CmvsMd5[3] ^ cpz.MasterKey; DecryptIndexDirectory (index, cpz.DirEntriesSize, key); decoder.Init (cpz.MasterKey, cpz.CmvsMd5[2]); int base_offset = 0x40 + index.Length; int dir_offset = 0; var dir = new List<Entry>(); for (int i = 0; i < cpz.DirCount; ++i) { int dir_size = LittleEndian.ToInt32 (index, dir_offset); if (dir_size <= 0x10 || dir_size > index.Length) return null; int file_count = LittleEndian.ToInt32 (index, dir_offset+4); if (file_count >= 0x10000) return null; int entries_offset = LittleEndian.ToInt32 (index, dir_offset+8); uint dir_key = LittleEndian.ToUInt32 (index, dir_offset+0xC); var dir_name = Binary.GetCString (index, dir_offset+0x10, dir_size-0x10); int next_entries_offset; if (i + 1 == cpz.DirCount) next_entries_offset = cpz.FileEntriesSize; else next_entries_offset = LittleEndian.ToInt32 (index, dir_offset + dir_size + 8); int cur_entries_size = next_entries_offset - entries_offset; if (cur_entries_size <= 0) return null; int cur_offset = cpz.DirEntriesSize + entries_offset; int cur_entries_end = cur_offset + cur_entries_size; decoder.Decode (index, cur_offset, cur_entries_size, 0x7E); key[0] = cpz.CmvsMd5[0] ^ dir_key; key[1] = cpz.CmvsMd5[1] ^ (dir_key + 0x112233); key[2] = cpz.CmvsMd5[2] ^ dir_key; key[3] = cpz.CmvsMd5[3] ^ (dir_key + 0x34258765); DecryptIndexEntry (index, cur_offset, cur_entries_size, key); bool is_root_dir = dir_name == "root"; dir.Capacity = dir.Count + file_count; for (int j = 0; j < file_count; ++j) { int entry_size = LittleEndian.ToInt32 (index, cur_offset); if (entry_size > index.Length || entry_size <= 0x18) return null; var name = Binary.GetCString (index, cur_offset+0x18, cur_entries_end-(cur_offset+0x18)); if (!is_root_dir) name = Path.Combine (dir_name, name); var entry = FormatCatalog.Instance.Create<CpzEntry> (name); entry.Offset = LittleEndian.ToInt64 (index, cur_offset+4) + base_offset; entry.Size = LittleEndian.ToUInt32 (index, cur_offset+0xC); entry.CheckSum = LittleEndian.ToUInt32 (index, cur_offset+0x10); entry.Key = LittleEndian.ToUInt32 (index, cur_offset+0x14); entry.DirKey = dir_key; if (!entry.CheckPlacement (file.MaxOffset)) return null; dir.Add (entry); cur_offset += entry_size; } dir_offset += dir_size; } if (cpz.IsEncrypted) decoder.Init (cpz.CmvsMd5[3], cpz.MasterKey); return new CpzArchive (file, this, dir, cpz, decoder); }
public override ArcFile TryOpen(ArcView file) { if (null == KnownSchemes) throw new OperationCanceledException ("Outdated encryption schemes database"); var header = file.View.ReadBytes (0, 0x3C); var checksum = file.View.ReadUInt32 (0x3C); if (checksum != CheckSum (header, 0, 0x3C, 0x923A564Cu)) return null; var cpz = new CpzHeader { DirCount = -0x1C5AC27 ^ LittleEndian.ToInt32 (header, 4), DirEntriesSize = 0x37F298E7 ^ LittleEndian.ToInt32 (header, 8), FileEntriesSize = 0x7A6F3A2C ^ LittleEndian.ToInt32 (header, 0x0C), MasterKey = 0xAE7D39BF ^ LittleEndian.ToUInt32 (header, 0x30), IsEncrypted = 0 != (0xFB73A955 ^ LittleEndian.ToUInt32 (header, 0x34)), IsCompressed = 0 != (0x37ACF831 ^ LittleEndian.ToUInt32 (header, 0x38)), }; var cmvs_md5 = new uint[] { 0x43DE7C19 ^ LittleEndian.ToUInt32 (header, 0x20), 0xCC65F415 ^ LittleEndian.ToUInt32 (header, 0x24), 0xD016A93C ^ LittleEndian.ToUInt32 (header, 0x28), 0x97A3BA9A ^ LittleEndian.ToUInt32 (header, 0x2C), }; int index_size = cpz.DirEntriesSize + cpz.FileEntriesSize; var index = file.View.ReadBytes (0x40, (uint)index_size); if (index.Length != index_size) return null; using (var md5 = MD5.Create()) { var hash = md5.ComputeHash (index); if (!header.Skip (0x10).Take (0x10).SequenceEqual (hash)) return null; } foreach (var scheme in KnownSchemes.Values) { // both CmvsMd5 and index will be altered by ReadIndex in decryption attempt Array.Copy (cmvs_md5, cpz.CmvsMd5, 4); var arc = ReadIndex (file, scheme, cpz, index); if (null != arc) return arc; file.View.Read (0x40, index, 0, (uint)index_size); } throw new UnknownEncryptionScheme(); }