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
        internal ArcFile ReadIndex(ArcView file, CmvsScheme scheme, CpzHeader cpz, byte[] index, ArchiveKey arc_key)
        {
            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, arc_key.IndexDirKey);

            decoder.Init(cpz.MasterKey, cpz.CmvsMd5[2]);
            uint base_offset = cpz.IndexOffset + cpz.IndexSize;
            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, arc_key.IndexEntryKey);
                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 <= cpz.EntryNameOffset)
                    {
                        return(null);
                    }
                    int name_offset = cur_offset + cpz.EntryNameOffset;
                    var name        = Binary.GetCString(index, name_offset, cur_entries_end - name_offset);
                    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);
                    int key_offset = cur_offset + 0x10;
                    if (cpz.IsLongSize)
                    {
                        key_offset += 4;
                    }
                    entry.CheckSum = LittleEndian.ToUInt32(index, key_offset);
                    entry.Key      = LittleEndian.ToUInt32(index, key_offset + 4) + 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, arc_key));
        }