diff --git a/src/Dax.Vpax.Obfuscator.CLI/Dax.Vpax.Obfuscator.CLI.csproj b/src/Dax.Vpax.Obfuscator.CLI/Dax.Vpax.Obfuscator.CLI.csproj index 85b2c09..36688f7 100644 --- a/src/Dax.Vpax.Obfuscator.CLI/Dax.Vpax.Obfuscator.CLI.csproj +++ b/src/Dax.Vpax.Obfuscator.CLI/Dax.Vpax.Obfuscator.CLI.csproj @@ -4,6 +4,7 @@ net6.0 Exe false + VpaxObfuscator diff --git a/src/Dax.Vpax.Obfuscator.CLI/Program.cs b/src/Dax.Vpax.Obfuscator.CLI/Program.cs index d8e704c..be0e121 100644 --- a/src/Dax.Vpax.Obfuscator.CLI/Program.cs +++ b/src/Dax.Vpax.Obfuscator.CLI/Program.cs @@ -1,54 +1,117 @@ -using Dax.Vpax.Obfuscator.Common; -using System.CommandLine; +using System.CommandLine; +using Dax.Vpax.Obfuscator.Common; namespace Dax.Vpax.Obfuscator.CLI; internal class Program { - public static int Main(string[] args) + public static int Main(string[] args) => BuildCommand().Invoke(args); + + private static RootCommand BuildCommand() { - var command = BuildCommand(); - return command.Invoke(args); + var allowOverwriteOption = new Option(name: "--allow-overwrite") + { + Description = "Allow output files to be overwritten. If not provided, the command will fail if an output file already exists.", + IsRequired = false, + }; + var command = new RootCommand("VertiPaq-Analyzer obfuscator CLI"); + command.AddGlobalOption(allowOverwriteOption); + command.AddCommand(BuildCommandObfuscate(allowOverwriteOption)); + command.AddCommand(BuildCommandDeobfuscate(allowOverwriteOption)); + return command; } - private static RootCommand BuildCommand() + private static Command BuildCommandObfuscate(Option allowOverwriteOption) { - var fileOption = new Option("--file") + var vpaxOption = new Option(name: "--vpax") { - Description = "The full path to the VPAX file to obfuscate.", + Description = "Path to the unobfuscated VPAX file.", IsRequired = true, }; - fileOption.AddAlias("-f"); - - var dictionaryOption = new Option("--dictionary") + var dictionaryOption = new Option(name: "--dictionary") { - Description = "The full path to the JSON dictionary file to use for obfuscation.", + Description = "Path to the dictionary file to be used for incremental obfuscation. If not provided, a new dictionary will be created.", IsRequired = false, }; - dictionaryOption.AddAlias("-d"); - - var rootCommand = new RootCommand("VertiPaq-Analyzer obfuscator."); - rootCommand.AddOption(fileOption); - rootCommand.AddOption(dictionaryOption); - rootCommand.SetHandler(Obfuscate, fileOption, dictionaryOption); + var outputVpaxOption = new Option(name: "--output-vpax") + { + Description = "Path to write the obfuscated VPAX file.", + IsRequired = true, + }; + var outputDictionaryOption = new Option(name: "--output-dictionary") + { + Description = "Path to write the obfuscation dictionary file.", + IsRequired = true, + }; - return rootCommand; + var command = new Command("obfuscate", "Obfuscate the DaxModel.json file and delete all other contents from a VPAX file."); + command.AddOption(vpaxOption); + command.AddOption(dictionaryOption); + command.AddOption(outputVpaxOption); + command.AddOption(outputDictionaryOption); + command.SetHandler(Obfuscate, vpaxOption, dictionaryOption, outputVpaxOption, outputDictionaryOption, allowOverwriteOption); + return command; } - private static void Obfuscate(FileInfo vpaxFile, FileInfo dictionaryFile) + private static Command BuildCommandDeobfuscate(Option allowOverwriteOption) { - var buffer = File.ReadAllBytes(vpaxFile.FullName); - using var stream = new MemoryStream(); - stream.Write(buffer, 0, buffer.Length); + var vpaxOption = new Option(name: "--vpax") + { + Description = "Path to the obfuscated VPAX file.", + IsRequired = true, + }; + var dictionaryOption = new Option(name: "--dictionary") + { + Description = "Path to the dictionary file.", + IsRequired = true, + }; + var outputVpaxOption = new Option(name: "--output-vpax") + { + Description = "Path to write the deobfuscated VPAX file.", + IsRequired = true, + }; + var command = new Command("deobfuscate", "Deobfuscate the DaxModel.json file from an obfuscated VPAX file using the provided dictionary."); + command.AddOption(vpaxOption); + command.AddOption(dictionaryOption); + command.AddOption(outputVpaxOption); + command.SetHandler(Deobfuscate, vpaxOption, dictionaryOption, outputVpaxOption, allowOverwriteOption); + return command; + } + private static void Obfuscate(FileInfo vpaxFile, FileInfo? dictionaryOption, FileInfo outputVpaxFile, FileInfo outputDictionaryFile, bool allowOverwrite) + { + using var stream = Read(vpaxFile); var obfuscator = new VpaxObfuscator(); - var dictionary = dictionaryFile != null - ? obfuscator.Obfuscate(stream, ObfuscationDictionary.ReadFrom(dictionaryFile.FullName)) + var outputDictionary = dictionaryOption is not null + ? obfuscator.Obfuscate(stream, dictionary: ObfuscationDictionary.ReadFrom(dictionaryOption.FullName)) : obfuscator.Obfuscate(stream); - var path = Path.Combine(vpaxFile.DirectoryName!, Path.ChangeExtension(vpaxFile.Name, ".dictionary.json")); - dictionary.WriteTo(path, overwrite: false, indented: true); + outputDictionary.WriteTo(outputDictionaryFile.FullName, overwrite: allowOverwrite, indented: true); + Write(stream, outputVpaxFile, allowOverwrite); + } + + private static void Deobfuscate(FileInfo vpaxFile, FileInfo dictionaryFile, FileInfo outputVpaxFile, bool allowOverwrite) + { + using var stream = Read(vpaxFile); + var obfuscator = new VpaxObfuscator(); - File.WriteAllBytes(vpaxFile.FullName, stream.ToArray()); + obfuscator.Deobfuscate(stream, dictionary: ObfuscationDictionary.ReadFrom(dictionaryFile.FullName)); + Write(stream, outputVpaxFile, allowOverwrite); + } + + private static MemoryStream Read(FileInfo file) + { + var buffer = File.ReadAllBytes(file.FullName); + var stream = new MemoryStream(); + stream.Write(buffer, 0, buffer.Length); + return stream; + } + + private static void Write(MemoryStream stream, FileInfo file, bool allowOverwrite) + { + var bytes = stream.ToArray(); + var mode = allowOverwrite ? FileMode.Create : FileMode.CreateNew; + using var fileStream = new FileStream(file.FullName, mode, FileAccess.Write, FileShare.Read); + fileStream.Write(bytes, 0, bytes.Length); } }