private static void SerializeImageToDisk( DevirtualisationOptions options, DevirtualisationContext context, ModuleDefinition module, string fileName) { var imageBuilder = new ManagedPEImageBuilder(); var result = imageBuilder.CreateImage(module); if (result.DiagnosticBag.IsFatal) { throw new AggregateException(result.DiagnosticBag.Exceptions); } foreach (var error in result.DiagnosticBag.Exceptions) { context.Logger.Error(Tag, error.Message); } var fileBuilder = new ManagedPEFileBuilder(); var file = fileBuilder.CreateFile(result.ConstructedImage); file.Write(Path.Combine(options.OutputOptions.RootDirectory, fileName)); }
private ModuleDefinition ResolveRuntimeModule(DevirtualisationOptions options, ModuleDefinition targetModule) { ModuleDefinition runtimeModule = null; if (options.AutoDetectRuntimeFile) { Logger.Debug(Tag, "Attempting to autodetect location of the runtime library..."); var runtimeAssemblies = targetModule.AssemblyReferences .Where(r => RuntimeAssemblyNames.Contains(r.Name)) .ToArray(); switch (runtimeAssemblies.Length) { case 0: // No assembly references detected, default to embedded. Logger.Debug(Tag, "No references found to a runtime library."); options.RuntimeFile = options.InputFile; break; case 1: // A single assembly reference with a known KoiVM runtime library name was found. Logger.Debug(Tag, $"Reference to runtime library detected ({runtimeAssemblies[0].Name})."); options.RuntimeFile = Path.Combine(Path.GetDirectoryName(options.InputFile), runtimeAssemblies[0].Name + ".dll"); break; default: // Multiple assembly references with a known KoiVM runtime library name were found. // Report to the user that they have to choose which one to use. throw new DevirtualisationException( "Multiple runtime assembly reference detected. " + "Please specify the location of the runtime assembly to use in the devirtualizer options."); } } if (options.RuntimeIsEmbedded) { // Runtime is embedded into the assembly, so they share the same metadata image. Logger.Log(Tag, "Runtime is embedded in the target assembly."); runtimeModule = targetModule; } else if (options.RuntimeFile != null) { // Resolve runtime library. Logger.Log(Tag, $"Opening external runtime library located at {options.RuntimeFile}..."); string runtimePath = Path.IsPathRooted(options.RuntimeFile) ? options.RuntimeFile : Path.Combine(Path.GetDirectoryName(options.InputFile), options.RuntimeFile); runtimeModule = ModuleDefinition.FromFile(runtimePath); } else { throw new DevirtualisationException( "Failed to resolve runtime library. This could be a bug in the initial scanning phase. " + "Try specifying the location of the runtime assembly in the devirtualizer options."); } return(runtimeModule); }
private DevirtualisationContext CreateDevirtualisationContext(DevirtualisationOptions options) { // Open target file. Logger.Log(Tag, $"Opening target file {options.InputFile}..."); var assembly = WindowsAssembly.FromFile(options.InputFile); var header = assembly.NetDirectory?.MetadataHeader; if (header == null) { throw new BadImageFormatException("Assembly does not contain a valid .NET header."); } // Hook into md stream parser. var parser = new KoiVmAwareStreamParser(options.KoiStreamName, Logger); if (options.OverrideKoiStreamData) { string path = Path.IsPathRooted(options.KoiStreamDataFile) ? options.KoiStreamDataFile : Path.Combine(Path.GetDirectoryName(options.InputFile), options.KoiStreamDataFile); Logger.Log(Tag, $"Opening external Koi stream data file {path}..."); parser.ReplacementData = File.ReadAllBytes(path); var streamHeader = header.StreamHeaders.FirstOrDefault(h => h.Name == options.KoiStreamName); if (streamHeader == null) { streamHeader = new MetadataStreamHeader(options.KoiStreamName) { Stream = KoiStream.FromReadingContext( new ReadingContext() { Reader = new MemoryStreamReader(parser.ReplacementData) }, Logger) }; header.StreamHeaders.Add(streamHeader); } } header.StreamParser = parser; // Ignore invalid / encrypted method bodies when specified. if (options.IgnoreInvalidMethodBodies) { var table = header.GetStream <TableStream>().GetTable <MethodDefinitionTable>(); table.MethodBodyReader = new DefaultMethodBodyReader { ThrowOnInvalidMethodBody = false }; } // Lock image and set custom md resolver. var image = header.LockMetadata(); image.MetadataResolver = new DefaultMetadataResolver( new DefaultNetAssemblyResolver(Path.GetDirectoryName(options.InputFile))); var runtimeImage = ResolveRuntimeImage(options, image); return(new DevirtualisationContext(options, image, runtimeImage, Logger)); }
public void Devirtualise(DevirtualisationOptions options) { Logger.Log(Tag, "Started devirtualisation."); // Create output directory. options.OutputOptions.EnsureDirectoriesExist(); var context = CreateDevirtualisationContext(options); // Run pipeline. RunPipeline(context); // Unlock images. Logger.Log(Tag, $"Commiting changes to metadata streams..."); context.TargetImage.Header.UnlockMetadata(); bool rebuildRuntimeImage = options.RenameSymbols && !options.RuntimeIsEmbedded; if (rebuildRuntimeImage) { context.RuntimeImage.Header.UnlockMetadata(); } RemoveFinalTraces(options, context); // Rebuild. Rebuild(options, context, rebuildRuntimeImage); Logger.Log(Tag, $"Finished. All fish were caught and served!"); }
private DevirtualisationContext CreateDevirtualisationContext(DevirtualisationOptions options) { string workingDirectory = Path.GetDirectoryName(options.InputFile); // Open target PE. Logger.Log(Tag, $"Opening target file {options.InputFile}..."); var peFile = PEFile.FromFile(options.InputFile); // Create #Koi stream aware metadata reader. var streamReader = options.OverrideKoiStreamData ? new DefaultMetadataStreamReader() : (IMetadataStreamReader) new KoiVmAwareStreamReader(options.KoiStreamName, Logger); var peImage = PEImage.FromFile(peFile, new PEReaderParameters { MetadataStreamReader = streamReader }); var metadata = peImage.DotNetDirectory?.Metadata; if (metadata is null) { throw new BadImageFormatException("Assembly does not contain a valid .NET header."); } // If custom koi stream data was provided, inject it. KoiStream koiStream; if (!options.OverrideKoiStreamData) { koiStream = metadata.GetStream <KoiStream>() ?? throw new DevirtualisationException( "Koi stream was not found in the target PE. This could be because the input file is " + "not protected with KoiVM, or the metadata stream uses a name that is different " + "from the one specified in the input parameters."); } else { string path = Path.IsPathRooted(options.KoiStreamDataFile) ? options.KoiStreamDataFile : Path.Combine(workingDirectory, options.KoiStreamDataFile); Logger.Log(Tag, $"Opening external Koi stream data file {path}..."); var contents = File.ReadAllBytes(path); // Replace original koi stream if it existed. koiStream = new KoiStream(options.KoiStreamName, new DataSegment(contents), Logger); } // Ignore invalid / encrypted method bodies when specified. var moduleReadParameters = new ModuleReaderParameters(workingDirectory, options.IgnoreInvalidMD ? (IErrorListener)ThrowErrorListener.Instance : EmptyErrorListener.Instance); var module = ModuleDefinition.FromImage(peImage, moduleReadParameters); var runtimeModule = ResolveRuntimeModule(options, module); koiStream.ResolutionContext = module; return(new DevirtualisationContext(options, module, runtimeModule, koiStream, Logger)); }
public DevirtualisationContext(DevirtualisationOptions options, MetadataImage targetImage, MetadataImage runtimeImage, ILogger logger) { Options = options ?? throw new ArgumentNullException(nameof(options)); TargetImage = targetImage ?? throw new ArgumentNullException(nameof(targetImage)); RuntimeImage = runtimeImage ?? throw new ArgumentNullException(nameof(runtimeImage)); Logger = logger ?? throw new ArgumentNullException(nameof(logger)); ReferenceImporter = new ReferenceImporter(targetImage); }
public DevirtualisationContext(DevirtualisationOptions options, ModuleDefinition targetModule, ModuleDefinition runtimeModule, KoiStream koiStream, ILogger logger) { Options = options ?? throw new ArgumentNullException(nameof(options)); TargetModule = targetModule ?? throw new ArgumentNullException(nameof(targetModule)); RuntimeModule = runtimeModule ?? throw new ArgumentNullException(nameof(runtimeModule)); KoiStream = koiStream ?? throw new ArgumentNullException(nameof(koiStream)); Logger = logger ?? throw new ArgumentNullException(nameof(logger)); ReferenceImporter = new ReferenceImporter(targetModule); }
private void Rebuild(DevirtualisationOptions options, DevirtualisationContext context) { bool rebuildRuntimeImage = options.RenameSymbols && !options.RuntimeIsEmbedded; Logger.Log(Tag, $"Reassembling image..."); SerializeImageToDisk(options, context, context.TargetModule, Path.GetFileName(options.InputFile)); if (rebuildRuntimeImage) { Logger.Log(Tag, $"Reassembling runtime image..."); SerializeImageToDisk(options, context, context.RuntimeModule, Path.GetFileName(context.Options.RuntimeFile)); } }
private void Rebuild(DevirtualisationOptions options, DevirtualisationContext context, bool rebuildRuntimeImage) { Logger.Log(Tag, $"Reassembling file..."); context.TargetAssembly.Write( Path.Combine(options.OutputOptions.RootDirectory, Path.GetFileName(options.InputFile)), new CompactNetAssemblyBuilder(context.TargetAssembly)); if (rebuildRuntimeImage) { context.RuntimeAssembly.Write( Path.Combine(options.OutputOptions.RootDirectory, Path.GetFileName(context.Options.RuntimeFile)), new CompactNetAssemblyBuilder(context.RuntimeAssembly)); } }
private void RemoveFinalTraces(DevirtualisationOptions options, DevirtualisationContext context) { // Remove #koi stream. if (!context.AllVirtualisedMethodsRecompiled) { var header = context.TargetImage.Header; Logger.Debug(Tag, "Removing #Koi metadata stream..."); header.StreamHeaders.Remove(header.GetStream <KoiStream>().StreamHeader); } else { Logger.Debug(Tag, "Not removing koi stream as some exports were ignored."); } }
public void Devirtualise(DevirtualisationOptions options) { Logger.Log(Tag, "Started devirtualisation."); // Create output directory. options.OutputOptions.EnsureDirectoriesExist(); var context = CreateDevirtualisationContext(options); // Run pipeline. RunPipeline(context); // Rebuild. Rebuild(options, context); Logger.Log(Tag, $"Finished. All fish were caught and served!"); }