static void Main(string[] args) { if (args.Length == 0) { Console.WriteLine("Usage: UnWak [FILE] [<path>]"); Console.WriteLine("Unpack wak file"); return; } byte[] wak_buffer = null; try { using (FileStream wak_file = File.OpenRead(args[0])) { wak_buffer = new byte[wak_file.Length]; wak_file.Read(wak_buffer, 0, (int)wak_file.Length); } } catch (Exception e) { Console.WriteLine($"Failed to read wak file: {e}"); Environment.Exit(-1); } FiletableEntry[] filetable = WakDecryptor.DecryptWak(wak_buffer); Console.WriteLine($"Wak file decrypted, found {filetable.Length} files"); Console.Write("Unpacking... 0%"); string parent_path = args.Length > 1 ? args[1] : ""; for (int i = 0; i < filetable.Length; i++) { FiletableEntry filetable_entry = filetable[i]; string path = Path.Combine(parent_path, filetable_entry.filename); try { Directory.CreateDirectory(Path.GetDirectoryName(path)); using (FileStream file = File.Create(path)) { file.Write(wak_buffer, filetable_entry.file_offset, filetable_entry.file_size); } } catch (Exception e) { Console.WriteLine($"\nFailed to write file {path}: {e}"); Environment.Exit(-1); } if (i % (filetable.Length / 100) == 0) { Console.Write($"\b\b\b\b{(100 * i / filetable.Length).ToString().PadLeft(3, ' ')}%"); } } Console.WriteLine("\b\b\b\b100%\nDone"); }
/// <summary> /// Decrypts the specified wak file and copies the resulting data to the output byte array /// </summary> /// <param name="wak_file">The wak file to decrypt</param> /// <param name="output">The output to which to write the decrypted file</param> /// <returns></returns> static public FiletableEntry[] DecryptWak(byte[] wak_file, byte[] output) { byte[] key = new byte[16]; byte[] IV = new byte[16]; GenerateIV(key, Constants.wak_key_seed); GenerateIV(IV, Constants.wak_header_IV_seed); Aes128CounterMode aes = new Aes128CounterMode(IV); ICryptoTransform ict = aes.CreateDecryptor(key, null); ict.TransformBlock(wak_file, 0, 16, output, 0); // decrypt header int num_files = BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan <byte>(output, 4, 4)); int files_offset = BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan <byte>(output, 8, 4)); FiletableEntry[] fileTable = new FiletableEntry[num_files]; GenerateIV(IV, Constants.wak_filetable_IV_seed); ict.TransformBlock(wak_file, 16, files_offset - 16, output, 16); // decrypt filetable int entry_offset = 16; for (int i = 0; i < num_files; i++) { int file_offset = BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan <byte>(output, entry_offset, 4)); int file_size = BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan <byte>(output, entry_offset + 4, 4)); int filename_length = BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan <byte>(output, entry_offset + 8, 4)); string filename = Encoding.UTF8.GetString(new ReadOnlySpan <byte>(output, entry_offset + 12, filename_length)); GenerateIV(IV, i); // file IV ict.TransformBlock(wak_file, file_offset, file_size, output, file_offset); // decrypt file fileTable[i].file_offset = file_offset; fileTable[i].file_size = file_size; fileTable[i].filename = filename; entry_offset += 12 + filename_length; } return(fileTable); }