예제 #1
0
        private static string DeobfuscateFileName(string fileName, uint seed)
        {
            const string keyspace = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

            var  sb    = new StringBuilder(fileName.Length);
            uint key   = MersenneTwister.GenRand(seed);
            int  shift = (byte)((key >> 24) + (key >> 16) + (key >> 8) + key);

            for (int i = 0; i < fileName.Length; i++, shift++)
            {
                char c = fileName[i];

                // the crypto is basically caesar cipher on reversed keyspace, with shifting index
                if (keyspace.Contains(c))
                {
                    int idx        = keyspace.IndexOf(c);
                    int reverseIdx = keyspace.Length - idx - 1;
                    c = keyspace[mod(reverseIdx - shift, keyspace.Length)];
                }

                sb.Append(c);

                // mod function, because % operator is remainder
                int mod(int x, int m) => (x % m + m) % m;
            }

            return(sb.ToString());
        }
예제 #2
0
        private static void UnobfuscateFileName(byte[] s, uint seed)
        {
            const string FWD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
            const string REV = "zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA";

            //MersenneTwister.Seed(seed);
            //uint key = MersenneTwister.GenRand();
            uint key   = MersenneTwister.GenRand(seed);
            int  shift = (byte)((key >> 24) + (key >> 16) + (key >> 8) + key);

            for (int i = 0; i < s.Length; i++, shift++)
            {
                byte c = s[i];

                if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
                {
                    int index  = 0;
                    int index2 = shift;

                    while (REV[index2 % 0x34] != c)
                    {
                        if (REV[(shift + index + 1) % 0x34] == c)
                        {
                            index += 1;
                            break;
                        }

                        if (REV[(shift + index + 2) % 0x34] == c)
                        {
                            index += 2;
                            break;
                        }

                        if (REV[(shift + index + 3) % 0x34] == c)
                        {
                            index += 3;
                            break;
                        }

                        index  += 4;
                        index2 += 4;

                        if (index > 0x34)
                        {
                            break;
                        }
                    }

                    if (index < 0x34)
                    {
                        s[i] = (byte)FWD[index];
                    }
                }

                //shift++;
            }

            return;
        }
        /// <summary>
        ///  Unobfuscates the <see cref="KIFENTRY.FileNameRaw"/> field using the specified seed.
        /// </summary>
        /// <param name="fileName">The raw file name in bytes.</param>
        /// <param name="seed">
        ///  The seed used to generate the unobfuscation key.
        ///  This is the value generated by <see cref="GenerateTocSeed"/> + entryIndex.
        /// </param>
        private static void UnobfuscateFileName(byte[] fileName, uint seed)
        {
            const int    Length = 52;
            const string FWD    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
            const string REV    = "zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA";

            uint key   = MersenneTwister.GenRand(seed);
            int  shift = (byte)((key >> 24) + (key >> 16) + (key >> 8) + key);

            for (int i = 0; i < fileName.Length; i++, shift++)
            {
                byte c = fileName[i];

                if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
                {
                    int index  = 0;
                    int index2 = shift;

                    while (REV[index2 % Length] != c)
                    {
                        if (REV[(shift + index + 1) % Length] == c)
                        {
                            index += 1;
                            break;
                        }

                        if (REV[(shift + index + 2) % Length] == c)
                        {
                            index += 2;
                            break;
                        }

                        if (REV[(shift + index + 3) % Length] == c)
                        {
                            index += 3;
                            break;
                        }

                        index  += 4;
                        index2 += 4;

                        if (index >= Length)                         // We're outside the array, no need to continue
                        {
                            break;
                        }
                    }

                    if (index < Length)                     // Only assign if we're inside the array
                    {
                        fileName[i] = (byte)FWD[index];
                    }
                }
            }
        }
        private static string[] IdentifyFileTypes(Stream stream, string kifintPath, string vcode2)
        {
            BinaryReader reader = new BinaryReader(stream);
            KIFHDR       hdr    = reader.ReadUnmanaged <KIFHDR>();

            if (hdr.Signature != "KIF")             // It's really a KIF INT file
            {
                throw new UnexpectedFileTypeException(kifintPath, "KIF");
            }

            KIFENTRY[] entries = reader.ReadUnmanagedArray <KIFENTRY>(hdr.EntryCount);

            uint tocSeed = GenerateTocSeed(vcode2);
            uint fileKey = 0;
            bool decrypt = false;

            // Obtain the decryption file key if one exists
            for (int i = 0; i < hdr.EntryCount; i++)
            {
                if (entries[i].FileName == KeyFileName)
                {
                    fileKey = MersenneTwister.GenRand(entries[i].Length);
                    decrypt = true;
                    break;
                }
            }

            HashSet <string> extensions = new HashSet <string>();

            // Decrypt the KIFINT entries using the file key
            if (decrypt)
            {
                for (uint i = 0; i < hdr.EntryCount; i++)
                {
                    if (entries[i].FileName == KeyFileName)
                    {
                        continue;
                    }
                    // Give the entry the correct name
                    UnobfuscateFileName(entries[i].FileNameRaw, tocSeed + i);
                }
            }
            for (uint i = 0; i < hdr.EntryCount; i++)
            {
                string entryFileName = entries[i].FileName;
                if (entryFileName == KeyFileName)
                {
                    continue;
                }
                extensions.Add(Path.GetExtension(entryFileName));
            }

            return(extensions.ToArray());
        }
예제 #5
0
        public IEnumerable <IExtractableFile> LoadFilesFromArchive(string inputArchive)
        {
            // find keys from exe, if not present yet
            if (key1 is null || key2 is null)
            {
                (key1, key2, indexRngSeed) = LoadKeys(Path.GetDirectoryName(inputArchive));
            }

            using (var fs = File.OpenRead(inputArchive))
                using (var br = new BinaryReader(fs)) {
                    string magic = Encoding.ASCII.GetString(br.ReadBytes(4));
                    if (magic != FileMagic)
                    {
                        throw new InvalidMagicException();
                    }

                    int entries           = br.ReadInt32();
                    BlowfishDecryptor dec = null;
                    for (int i = 0; i < entries + 1; i++)
                    {
                        string fileName = br.ReadBytes(0x40).ToCString();
                        ulong  read     = br.ReadUInt64();

                        if (fileName == "__key__.dat")
                        {
                            // don't make key multiple times
                            if (!(dec is null))
                            {
                                continue;
                            }

                            uint key = MersenneTwister.GenRand((uint)(read >> 32));
                            dec = new BlowfishDecryptor(new Blowfish(BitConverter.GetBytes(key)));
                            continue;
                        }

                        if (dec is null)
                        {
                            throw new Exception("First file entry wasn't encryption key");
                        }

                        fileName = DeobfuscateFileName(fileName, (uint)unchecked (indexRngSeed + i));
                        read    += (ulong)i;
                        var newBytes = dec.TransformFinalBlock(BitConverter.GetBytes(read), 0, 8);

                        uint pos = BitConverter.ToUInt32(newBytes, 0);
                        uint len = BitConverter.ToUInt32(newBytes, 4);

                        yield return(new EncryptedFileSlice(fileName, pos, len, inputArchive, dec));
                    }
                }
        }
예제 #6
0
 public void TestInitFromArray()
 {
     MersenneTwister mt = new MersenneTwister(new ulong[] {0x12345, 0x23456, 0x34567, 0x45678});
     Assert.AreEqual(7266447313870364031, mt.GenRand());
 }
예제 #7
0
 public void TestNoSeed()
 {
     MersenneTwister mt = new MersenneTwister();
     Assert.AreEqual(14514284786278117030, mt.GenRand());
 }
        /// <summary>
        ///  Decrypts the KIFINT archives using the known archive type, install directory, and name of executable with
        ///  the V_CODE2 used to decrypt.
        /// </summary>
        /// <param name="type">The type of archive to look for and decrypt.</param>
        /// <param name="stream">The stream to the open KIFINT archive.</param>
        /// <param name="installDir">The installation directory for both the archives and executable.</param>
        /// <param name="exePath">The path to the executable to extract the V_CODE2 key from.</param>
        /// <returns>The <see cref="KifintLookup"/> merged with all loaded archives.</returns>
        ///
        /// <exception cref="ArgumentNullException">
        ///  <paramref name="stream"/>, <paramref name="kifintPath"/>, or <paramref name="exePath"/> is null.
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        ///  The <paramref name="stream"/> is closed.
        /// </exception>
        private static KifintArchive LoadLookup(KifintType type, Stream stream, string kifintPath, string vcode2,
                                                KifintProgressArgs progress, KifintProgressCallback callback)
        {
            if (kifintPath == null)
            {
                throw new ArgumentNullException(nameof(kifintPath));
            }
            if (vcode2 == null)
            {
                throw new ArgumentNullException(nameof(vcode2));
            }

            BinaryReader reader = new BinaryReader(stream);
            KIFHDR       hdr    = reader.ReadUnmanaged <KIFHDR>();

            UnexpectedFileTypeException.ThrowIfInvalid(hdr.Signature, KIFHDR.ExpectedSignature);

            KIFENTRY[] entries = reader.ReadUnmanagedArray <KIFENTRY>(hdr.EntryCount);

            progress.EntryName  = null;
            progress.EntryIndex = 0;
            progress.EntryCount = entries.Length;

            // Table of contents seed
            uint     tocSeed      = GenerateTocSeed(vcode2);
            uint     fileKey      = 0;
            int      fileKeyIndex = -1;
            Blowfish blowfish     = null;

            // Obtain the decryption file key if one exists
            for (int i = 0; i < hdr.EntryCount; i++)
            {
                if (entries[i].FileName == KeyFileName)
                {
                    fileKey      = MersenneTwister.GenRand(entries[i].Length);
                    blowfish     = CatDebug.NewBlowfish(fileKey);
                    fileKeyIndex = i;
                    break;
                }
            }

            const int ProgressThreshold = 500;

            // Decrypt the KIFINT entries using the file key
            if (fileKeyIndex != -1)
            {
                for (uint i = 0; i < hdr.EntryCount; i++, progress.EntryIndex++)
                {
                    if (unchecked ((int)i) == fileKeyIndex)
                    {
                        continue;
                    }

                    // Give the entry the correct name
                    UnobfuscateFileName(entries[i].FileNameRaw, unchecked (tocSeed + i));
                    // Apply the extra offset before decryption
                    entries[i].Offset += i;
                    // Decrypt the entry's offset and length
                    blowfish.Decrypt(ref entries[i].Info);

                    progress.EntryName = entries[i].FileName;
                    if (i % ProgressThreshold == 0 || i + 1 == hdr.EntryCount)
                    {
                        callback?.Invoke(progress);
                    }
                }
            }

            return(new KifintArchive(kifintPath, entries, fileKeyIndex != -1, fileKey, type, blowfish));
        }
        private static bool DecryptArchive(Stream inStream, string kifintPath, string kifintBackup, string vcode2, bool isBackup,
                                           KifintProgressArgs progress, KifintProgressCallback callback)
        {
            if (kifintPath == null)
            {
                throw new ArgumentNullException(nameof(kifintPath));
            }
            if (vcode2 == null)
            {
                throw new ArgumentNullException(nameof(vcode2));
            }

            /*const string BackupName = "intbackup";
             * string dir = Path.GetDirectoryName(kifintPath);
             * string name = Path.GetFileName(kifintPath);
             * string kifintBackup = Path.Combine(dir, BackupName, name);
             * bool isBackup = Path.GetFileName(dir).ToLower() == BackupName;
             * if (isBackup) {
             *      dir = Path.GetDirectoryName(dir);
             *      kifintPath = Path.Combine(dir, name);
             * }
             * else {
             *
             * }*/
            Directory.CreateDirectory(Path.GetDirectoryName(kifintBackup));

            BinaryReader reader = new BinaryReader(inStream);
            KIFHDR       hdr    = reader.ReadUnmanaged <KIFHDR>();

            try {
                UnexpectedFileTypeException.ThrowIfInvalid(hdr.Signature, KIFHDR.ExpectedSignature);
            } catch (UnexpectedFileTypeException) {
                if (!isBackup && File.Exists(kifintBackup))
                {
                    inStream.Close();
                    // We must have stopped while decrypting an archive. Let's restart from the already-made backup
                    using (inStream = File.OpenRead(kifintBackup))
                        DecryptArchive(inStream, kifintPath, kifintBackup, vcode2, true, progress, callback);
                    return(true);
                }
                throw;
            }

            List <KIFENTRY> entries = new List <KIFENTRY>(hdr.EntryCount);

            entries.AddRange(reader.ReadUnmanagedArray <KIFENTRY>(hdr.EntryCount));

            // Table of contents seed
            uint     tocSeed  = GenerateTocSeed(vcode2);
            uint     fileKey  = 0;
            bool     decrypt  = false;
            Blowfish blowfish = null;
            int      keyIndex = -1;

            // Obtain the decryption file key if one exists
            for (int i = 0; i < hdr.EntryCount; i++)
            {
                if (entries[i].FileName == KeyFileName)
                {
                    fileKey  = MersenneTwister.GenRand(entries[i].Length);
                    decrypt  = true;
                    blowfish = CatDebug.NewBlowfish(fileKey);
                    keyIndex = i;
                    break;
                }
            }

            // This archive is already decrypted, return and let the calling method know
            if (!decrypt)
            {
                return(false);
            }

            if (isBackup)
            {
                using (Stream outStream = File.Create(kifintPath))
                    DecryptArchiveSave(hdr, entries, tocSeed, fileKey, blowfish, keyIndex, inStream, outStream,
                                       progress, callback);
            }
            else
            {
                if (File.Exists(kifintBackup))
                {
                    File.Delete(kifintBackup);
                }
                inStream.Close();
                File.Move(kifintPath, kifintBackup);
                using (inStream = File.OpenRead(kifintBackup))
                    using (Stream outStream = File.Create(kifintPath))
                        DecryptArchiveSave(hdr, entries, tocSeed, fileKey, blowfish, keyIndex, inStream, outStream,
                                           progress, callback);
            }

            return(true);
        }
예제 #10
0
        private static void Run(Stream stream, string intFile, string exeFile,
                                string outputDir, ExkifintCallback progress = null)
        {
            Stopwatch watch     = Stopwatch.StartNew();
            DateTime  startTime = DateTime.UtcNow;
            string    gameId    = FindVCode2(exeFile);


            BinaryReader reader = new BinaryReader(stream);
            KIFHDR       hdr    = reader.ReadStruct <KIFHDR>();

            if (hdr.Signature != "KIF")             // It's really a KIF INT file
            {
                throw new InvalidFileException(Path.GetFileName(intFile), "INT");
            }

            KIFENTRY[] entries = reader.ReadStructArray <KIFENTRY>(hdr.EntryCount);

            uint tocSeed = GenTocSeed(gameId);
            uint fileKey = 0;
            bool decrypt = false;

            /*const string fileName = "bom_s.hg3";
             * const uint offset = 1112718577;
             * const int length = 1907629000;
             * const uint fileKey2 = 1457527205;
             * KIFENTRY kifEntry = new KIFENTRY {
             *      FileNameRaw = new char[64],
             *      Offset = offset,
             *      Length = length,
             * };
             * Array.Copy(fileName.ToCharArray(), kifEntry.FileNameRaw, fileName.Length);
             * DecryptEntry(ref kifEntry, fileKey2);*/

            ExkifintArgs args = new ExkifintArgs();

            for (int i = 0; i < hdr.EntryCount; i++)
            {
                if (entries[i].FileName == "__key__.dat")
                {
                    if (!decrypt)
                    {
                        //MersenneTwister.Seed(entries[i].Length);
                        //fileKey = MersenneTwister.GenRand();
                        fileKey = MersenneTwister.GenRand(entries[i].Length);
                        decrypt = true;
                    }
                }
                else
                {
                    args.FileCount++;
                }
            }

            DateTime  lastRefresh = DateTime.MinValue;
            Stopwatch writeTime   = new Stopwatch();
            TimeSpan  refreshTime = TimeSpan.FromMilliseconds(20);

            //Stopwatch processTime = new Stopwatch();
            for (uint i = 0; i < hdr.EntryCount; i++)
            {
                if (entries[i].FileName == "__key__.dat")
                {
                    continue;
                }

                if (decrypt)
                {
                    UnobfuscateFileName(entries[i].FileNameRaw, tocSeed + i);
                    //UnobfuscateFileName(ref entries[i].FileName, tocSeed + i);

                    entries[i].Offset += i;

                    DecryptEntry(ref entries[i].Info, fileKey);

                    /*Blowfish bf = new Blowfish();
                     * bf.Set_Key((byte*) &file_key, 4);
                     * byte[] entry_buff = entries[i].bytes;
                     * bf.Decrypt(entry_buff, 8);
                     * entries[i].bytes = entry_buff;*/
                }

                args.Ellapsed = DateTime.UtcNow - startTime;
                // Round to nearest hundredth
                args.Percent  = Math.Round((double)args.FileIndex / args.FileCount * 10000) / 100;
                args.FileName = entries[i].FileName;
                TimeSpan sinceRefresh = DateTime.UtcNow - lastRefresh;
                if (sinceRefresh >= refreshTime)
                {
                    lastRefresh = DateTime.UtcNow;
                    writeTime.Start();
                    progress?.Invoke(args);
                    writeTime.Stop();
                }

                //processTime.Restart();
                stream.Position = entries[i].Offset;
                byte[] buffer = reader.ReadBytes(entries[i].Length);

                if (decrypt)
                {
                    DecryptData(buffer, entries[i].Length, fileKey);

                    /*Blowfish bf = new Blowfish();
                     * bf.Set_Key((byte*)&file_key, 4);
                     * bf.Decrypt(buff, (len / 8) * 8);*/
                }

                string path = Path.Combine(outputDir, entries[i].FileName);
                File.WriteAllBytes(path, buffer);
                args.FileIndex++;
                //processTime.Stop();
                //if (processTime.ElapsedMilliseconds >= 500)
                //	Trace.WriteLine($"Large File: {buffer.Length / 1024:###,###,###,###}KB [{processTime.ElapsedMilliseconds}ms]");
            }

            args.Ellapsed = DateTime.UtcNow - startTime;
            args.Percent  = 100.0;
            progress?.Invoke(args);
            Trace.WriteLine($"Console Write Time: {writeTime.Elapsed:mm\\:ss\\.fff}");
        }
예제 #11
0
 public static void GH()
 {
     ulong[] arr = { 0x12345, 0x23456, 0x34567, 0x45678 };
     MersenneTwister mt = new MersenneTwister(arr);
     for (int i = 0; i < 5; i++) {
         Console.Write(String.Format("{0:d20} ", mt.GenRand()));
     }
     Console.WriteLine();
 }