private IEnumerable<IRewriteTarget> GetSetupsFromInstructions(object target, MethodBody body, List<MethodDefinition> setupMethods) { var result = new List<IRewriteTarget>(); foreach (var instruction in body.Instructions) { MethodReference calledMethod; if (_instructionHelper.TryGetCall(instruction, out calledMethod)) { var operandMethod = calledMethod.Resolve(); if (setupMethods.Contains(operandMethod)) { const int expectedStackSize = 3; // Raise(addAction, removeAction, argument) --> skip 1 for removeAction, skip 2 for addAction. Action addAction = _expressionDecompiler.Decompile(body, instruction, target, expectedStackSize, 2); Action removeAction = _expressionDecompiler.Decompile(body, instruction, target, expectedStackSize, 1); EventAccessorPair accessorPair = _eventAccessorExtractor.FindEventAccessor(addAction, removeAction); Type addMethodEventHandlerType = accessorPair.AddAccessor.GetParameters()[0].ParameterType; Type removeMethodEventHandlerType = accessorPair.RemoveAccessor.GetParameters()[0].ParameterType; if (addMethodEventHandlerType != removeMethodEventHandlerType) { throw new ArgumentException("Event handler types do not match"); } result.Add(new EventRewriteTarget(accessorPair.AddAccessor, accessorPair.RemoveAccessor, addMethodEventHandlerType)); } } } return result; }
private void AnalyzeBodyTypeReferences(TypeDefinition typeDecl, MethodDefinition method, Mono.Cecil.Cil.MethodBody body) { int index = 0; var instructions = body.Instructions; while (index < instructions.Count) { var i = instructions[index]; var opCode = i.OpCode; switch (opCode.OperandType) { case Mono.Cecil.Cil.OperandType.InlineTok: case Mono.Cecil.Cil.OperandType.InlineType: case Mono.Cecil.Cil.OperandType.InlineMethod: case Mono.Cecil.Cil.OperandType.InlineField: { object op = i.Operand; if (op == null) { Logger.LogError("Unexpected null operand in method=" + method.Name); } else { TypeReference t = op as TypeReference; if (t != null) { string context = "Analyze type references of method " + typeDecl.Name + "::" + method.Name; RegisterRelation(t, typeDecl, "reference", context); } else { MemberReference m = op as MemberReference; if (m != null) { string context = "Analyze member references of method " + typeDecl.Name + "::" + method.Name; RegisterRelation(m.DeclaringType, typeDecl, "reference", context); } else { Logger.LogError("Unhandled token type: " + op + " in method = " + method.Name); } } } } break; } index++; } }
internal BlockBuilder(Mono.Cecil.Cil.MethodBody body, IDecompilerTypeSystem typeSystem, Dictionary <Mono.Cecil.Cil.ExceptionHandler, ILVariable> variableByExceptionHandler) { Debug.Assert(body != null); Debug.Assert(typeSystem != null); Debug.Assert(variableByExceptionHandler != null); this.body = body; this.typeSystem = typeSystem; this.variableByExceptionHandler = variableByExceptionHandler; }
public AnalysisNet.Types.MethodBody ExtractBody(Cecil.Cil.MethodBody cciBody) { cecilBody = cciBody; AnalysisNet.Types.MethodBody ourBody = new AnalysisNet.Types.MethodBody(AnalysisNet.Types.MethodBodyKind.Bytecode) { MaxStack = (ushort)cciBody.MaxStackSize }; ExtractParameters(cciBody.Method, ourBody.Parameters); ExtractLocalVariables(cciBody.Variables, ourBody.LocalVariables); ExtractExceptionInformation(cciBody.ExceptionHandlers, ourBody.ExceptionInformation); ExtractInstructions(cciBody.Instructions, ourBody.Instructions); return(ourBody); }
private string FormatMethodBody(Mono.Cecil.Cil.MethodBody body) { var builder = new StringBuilder(); builder.AppendLine( "Locals: [ " + string.Join( ", ", body.Variables.Select(local => local.VariableType.ToString())) + " ]"); foreach (var insn in body.Instructions) { builder.AppendLine(insn.ToString()); } return(builder.ToString()); }
private void AnalyzeLocalVariables(FileInfo assemblyFileInfo, TypeDefinition typeDecl, MethodDefinition method, Mono.Cecil.Cil.MethodBody body) { foreach (Mono.Cecil.Cil.VariableDefinition variable in body.Variables) { try { string context = "Analyze local variables of method " + typeDecl.Name + "::" + method.Name; RegisterRelation(variable.VariableType, typeDecl, "local", context); } catch (Exception e) { Logger.LogException( $"Analysis failed assembly={assemblyFileInfo.FullName} type={typeDecl.Name} method={method.Name} variable={variable}", e); } } }
private void AnalyseMethodBody(FileInfo assemblyFileInfo, TypeDefinition typeDecl, MethodDefinition method) { Mono.Cecil.Cil.MethodBody body = method.Body; try { if (body != null) { AnalyzeLocalVariables(assemblyFileInfo, typeDecl, method, body); AnalyzeBodyTypeReferences(typeDecl, method, body); } } catch (Exception e) { Logger.LogException( $"Analysis failed asssmbly={assemblyFileInfo.FullName} type={typeDecl.Name} method={method.Name}", e); } }
private void CreateExceptionHandlers(IDictionary <Model.Bytecode.Instruction, IList <Mono.Cecil.Cil.Instruction> > map, AnalysisNet.Types.MethodBody analysisNetBody, Cecil.Cil.MethodBody cecilBody) { foreach (AnalysisNet.ProtectedBlock protectedBlock in analysisNetBody.ExceptionInformation) { Cecil.Cil.ExceptionHandler handler = new Cecil.Cil.ExceptionHandler(GetExceptionHandlerType(protectedBlock.Handler.Kind)) { TryStart = GetTarget(protectedBlock.Start, map), TryEnd = GetTarget(protectedBlock.End, map) }; if (protectedBlock.Handler is AnalysisNet.FilterExceptionHandler filterHandler) { handler.CatchType = ReferenceGenerator.TypeReference(filterHandler.ExceptionType); handler.FilterStart = GetTarget(filterHandler.FilterStart, map); handler.HandlerStart = GetTarget(filterHandler.Start, map); handler.HandlerEnd = GetTarget(filterHandler.End, map); } else if (protectedBlock.Handler is AnalysisNet.CatchExceptionHandler catchHandler) { handler.CatchType = ReferenceGenerator.TypeReference(catchHandler.ExceptionType); handler.HandlerStart = GetTarget(catchHandler.Start, map); handler.HandlerEnd = GetTarget(catchHandler.End, map); } else if (protectedBlock.Handler is AnalysisNet.FaultExceptionHandler faultHandler) { handler.HandlerStart = GetTarget(faultHandler.Start, map); handler.HandlerEnd = GetTarget(faultHandler.End, map); } else if (protectedBlock.Handler is AnalysisNet.FinallyExceptionHandler finallyHandler) { handler.HandlerStart = GetTarget(finallyHandler.Start, map); handler.HandlerEnd = GetTarget(finallyHandler.End, map); } else { throw new NotImplementedException(); } cecilBody.ExceptionHandlers.Add(handler); } }
/// <summary> /// Writes a CIL method body, analyzes it as Flame IR, /// emits that as CIL and checks that the outcome matches /// what we'd expect. /// </summary> /// <param name="returnType"> /// The return type of the method body. /// </param> /// <param name="parameterTypes"> /// The parameter types of the method body. /// </param> /// <param name="localTypes"> /// The local variable types of the method body. /// </param> /// <param name="emitBody"> /// A function that writes the method body. /// </param> /// <param name="emitOracle"> /// A function that writes the expected method body. /// </param> private void RoundtripStaticMethodBody( TypeReference returnType, IReadOnlyList <TypeReference> parameterTypes, IReadOnlyList <TypeReference> localTypes, Action <Mono.Cecil.Cil.ILProcessor> emitBody, Action <Mono.Cecil.Cil.ILProcessor> emitOracle) { // Synthesize the expected CIL. var expectedCilBody = new Mono.Cecil.Cil.MethodBody( CreateStaticMethodDef(returnType, parameterTypes)); emitOracle(expectedCilBody.GetILProcessor()); expectedCilBody.Optimize(); // Format the synthesized CIL. RoundtripStaticMethodBody( returnType, parameterTypes, localTypes, emitBody, FormatMethodBody(expectedCilBody)); }
public DisassembleResult(ModuleDefinition module, MethodBody body) { ModuleDefinition = module; Body = body; }
public static void Uninstall(this InstallerWindow ins) { if (ins.MainMod == null) { return; } ins.MainMod.Dispose(); // Uninstall can be invoked without the installer running ins.Invoke(() => ExePath = ins.ExePathBox.Text).Wait(); if (ETGFinder.IsBinaryWrapped) { ExePath = Path.Combine(Directory.GetParent(ExePath).FullName, ETGFinder.MainName); } string pathGame = ins.MainModDir; string pathBackup = Path.Combine(pathGame, "ModBackup"); if (!Directory.Exists(pathBackup)) { return; } string[] files = Directory.GetFiles(pathGame); ins.InitProgress("Removing leftover files", files.Length + 1); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (!file.Contains(".mm.")) { continue; } ins.Log("Removing: ").LogLine(file); ins.SetProgress("Removing: " + file, i); File.Delete(files[i]); } if (ins.ModVersion != null) { ins.Log("Found previous mod installation: ").LogLine(ins.ModVersion); ins.LogLine("Reverting to unmodded backup..."); } else { ins.LogLine("No previous mod installation found."); ins.LogLine("Still reverting to unmodded backup..."); } string etgBackup = Path.Combine(pathBackup, ETGFinder.MainName); ins.Log("Reverting: ").LogLine(ETGFinder.MainName); if (File.Exists(etgBackup)) { File.Delete(ExePath); File.Move(etgBackup, ExePath); } else { ins.Log("WARNING: Backup not found for ").LogLine(ETGFinder.MainName); } files = Directory.GetFiles(pathBackup); ins.InitProgress("Uninstalling ETGMod", files.Length + 1); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); ins.Log("Reverting: ").LogLine(file); ins.SetProgress("Reverting: " + file, i); string origPath = Path.Combine(pathGame, file); File.Delete(origPath); File.Move(files[i], origPath); } ins.LogLine("Reloading Assembly-CSharp.dll"); ins.SetProgress("Reloading Assembly-CSharp.dll", files.Length); ins.MainMod = new MonoModder() { InputPath = ins.MainModIn }; ins.MainMod.SetupETGModder(); #if DEBUG if (LogPath == null) { ins.MainMod.Read(); // Read main module first ins.MainMod.ReadMod(ins.MainModDir); // ... then mods ins.MainMod.MapDependencies(); // ... then all dependencies } else using (FileStream fileStream = File.Open(LogPath, FileMode.Append)) { using (StreamWriter streamWriter = new StreamWriter(fileStream)) { ins.MainMod.Logger = (string s) => ins.OnActivity(); ins.MainMod.Logger += (string s) => streamWriter.WriteLine(s); // MonoMod.MonoModSymbolReader.MDBDEBUG = true; #endif ins.MainMod.Read(); // Read main module first ins.MainMod.ReadMod(ins.MainModDir); // ... then mods ins.MainMod.MapDependencies(); // ... then all dependencies #if DEBUG Mono.Cecil.TypeDefinition etgMod = ins.MainMod.Module.GetType("ETGMod"); if (etgMod != null) { for (int i = 0; i < etgMod.Methods.Count; i++) { Mono.Cecil.Cil.MethodBody body = etgMod.Methods[i].Body; } } } } ins.MainMod.Logger = null; #endif ins.EndProgress("Uninstalling complete."); }
private void BuildIL(MethodBase method, GetILGeneratorDelegate getILGenerator, Mono.Cecil.Cil.MethodBody body) { ILGenerator generator = getILGenerator(body.CodeSize); dynamicMethodBuilder.BuildIL(method, generator, body); }
/// <summary> /// Compiles the source body to a CIL method body. /// </summary> /// <returns>A CIL method body.</returns> public Mono.Cecil.Cil.MethodBody Compile() { // Create a method body. var result = new Mono.Cecil.Cil.MethodBody(Method); // Figure out which 'alloca' values can be replaced // by local variables. Usually, that's all of them. var sourceGraph = SourceBody.Implementation; var allocaToVarMap = AllocasToVariables(sourceGraph); // Select instructions. var selector = new CilInstructionSelector(Method, TypeEnvironment, allocaToVarMap); var streamBuilder = new LinearInstructionStreamBuilder <CilCodegenInstruction>( selector); var codegenInsns = streamBuilder.ToInstructionStream(sourceGraph); codegenInsns = OptimizeRegisterAccesses(codegenInsns); // Find the set of loaded values so we can allocate registers to them. var loadedValues = new HashSet <ValueTag>( codegenInsns .SelectMany(insn => insn.Traversal) .OfType <CilLoadRegisterInstruction>() .Select(insn => insn.Value)); loadedValues.UnionWith( codegenInsns .SelectMany(insn => insn.Traversal) .OfType <CilAddressOfRegisterInstruction>() .Select(insn => insn.Value)); // Allocate registers to values. var regAllocator = new CilRegisterAllocator( loadedValues, GetPreallocatedRegisters(sourceGraph), Method.Module); var regAllocation = regAllocator.Analyze(sourceGraph); // Synthesize the actual method body. var processor = result.GetILProcessor(); var emitter = new CodegenEmitter(processor, regAllocation); emitter.Emit(codegenInsns); // Add local variables to method body. Put most popular // locals first to minimize the number of long-form ldloc/stloc // instructions. result.InitLocals = true; foreach (var pair in emitter.RegisterUseCounts.OrderByDescending(pair => pair.Value)) { result.Variables.Add(pair.Key); } foreach (var local in allocaToVarMap.Values) { result.Variables.Add(local); } foreach (var temp in selector.Temporaries) { result.Variables.Add(temp); } // Apply peephole optimizations to the generated method body. CilPeepholeOptimizer.Optimize(result); // Apply Cecil's macro optimizations to the generated method body. MethodBodyRocks.Optimize(result); return(result); }
/// <summary> /// Writes a CIL method body, analyzes it as Flame IR, /// emits that as CIL and checks that the outcome matches /// what we'd expect. /// </summary> /// <param name="returnType"> /// The return type of the method body. /// </param> /// <param name="parameterTypes"> /// The parameter types of the method body. /// </param> /// <param name="localTypes"> /// The local variable types of the method body. /// </param> /// <param name="emitBody"> /// A function that writes the method body. /// </param> /// <param name="oracle"> /// A printed version of the expected method body. /// </param> private void RoundtripStaticMethodBody( TypeReference returnType, IReadOnlyList <TypeReference> parameterTypes, IReadOnlyList <TypeReference> localTypes, Action <Mono.Cecil.Cil.ILProcessor> emitBody, string oracle) { // Define a method. var methodDef = CreateStaticMethodDef(returnType, parameterTypes); // Emit the source CIL. var cilBody = new Mono.Cecil.Cil.MethodBody(methodDef); foreach (var localType in localTypes) { cilBody.Variables.Add(new Mono.Cecil.Cil.VariableDefinition(localType)); } emitBody(cilBody.GetILProcessor()); cilBody.Optimize(); // Analyze it as Flame IR. var irBody = ClrMethodBodyAnalyzer.Analyze( cilBody, new Parameter(TypeHelpers.BoxIfReferenceType(corlib.Resolve(returnType))), default(Parameter), parameterTypes .Select((type, i) => new Parameter(TypeHelpers.BoxIfReferenceType(corlib.Resolve(type)), "param_" + i)) .ToArray(), corlib); // Register analyses. irBody = new global::Flame.Compiler.MethodBody( irBody.ReturnParameter, irBody.ThisParameter, irBody.Parameters, irBody.Implementation .WithAnalysis(LazyBlockReachabilityAnalysis.Instance) .WithAnalysis(NullabilityAnalysis.Instance) .WithAnalysis(new EffectfulInstructionAnalysis()) .WithAnalysis(PredecessorAnalysis.Instance) .WithAnalysis(RelatedValueAnalysis.Instance) .WithAnalysis(LivenessAnalysis.Instance) .WithAnalysis(InterferenceGraphAnalysis.Instance) .WithAnalysis(ValueUseAnalysis.Instance) .WithAnalysis(ConservativeInstructionOrderingAnalysis.Instance)); // Optimize the IR a tiny bit. irBody = irBody.WithImplementation( irBody.Implementation.Transform( AllocaToRegister.Instance, CopyPropagation.Instance, new ConstantPropagation(), SwitchSimplification.Instance, DeadValueElimination.Instance, InstructionSimplification.Instance, new JumpThreading(true), DeadBlockElimination.Instance, new SwitchLowering(corlib.Resolver.TypeEnvironment), CopyPropagation.Instance, InstructionSimplification.Instance, DeadValueElimination.Instance, InstructionReordering.Instance, new JumpThreading(false))); // Turn Flame IR back into CIL. var newCilBody = ClrMethodBodyEmitter.Compile(irBody, methodDef, corlib.Resolver.TypeEnvironment); // Check that the resulting CIL matches the expected CIL. var actual = FormatMethodBody(newCilBody); actual = actual.Trim().Replace("\r", ""); oracle = oracle.Trim().Replace("\r", ""); if (actual != oracle) { var encoder = new EncoderState(); var encodedImpl = encoder.Encode(irBody.Implementation); var actualIr = Les2LanguageService.Value.Print( encodedImpl, options: new LNodePrinterOptions { IndentString = new string(' ', 4) }); log.Log( new LogEntry( Severity.Message, "emitted CIL-oracle mismatch", "round-tripped CIL does not match the oracle. CIL emit output:", new Paragraph(new WrapBox(actual, 0, -actual.Length)), DecorationSpan.MakeBold("remark: Flame IR:"), new Paragraph(new WrapBox(actualIr, 0, -actualIr.Length)))); } Assert.AreEqual(oracle, actual); }
/// <summary> /// Writes a CIL method body, analyzes it as Flame IR /// and checks that the result is what we'd expect. /// </summary> /// <param name="returnType"> /// The return type of the method body. /// </param> /// <param name="parameterTypes"> /// The parameter types of the method body. /// </param> /// <param name="emitBody"> /// A function that writes the method body. /// </param> /// <param name="oracle"> /// The expected Flame IR flow graph, as LESv2. /// </param> private void AnalyzeStaticMethodBody( TypeReference returnType, IReadOnlyList <TypeReference> parameterTypes, IReadOnlyList <TypeReference> localTypes, Action <Mono.Cecil.Cil.ILProcessor> emitBody, string oracle) { var methodDef = new MethodDefinition( "f", MethodAttributes.Public | MethodAttributes.Static, returnType); foreach (var type in parameterTypes) { methodDef.Parameters.Add(new ParameterDefinition(type)); int index = methodDef.Parameters.Count - 1; methodDef.Parameters[index].Name = "param_" + index; } var cilBody = new Mono.Cecil.Cil.MethodBody(methodDef); foreach (var localType in localTypes) { cilBody.Variables.Add(new Mono.Cecil.Cil.VariableDefinition(localType)); } emitBody(cilBody.GetILProcessor()); var irBody = ClrMethodBodyAnalyzer.Analyze( cilBody, new Parameter(TypeHelpers.BoxIfReferenceType(corlib.Resolve(returnType))), default(Parameter), parameterTypes .Select((type, i) => new Parameter(TypeHelpers.BoxIfReferenceType(corlib.Resolve(type)), "param_" + i)) .ToArray(), corlib); var encoder = new EncoderState(); var encodedImpl = encoder.Encode(irBody.Implementation); var actual = Les2LanguageService.Value.Print( encodedImpl, options: new LNodePrinterOptions { IndentString = new string(' ', 4) }); if (actual.Trim() != oracle.Trim()) { log.Log( new LogEntry( Severity.Message, "CIL analysis-oracle mismatch", "analyzed CIL does not match the oracle. CIL analysis output:")); // TODO: ugly hack to work around wrapping. Console.Error.WriteLine(actual.Trim()); } Assert.AreEqual( actual.Trim(), oracle.Trim()); }
protected override void Visit(Mono.Cecil.Cil.MethodBody methodBody, Context context) { Touch(methodBody); base.Visit(methodBody, context); }
private IEnumerable <IRewriteTarget> GetSetupsFromInstructions(object target, MethodBody body, List <MethodDefinition> setupMethods) { var result = new List <IRewriteTarget>(); foreach (var instruction in body.Instructions) { MethodReference calledMethod; if (_instructionHelper.TryGetCall(instruction, out calledMethod)) { var operandMethod = calledMethod.Resolve(); if (setupMethods.Contains(operandMethod)) { const int expectedStackSize = 3; // Raise(addAction, removeAction, argument) --> skip 1 for removeAction, skip 2 for addAction. Action addAction = _expressionDecompiler.Decompile(body, instruction, target, expectedStackSize, 2); Action removeAction = _expressionDecompiler.Decompile(body, instruction, target, expectedStackSize, 1); EventAccessorPair accessorPair = _eventAccessorExtractor.FindEventAccessor(addAction, removeAction); Type addMethodEventHandlerType = accessorPair.AddAccessor.GetParameters()[0].ParameterType; Type removeMethodEventHandlerType = accessorPair.RemoveAccessor.GetParameters()[0].ParameterType; if (addMethodEventHandlerType != removeMethodEventHandlerType) { throw new ArgumentException("Event handler types do not match"); } result.Add(new EventRewriteTarget(accessorPair.AddAccessor, accessorPair.RemoveAccessor, addMethodEventHandlerType)); } } } return(result); }
private void Manipulator(ILContext ctx) { hookBody = ctx.Body; HarmonyManipulator.Manipulate(Original, Original.GetPatchInfo(), ctx); }