Beispiel #1
0
 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;
 }
Beispiel #2
0
        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();
        }
Beispiel #3
0
        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();
        }
Beispiel #4
0
        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();
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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);
        }
Beispiel #8
0
        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));
        }
Beispiel #9
0
 public CpzArchive(ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, CpzHeader header, Cpz5Decoder decoder)
     : base(arc, impl, dir)
 {
     Header = header;
     Decoder = decoder;
 }
Beispiel #10
0
        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);
        }
Beispiel #11
0
        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();
        }