/// <summary> /// Print out information about whether or not a devirtualization attempt was /// successful, and if not why not. /// </summary> /// <param name="options">MonoOptions set from passed command-line arguments</param> /// <param name="module">EazModule</param> /// <param name="attempt">Data about the devirtualization attempt</param> static void PrintAttemptSuccess(MonoOptions options, EazModule module, DevirtualizeAttempt attempt) { var reader = attempt.Reader; if (attempt.Successful) Console.WriteLine("--> Devirtualizable"); else if (attempt.WasInstructionUnknown) { var matches = module.VirtualInstructions .Where((instr) => { return instr.VirtualCode == reader.LastVirtualOpCode; }) .ToArray(); if (matches.Length > 0) { VirtualOpCode v = matches[0]; Console.WriteLine("--> Not yet devirtualizable (contains unknown virtual instruction)"); Console.WriteLine("-----> Virtual OpCode = {0} @ [{1}] (0x{2:X8})", reader.LastVirtualOpCode, reader.CurrentInstructionOffset, reader.CurrentVirtualOffset); Console.WriteLine("-----> Delegate method: {0} (MDToken = 0x{1:X8})", v.DelegateMethod.FullName, v.DelegateMethod.MDToken.Raw); } else { Console.WriteLine("--> Not yet devirtualizable (contains unexpected virtual instruction @ [{0}] (0x{1:X8}))", reader.CurrentInstructionOffset, reader.CurrentVirtualOffset); } } else Console.WriteLine("--> Not yet devirtualizable (threw exception)"); }
/// <summary> /// Print out information about whether or not a devirtualization attempt was /// successful, and if not why not. /// </summary> /// <param name="options">MonoOptions set from passed command-line arguments</param> /// <param name="module">EazModule</param> /// <param name="attempt">Data about the devirtualization attempt</param> static void PrintAttemptSuccess(MonoOptions options, EazModule module, DevirtualizeAttempt attempt) { var reader = attempt.Reader; if (attempt.Successful) { Console.WriteLine("--> Devirtualizable"); } else if (attempt.WasInstructionUnknown) { var matches = module.VirtualInstructions .Where((instr) => { return(instr.VirtualCode == reader.LastVirtualOpCode); }) .ToArray(); if (matches.Length > 0) { VirtualOpCode v = matches[0]; Console.WriteLine("--> Not yet devirtualizable (contains unknown virtual instruction)"); Console.WriteLine("-----> Virtual OpCode = {0} @ [{1}] (0x{2:X8})", reader.LastVirtualOpCode, reader.CurrentInstructionOffset, reader.CurrentVirtualOffset); Console.WriteLine("-----> Delegate method: {0} (MDToken = 0x{1:X8})", v.DelegateMethod.FullName, v.DelegateMethod.MDToken.Raw); } else { Console.WriteLine("--> Not yet devirtualizable (contains unexpected virtual instruction @ [{0}] (0x{1:X8}))", reader.CurrentInstructionOffset, reader.CurrentVirtualOffset); } } else { Console.WriteLine("--> Not yet devirtualizable (threw exception)"); } }
/// <summary> /// Perform "methods" verb. /// </summary> /// <param name="options">Options</param> static void DoMethods(MonoOptions options) { ILogger logger = GetLogger(options); EazModule module; if (!TryLoadModule(options.AssemblyPath, logger, out module)) { return; } Devirtualizer devirtualizer = new Devirtualizer(module, options.MethodFixers, logger); var results = devirtualizer.Devirtualize((attempt) => { PrintAttempt(options, module, attempt); }); if (results.Empty) { Console.WriteLine("No virtualized methods found"); return; } Console.WriteLine("{0}/{1} method stubs devirtualizable", results.DevirtualizedCount, results.MethodCount); }
/// <summary> /// Perform "generate" verb. /// </summary> /// <param name="options">Options</param> static void DoGenerate(MonoOptions options) { var generator = new VirtualizableAssemblyGenerator(); String instructionSet = "all"; if (options.InstructionSet != null && options.InstructionSet.Length > 0) { instructionSet = options.InstructionSet.ToLower(); } String[] sets; if (instructionSet.Contains(',')) { sets = instructionSet.Split(','); } else { sets = new String[] { instructionSet } }; Boolean all = sets.Contains("all"); if (sets.Contains("calli") || all) { generator.AddCalliMethod(); } if (sets.Contains("conv") || all) { generator.AddConvMethod(); } if (sets.Contains("ind") || all) { generator.AddIndMethod(); } if (sets.Contains("static-field") || all) { generator.AddStaticFieldMethod(); } if (!generator.HasMethod) { Console.WriteLine("Unknown set(s): {0}", instructionSet); return; } var assembly = generator.Generate(); String filepath = options.OutputPath; if (filepath == null) { filepath = "eazdevirt-test.exe"; } Console.WriteLine("Saving test assembly {0}", filepath); assembly.Write(filepath); } }
/// <summary> /// Perform "get-key" verb. /// </summary> /// <param name="options">Options</param> static void DoGetKey(MonoOptions options) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) return; MethodStub method = module.FindFirstVirtualizedMethod(); Console.WriteLine("Key: {0}", method.ResourceCryptoKey); }
/// <summary> /// Perform "get-key" verb. /// </summary> /// <param name="options">Options</param> static void DoGetKey(MonoOptions options) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) { return; } MethodStub method = module.FindFirstVirtualizedMethod(); Console.WriteLine("Key: {0}", method.ResourceCryptoKey); }
/// <summary> /// Perform "resource" verb. /// </summary> /// <param name="options">Options</param> static void DoResource(MonoOptions options) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) { return; } // If no action set, set the default action (extract) //if (!options.Extract) // options.Extract = true; MethodStub method = module.FindFirstVirtualizedMethod(); if (method != null) { if (true /* options.Extract */) { String outputPath = options.OutputPath; if (outputPath == null || outputPath.Equals("")) { outputPath = method.ResourceStringId; } FileMode fileMode = FileMode.CreateNew; if (options.OverwriteExisting) { fileMode = FileMode.Create; } using (Stream resourceStream = module.GetResourceStream(options.KeepEncrypted)) { try { using (FileStream fileStream = new FileStream(outputPath, fileMode, FileAccess.Write)) { resourceStream.CopyTo(fileStream); } } catch (IOException e) { Console.Write(e); } } Console.WriteLine("Extracted {0} resource to {1}", options.KeepEncrypted ? "encrypted" : "decrypted", outputPath); } } }
/// <summary> /// Perform "devirtualize" verb. /// </summary> /// <param name="options">Options</param> static void DoDevirtualize(MonoOptions options) { ILogger logger = GetLogger(options); EazModule module; if (!TryLoadModule(options.AssemblyPath, logger, out module)) { return; } // Setup devirtualize options var opts = DevirtualizeOptions.Nothing; if (options.InjectAttributes) { opts |= DevirtualizeOptions.InjectAttributes; } Devirtualizer devirtualizer = new Devirtualizer(module, opts, options.MethodFixers, logger); var results = devirtualizer.Devirtualize((attempt) => { if (attempt.Successful) { PrintAttempt(options, module, attempt); } }); if (results.Empty) { Console.WriteLine("No virtualized methods found"); return; } else if (!options.Verbose) { Console.WriteLine(); } Console.WriteLine("Devirtualized {0}/{1} methods", results.DevirtualizedCount, results.MethodCount); // Only save if at least one method devirtualized if (results.DevirtualizedCount > 0) { String outputPath = options.OutputPath ?? GetDevirtualizedModulePath(options.AssemblyPath); Console.WriteLine("Saving {0}", outputPath); module.Write(outputPath, options.NoThrow); } }
static ILogger GetLogger(MonoOptions options) { LoggerEvent e = LoggerEvent.Info; if (options.VerboseLevel == 1) { e = LoggerEvent.Verbose; } else if (options.VerboseLevel > 1) { e = LoggerEvent.VeryVerbose; } return(new ConsoleLogger(e)); }
static void PrintHelp(MonoOptions parsed) { Console.WriteLine(GetDescriptorString()); Console.WriteLine(); Console.WriteLine("usage: eazdevirt [-dgikmpr] [options] <assembly>"); Console.WriteLine(); String generatedHelp = parsed.OptionDescriptors; Console.Write(generatedHelp); Console.WriteLine(); Console.WriteLine("examples:"); Console.WriteLine(" eazdevirt -d MyAssembly.exe"); Console.WriteLine(" eazdevirt -r --keep-encrypted MyAssembly.exe"); }
/// <summary> /// Perform "position" verb. /// </summary> /// <param name="options">Options</param> static void DoPosition(MonoOptions options) { IPositionTranslator translator = PositionTranslator.DefaultInstance; Int64 position = 0; if (options.Key.HasValue) { // This doesn't work yet: Command line parser can't parse Nullable? position = translator.ToPosition(options.PositionString, options.Key.Value); } else if (options.AssemblyPath != null) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) { return; } MethodStub method = module.FindFirstVirtualizedMethod(); if (method != null) { try { position = translator.ToPosition(options.PositionString, method.ResourceCryptoKey); } catch (FormatException e) { Console.WriteLine(e.Message); return; } } else { Console.WriteLine("No virtualized methods found in specified assembly"); return; } } else { Console.WriteLine("Provide either the crypto key or assembly from which to extract the crypto key"); return; } Console.WriteLine("{0} => {1:X8}", options.PositionString, position); }
/// <summary> /// Perform "generate" verb. /// </summary> /// <param name="options">Options</param> static void DoGenerate(MonoOptions options) { var generator = new VirtualizableAssemblyGenerator(); String instructionSet = string.IsNullOrEmpty(options.InstructionSet) ? "all" : options.InstructionSet.ToLower(); String[] sets = instructionSet.Contains(',') ? instructionSet.Split(',') : new String[] { instructionSet }; Boolean all = sets.Contains("all"); if (sets.Contains("calli") || all) { generator.AddCalliMethod(); } if (sets.Contains("conv") || all) { generator.AddConvMethod(); } if (sets.Contains("ind") || all) { generator.AddIndMethod(); } if (sets.Contains("static-field") || all) { generator.AddStaticFieldMethod(); } if (!generator.HasMethod) { Console.WriteLine("Unknown set(s): {0}", instructionSet); return; } var assembly = generator.Generate(); String filepath = options.OutputPath ?? "eazdevirt-test.exe"; Console.WriteLine("Saving test assembly {0}", filepath); assembly.Write(filepath); }
/// <summary> /// Perform "position" verb. /// </summary> /// <param name="options">Options</param> static void DoPosition(MonoOptions options) { IPositionTranslator translator = PositionTranslator.DefaultInstance; Int64 position = 0; if (options.Key.HasValue) { // This doesn't work yet: Command line parser can't parse Nullable? position = translator.ToPosition(options.PositionString, options.Key.Value); } else if (options.AssemblyPath != null) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) return; MethodStub method = module.FindFirstVirtualizedMethod(); if (method != null) { try { position = translator.ToPosition(options.PositionString, method.ResourceCryptoKey); } catch (FormatException e) { Console.WriteLine(e.Message); return; } } else { Console.WriteLine("No virtualized methods found in specified assembly"); return; } } else { Console.WriteLine("Provide either the crypto key or assembly from which to extract the crypto key"); return; } Console.WriteLine("{0} => {1:X8}", options.PositionString, position); }
/// <summary> /// Perform "resource" verb. /// </summary> /// <param name="options">Options</param> static void DoResource(MonoOptions options) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) return; // If no action set, set the default action (extract) //if (!options.Extract) // options.Extract = true; MethodStub method = module.FindFirstVirtualizedMethod(); if (method != null) { if (true /* options.Extract */) { String outputPath = options.OutputPath; if (outputPath == null || outputPath.Equals("")) outputPath = method.ResourceStringId; FileMode fileMode = FileMode.CreateNew; if (options.OverwriteExisting) fileMode = FileMode.Create; using (Stream resourceStream = module.GetResourceStream(options.KeepEncrypted)) { try { using (FileStream fileStream = new FileStream(outputPath, fileMode, FileAccess.Write)) { resourceStream.CopyTo(fileStream); } } catch (IOException e) { Console.Write(e); } } Console.WriteLine("Extracted {0} resource to {1}", options.KeepEncrypted ? "encrypted" : "decrypted", outputPath); } } }
/// <summary> /// Perform "position" verb. /// </summary> /// <param name="options">Options</param> static void DoPosition(MonoOptions options) { Int64 position = 0; if (options.AssemblyPath != null) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) { return; } IPositionTranslator translator = module.PositionTranslator; MethodStub method = module.FindFirstVirtualizedMethod(); if (method != null) { try { position = translator.ToPosition(options.PositionString, method.ResourceCryptoKey); } catch (FormatException e) { Console.WriteLine(e.Message); return; } } else { Console.WriteLine("No virtualized methods found in specified assembly"); return; } } else { Console.WriteLine("Provide either the crypto key or assembly from which to extract the crypto key"); return; } Console.WriteLine("{0} => {1:X8}", options.PositionString, position); }
/// <summary> /// Perform "position" verb. /// </summary> /// <param name="options">Options</param> static void DoPosition(MonoOptions options) { Int64 position = 0; if (options.AssemblyPath != null) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) return; IPositionTranslator translator = module.PositionTranslator; MethodStub method = module.FindFirstVirtualizedMethod(); if (method != null) { try { position = translator.ToPosition(options.PositionString, method.ResourceCryptoKey); } catch (FormatException e) { Console.WriteLine(e.Message); return; } } else { Console.WriteLine("No virtualized methods found in specified assembly"); return; } } else { Console.WriteLine("Provide either the crypto key or assembly from which to extract the crypto key"); return; } Console.WriteLine("{0} => {1:X8}", options.PositionString, position); }
/// <summary> /// Perform "generate" verb. /// </summary> /// <param name="options">Options</param> static void DoGenerate(MonoOptions options) { var generator = new VirtualizableAssemblyGenerator(); String instructionSet = "all"; if (options.InstructionSet != null && options.InstructionSet.Length > 0) instructionSet = options.InstructionSet.ToLower(); String[] sets; if (instructionSet.Contains(',')) sets = instructionSet.Split(','); else sets = new String[] { instructionSet }; Boolean all = sets.Contains("all"); if (sets.Contains("calli") || all) generator.AddCalliMethod(); if (sets.Contains("conv") || all) generator.AddConvMethod(); if (sets.Contains("ind") || all) generator.AddIndMethod(); if (sets.Contains("static-field") || all) generator.AddStaticFieldMethod(); if (!generator.HasMethod) { Console.WriteLine("Unknown set(s): {0}", instructionSet); return; } var assembly = generator.Generate(); String filepath = options.OutputPath; if (filepath == null) filepath = "eazdevirt-test.exe"; Console.WriteLine("Saving test assembly {0}", filepath); assembly.Write(filepath); }
/// <summary> /// Perform "methods" verb. /// </summary> /// <param name="options">Options</param> static void DoMethods(MonoOptions options) { ILogger logger = GetLogger(options); EazModule module; if (!TryLoadModule(options.AssemblyPath, logger, out module)) return; Devirtualizer devirtualizer = new Devirtualizer(module, options.MethodFixers, logger); var results = devirtualizer.Devirtualize((attempt) => { PrintAttempt(options, module, attempt); }); if (results.Empty) { Console.WriteLine("No virtualized methods found"); return; } Console.WriteLine("{0}/{1} method stubs devirtualizable", results.DevirtualizedCount, results.MethodCount); }
/// <summary> /// Perform "instructions" verb. /// </summary> /// <param name="options">Options</param> static void DoInstructions(MonoOptions options) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) { return; } MethodStub method = module.FindFirstVirtualizedMethod(); if (method == null) { Console.WriteLine("No methods in assembly seem to be virtualized"); return; } // The virtual-call-method should belong to the main virtualization type TypeDef virtualizationType = method.VirtualCallMethod.DeclaringType; var vInstructions = module.VirtualInstructions; if (vInstructions.Count > 0) { // Get # of identified instructions Int32 identified = 0; foreach (var v in vInstructions) { if (v.IsIdentified) { identified++; } } // Get % of identified instructions as a string String percentIdentified; if (identified == 0) { percentIdentified = "0%"; } else if (identified == vInstructions.Count) { percentIdentified = "100%"; } else { percentIdentified = Math.Floor( (((double)identified) / ((double)vInstructions.Count)) * 100d ) + "%"; } Console.WriteLine("Virtual instruction types found: {0}", vInstructions.Count); Console.WriteLine("{0}/{1} instruction types identified ({2})", identified, vInstructions.Count, percentIdentified); if (!options.Verbose) { Console.WriteLine(); } // If only showing identified instructions, remove all non-identified and sort by name if (options.OnlyIdentified) { vInstructions = new List <VirtualOpCode>(vInstructions .Where((instruction) => { return(instruction.IsIdentified); }) .OrderBy((instruction) => { return(instruction.Name); })); } // If only showing instructions with specific virtual operand types, filter if (options.OperandTypeWhitelist != Int32.MinValue) { vInstructions = new List <VirtualOpCode>(vInstructions .Where((instruction) => { return(options.OperandTypeWhitelist == instruction.VirtualOperandType); })); } foreach (var v in vInstructions) { if (!options.Verbose) // Simple output { if (v.IsIdentified) { Console.WriteLine("Instruction: {0}, Method: {1}", v.Name, v.DelegateMethod.FullName); } else { Console.WriteLine("Instruction: Unknown, Method: {0}", v.DelegateMethod.FullName); } } else // Not-Simple output? { Console.WriteLine(); if (v.IsIdentified) { Console.WriteLine("Instruction: {0}", v.Name); } else { Console.WriteLine("Instruction: Unknown"); } if (v.IsIdentified || !options.OnlyIdentified) { if (v.HasVirtualCode) { Console.WriteLine("--> Virtual OpCode: {0} ({0:X8})", v.VirtualCode); Console.WriteLine("--> Operand type: {0}", v.VirtualOperandType); } { Console.WriteLine("--> Delegate method: {0}", v.DelegateMethod.FullName); } } } } // Print operand information if (options.Operands) { var operandTypeDict = new Dictionary <Int32, Int32>(); foreach (var vInstr in vInstructions) { var type = vInstr.VirtualOperandType; if (operandTypeDict.ContainsKey(type)) { operandTypeDict[type] = (operandTypeDict[type] + 1); } else { operandTypeDict.Add(type, 1); } } Console.WriteLine(); Console.WriteLine("Virtual operand type counts:"); foreach (var kvp in operandTypeDict) { Console.WriteLine(" Operand {0}: {1} occurrence(s)", kvp.Key, kvp.Value); } } } else { Console.WriteLine("No virtual instructions found?"); } }
/// <summary> /// Print out information about a devirtualization attempt. /// </summary> /// <param name="options">MonoOptions set from passed command-line arguments</param> /// <param name="module">EazModule</param> /// <param name="attempt">Data about the devirtualization attempt</param> static void PrintAttempt(MonoOptions options, EazModule module, DevirtualizeAttempt attempt) { var reader = attempt.Reader; var method = attempt.Method; var stub = attempt.VirtualizedMethod; var body = attempt.MethodBody; IList<Local> locals = attempt.Successful ? body.Variables : reader.Locals; IList<ExceptionHandler> handlers = attempt.Successful ? body.ExceptionHandlers : reader.ExceptionHandlers; IList<Instruction> instructions = attempt.Successful ? body.Instructions : reader.Instructions; // Message prefix String prefix; switch(options.Action) { case ProgramAction.Devirtualize: prefix = "Devirtualized"; break; case ProgramAction.Methods: default: prefix = "Found"; break; } Console.WriteLine("{0} {1} (MDToken = 0x{2:X8})", prefix, method.FullName, method.MDToken.Raw); if (options.Action == ProgramAction.Methods || options.Verbose) { Console.WriteLine("--> Position string: {0}", stub.PositionString); Console.WriteLine("--> Position: {0} (0x{0:X8})", stub.Position); Console.WriteLine("--> Resource: {0}", stub.ResourceStringId); Console.WriteLine("--> Crypto key: {0}", stub.ResourceCryptoKey); Console.WriteLine("--> Actual method size: {0} (0x{0:X8})", reader.CodeSize); if (options.Action == ProgramAction.Methods) PrintAttemptSuccess(options, module, attempt); } if (options.Action == ProgramAction.Methods || options.Verbose) { Console.WriteLine(); // Print locals if (locals.Count > 0) { Int32 index = 0; Console.WriteLine("Locals:"); Console.WriteLine("-------"); foreach (var local in locals) Console.WriteLine("local[{0}]: {1}", index++, local.Type.FullName); Console.WriteLine(); } // Print exception handlers if (handlers.Count > 0) { Int32 index = 0; Console.WriteLine("Exception Handlers:"); Console.WriteLine("-------------------"); foreach (var handler in handlers) { if (handler.CatchType != null) Console.WriteLine("handler[{0}]: HandlerType = {1}, CatchType = {2}", index++, handler.HandlerType, handler.CatchType); else Console.WriteLine("handler[{0}]: HandlerType = {1}", index++, handler.HandlerType); Console.WriteLine("--> Try: [{0}, {1}]", handler.TryStart, handler.TryEnd); Console.WriteLine("--> Handler: [{0}, {1}]", handler.HandlerStart, handler.HandlerEnd); Console.WriteLine("--> Filter: {0}", handler.FilterStart); } Console.WriteLine(); } // Print instructions if (instructions != null && instructions.Count > 0) { Console.WriteLine("Instructions:"); Console.WriteLine("-------------"); foreach (var instr in instructions) Console.WriteLine(instr); Console.WriteLine(); } // Print out exception, if any if (!attempt.Successful && !attempt.WasInstructionUnknown) { Console.Write(attempt.Exception); Console.WriteLine(); Console.WriteLine(); } } if (!(options.Action == ProgramAction.Devirtualize && !options.Verbose)) Console.WriteLine(); }
/// <summary> /// Print out information about a devirtualization attempt. /// </summary> /// <param name="options">MonoOptions set from passed command-line arguments</param> /// <param name="module">EazModule</param> /// <param name="attempt">Data about the devirtualization attempt</param> static void PrintAttempt(MonoOptions options, EazModule module, DevirtualizeAttempt attempt) { var reader = attempt.Reader; var method = attempt.Method; var stub = attempt.VirtualizedMethod; var body = attempt.MethodBody; IList <Local> locals = attempt.Successful ? body.Variables : reader.Locals; IList <ExceptionHandler> handlers = attempt.Successful ? body.ExceptionHandlers : reader.ExceptionHandlers; IList <Instruction> instructions = attempt.Successful ? body.Instructions : reader.Instructions; // Message prefix String prefix; switch (options.Action) { case ProgramAction.Devirtualize: prefix = "Devirtualized"; break; case ProgramAction.Methods: default: prefix = "Found"; break; } Console.WriteLine("{0} {1} (MDToken = 0x{2:X8})", prefix, method.FullName, method.MDToken.Raw); if (options.Action == ProgramAction.Methods || options.Verbose) { Console.WriteLine("--> Position string: {0}", stub.PositionString); Console.WriteLine("--> Position: {0} (0x{0:X8})", stub.Position); Console.WriteLine("--> Resource: {0}", stub.ResourceStringId); Console.WriteLine("--> Crypto key: {0}", stub.ResourceCryptoKey); Console.WriteLine("--> Actual method size: {0} (0x{0:X8})", reader.CodeSize); if (options.Action == ProgramAction.Methods) { PrintAttemptSuccess(options, module, attempt); } } if (options.Action == ProgramAction.Methods || options.Verbose) { Console.WriteLine(); // Print locals if (locals.Count > 0) { Int32 index = 0; Console.WriteLine("Locals:"); Console.WriteLine("-------"); foreach (var local in locals) { Console.WriteLine("local[{0}]: {1}", index++, local.Type.FullName); } Console.WriteLine(); } // Print exception handlers if (handlers.Count > 0) { Int32 index = 0; Console.WriteLine("Exception Handlers:"); Console.WriteLine("-------------------"); foreach (var handler in handlers) { if (handler.CatchType != null) { Console.WriteLine("handler[{0}]: HandlerType = {1}, CatchType = {2}", index++, handler.HandlerType, handler.CatchType); } else { Console.WriteLine("handler[{0}]: HandlerType = {1}", index++, handler.HandlerType); } Console.WriteLine("--> Try: [{0}, {1}]", handler.TryStart, handler.TryEnd); Console.WriteLine("--> Handler: [{0}, {1}]", handler.HandlerStart, handler.HandlerEnd); Console.WriteLine("--> Filter: {0}", handler.FilterStart); } Console.WriteLine(); } // Print instructions if (instructions != null && instructions.Count > 0) { Console.WriteLine("Instructions:"); Console.WriteLine("-------------"); foreach (var instr in instructions) { Console.WriteLine(instr); } Console.WriteLine(); } // Print out exception, if any if (!attempt.Successful && !attempt.WasInstructionUnknown) { Console.Write(attempt.Exception); Console.WriteLine(); Console.WriteLine(); } } if (!(options.Action == ProgramAction.Devirtualize && !options.Verbose)) { Console.WriteLine(); } }
/// <summary> /// Perform "devirtualize" verb. /// </summary> /// <param name="options">Options</param> static void DoDevirtualize(MonoOptions options) { ILogger logger = GetLogger(options); EazModule module; if (!TryLoadModule(options.AssemblyPath, logger, out module)) return; // Setup devirtualize options var opts = DevirtualizeOptions.Nothing; if (options.InjectAttributes) opts |= DevirtualizeOptions.InjectAttributes; Devirtualizer devirtualizer = new Devirtualizer(module, opts, options.MethodFixers, logger); var results = devirtualizer.Devirtualize((attempt) => { if (attempt.Successful) PrintAttempt(options, module, attempt); }); if (results.Empty) { Console.WriteLine("No virtualized methods found"); return; } else if (!options.Verbose) Console.WriteLine(); Console.WriteLine("Devirtualized {0}/{1} methods", results.DevirtualizedCount, results.MethodCount); // Only save if at least one method devirtualized if (results.DevirtualizedCount > 0) { String outputPath = options.OutputPath ?? GetDevirtualizedModulePath(options.AssemblyPath); Console.WriteLine("Saving {0}", outputPath); module.Write(outputPath, options.NoThrow); } }
/// <summary> /// Perform "instructions" verb. /// </summary> /// <param name="options">Options</param> static void DoInstructions(MonoOptions options) { EazModule module; if (!TryLoadModule(options.AssemblyPath, out module)) return; MethodStub method = module.FindFirstVirtualizedMethod(); if (method == null) { Console.WriteLine("No methods in assembly seem to be virtualized"); return; } // The virtual-call-method should belong to the main virtualization type TypeDef virtualizationType = method.VirtualCallMethod.DeclaringType; var vInstructions = module.VirtualInstructions; if (vInstructions.Count > 0) { // Get # of identified instructions Int32 identified = 0; foreach (var v in vInstructions) if (v.IsIdentified) identified++; // Get % of identified instructions as a string String percentIdentified; if (identified == 0) percentIdentified = "0%"; else if (identified == vInstructions.Count) percentIdentified = "100%"; else percentIdentified = Math.Floor( (((double)identified) / ((double)vInstructions.Count)) * 100d ) + "%"; Console.WriteLine("Virtual instruction types found: {0}", vInstructions.Count); Console.WriteLine("{0}/{1} instruction types identified ({2})", identified, vInstructions.Count, percentIdentified); if (!options.Verbose) Console.WriteLine(); // If only showing identified instructions, remove all non-identified and sort by name if (options.OnlyIdentified) { vInstructions = new List<VirtualOpCode>(vInstructions .Where((instruction) => { return instruction.IsIdentified; }) .OrderBy((instruction) => { return instruction.Name; })); } // If only showing instructions with specific virtual operand types, filter if (options.OperandTypeWhitelist != Int32.MinValue) { vInstructions = new List<VirtualOpCode>(vInstructions .Where((instruction) => { return options.OperandTypeWhitelist == instruction.VirtualOperandType; })); } foreach (var v in vInstructions) { if (!options.Verbose) // Simple output { if (v.IsIdentified) Console.WriteLine("Instruction: {0}, Method: {1}", v.Name, v.DelegateMethod.FullName); else Console.WriteLine("Instruction: Unknown, Method: {0}", v.DelegateMethod.FullName); } else // Not-Simple output? { Console.WriteLine(); if (v.IsIdentified) Console.WriteLine("Instruction: {0}", v.Name); else Console.WriteLine("Instruction: Unknown"); if (v.IsIdentified || !options.OnlyIdentified) { if (v.HasVirtualCode) { Console.WriteLine("--> Virtual OpCode: {0} ({0:X8})", v.VirtualCode); Console.WriteLine("--> Operand type: {0}", v.VirtualOperandType); } { Console.WriteLine("--> Delegate method: {0}", v.DelegateMethod.FullName); } } } } // Print operand information if (options.Operands) { var operandTypeDict = new Dictionary<Int32, Int32>(); foreach (var vInstr in vInstructions) { var type = vInstr.VirtualOperandType; if (operandTypeDict.ContainsKey(type)) operandTypeDict[type] = (operandTypeDict[type] + 1); else operandTypeDict.Add(type, 1); } Console.WriteLine(); Console.WriteLine("Virtual operand type counts:"); foreach (var kvp in operandTypeDict) Console.WriteLine(" Operand {0}: {1} occurrence(s)", kvp.Key, kvp.Value); } } else Console.WriteLine("No virtual instructions found?"); }
/// <summary> /// Parse a MonoOptions from passed arguments. /// </summary> /// <param name="args">Arguments passed to program</param> /// <returns>MonoOptions</returns> static MonoOptions Parse(String[] args) { MonoOptions options = new MonoOptions(); OptionSet optionSet = new OptionSet() { // Program action options { "d|devirtualize", "attempt to devirtualize methods in a protected assembly", v => options.Action = ProgramAction.Devirtualize }, { "g|generate", "generate a test executable to be protected and analysed", v => options.Action = ProgramAction.Generate }, { "i|instructions", "print virtual opcode information extracted from a protected assembly", v => options.Action = ProgramAction.Instructions }, { "k|get-key", "extract the integer crypto key from a protected assembly", v => options.Action = ProgramAction.GetKey }, { "m|methods", "print virtualized method + method stub information extracted from a protected assembly", v => options.Action = ProgramAction.Methods }, { "p|position", "translate a position string into its Int64 representation given either an integer " + "crypto key or a protected assembly", v => options.Action = ProgramAction.Position }, { "r|resource", "extract the embedded resource from a protected assembly", v => options.Action = ProgramAction.Resource }, { "N|no-throw", "don't throw when writing a module", v => options.NoThrow = true }, // `devirtualize` options { "j|inject", "inject attributes", v => options.InjectAttributes = true }, { "F=|fixers=", "fixers to use", v => options.FixersString = v }, // `generate` options { "I=|instruction-set=", "name of \"instruction sets\" to generate", v => options.InstructionSet = v }, // `instructions` options { "only-identified", "only show identified opcodes", v => options.OnlyIdentified = true }, { "operands", "print info about operand types", v => options.Operands = true }, { "operand-type=", "operand type whitelist", (Int32 v) => options.OperandTypeWhitelist = v }, // `position` options { "K=|key=", "integer crypto key used to translate a position string", (Int32 v) => options.Key = v }, { "P=|position-string=", "position string to translate", v => options.PositionString = v }, // `resource` options { "o=|destination=", "destination file (type of file depends on program action)", v => options.OutputPath = v }, { "f|force", "overwrite destination file if it exists", v => options.OverwriteExisting = true }, { "x|extract", v => options.ExtractResource = true }, { "D|keep-encrypted", "don't decrypt the resource file when extracting", v => options.KeepEncrypted = true }, // Other options { "L|no-logo", "don't show the ascii logo", v => options.NoLogo = true }, { "h|?|help", "show help/usage info and exit", v => options.Help = true }, { "v|verbose", "more output", v => options.VerboseLevel++ } }; options.Extra = optionSet.Parse(args); options.OptionSet = optionSet; if (options.Extra.Count > 0) { options.AssemblyPath = options.Extra[0]; } return(options); }
/// <summary> /// Parse a MonoOptions from passed arguments. /// </summary> /// <param name="args">Arguments passed to program</param> /// <returns>MonoOptions</returns> static MonoOptions Parse(String[] args) { MonoOptions options = new MonoOptions(); OptionSet optionSet = new OptionSet() { // Program action options { "d|devirtualize", "attempt to devirtualize methods in a protected assembly", v => options.Action = ProgramAction.Devirtualize }, { "g|generate", "generate a test executable to be protected and analysed", v => options.Action = ProgramAction.Generate }, { "i|instructions", "print virtual opcode information extracted from a protected assembly", v => options.Action = ProgramAction.Instructions }, { "k|get-key", "extract the integer crypto key from a protected assembly", v => options.Action = ProgramAction.GetKey }, { "m|methods", "print virtualized method + method stub information extracted from a protected assembly", v => options.Action = ProgramAction.Methods }, { "p|position", "translate a position string into its Int64 representation given either an integer " + "crypto key or a protected assembly", v => options.Action = ProgramAction.Position }, { "r|resource", "extract the embedded resource from a protected assembly", v => options.Action = ProgramAction.Resource }, { "N|no-throw", "don't throw when writing a module", v => options.NoThrow = true }, // `devirtualize` options { "j|inject", "inject attributes", v => options.InjectAttributes = true }, { "F=|fixers=", "fixers to use", v => options.FixersString = v }, // `generate` options { "I=|instruction-set=", "name of \"instruction sets\" to generate", v => options.InstructionSet = v }, // `instructions` options { "only-identified", "only show identified opcodes", v => options.OnlyIdentified = true }, { "operands", "print info about operand types", v => options.Operands = true }, { "operand-type=", "operand type whitelist", (Int32 v) => options.OperandTypeWhitelist = v }, // `position` options { "K=|key=", "integer crypto key used to translate a position string", (Int32 v) => options.Key = v }, { "P=|position-string=", "position string to translate", v => options.PositionString = v }, // `resource` options { "o=|destination=", "destination file (type of file depends on program action)", v => options.OutputPath = v }, { "f|force", "overwrite destination file if it exists", v => options.OverwriteExisting = true }, { "x|extract", v => options.ExtractResource = true }, { "D|keep-encrypted", "don't decrypt the resource file when extracting", v => options.KeepEncrypted = true }, // Other options { "L|no-logo", "don't show the ascii logo", v => options.NoLogo = true }, { "h|?|help", "show help/usage info and exit", v => options.Help = true }, { "v|verbose", "more output", v => options.VerboseLevel++ } }; options.Extra = optionSet.Parse(args); options.OptionSet = optionSet; if (options.Extra.Count > 0) options.AssemblyPath = options.Extra[0]; return options; }
static ILogger GetLogger(MonoOptions options) { LoggerEvent e = LoggerEvent.Info; if (options.VerboseLevel == 1) e = LoggerEvent.Verbose; else if (options.VerboseLevel > 1) e = LoggerEvent.VeryVerbose; return new ConsoleLogger(e); }