protected override void RunTest(Program program, TextWriter fut) { InitProgram(program); IImportResolver importResolver = null; var eventListener = new FakeDecompilerEventListener(); DataFlowAnalysis dfa = new DataFlowAnalysis( program, importResolver, eventListener); dfa.UntangleProcedures(); foreach (Procedure proc in program.Procedures.Values) { SsaTransform sst = new SsaTransform( dfa.ProgramDataFlow, proc, importResolver, proc.CreateBlockDominatorGraph(), new HashSet<RegisterStorage>()); SsaState ssa = sst.SsaState; var icrw = new IndirectCallRewriter( program, ssa, eventListener); icrw.Rewrite(); ssa.Write(fut); proc.Write(false, fut); fut.WriteLine(); } }
/// <summary> /// Converts all registers and stack accesses to SSA variables. /// </summary> /// <param name="proc"></param> /// <returns>The SsaTransform for the procedure.</returns> public SsaTransform ConvertToSsa(Procedure proc) { if (program.NeedsSsaTransform) { // Transform the procedure to SSA state. When encountering 'call' // instructions, they can be to functions already visited. If so, // they have a "ProcedureFlow" associated with them. If they have // not been visited, or are computed destinations (e.g. vtables) // they will have no "ProcedureFlow" associated with them yet, in // which case the the SSA treats the call as a "hell node". var sst = new SsaTransform(program, proc, sccProcs !, dynamicLinker, this.ProgramDataFlow); var ssa = sst.Transform(); DumpWatchedProcedure("ssa", "After SSA", ssa.Procedure); // Merge unaligned memory accesses. var fuser = new UnalignedMemoryAccessFuser(ssa); fuser.Transform(); // After value propagation expressions like (x86) // mem[esp_42+4] will have been converted to mem[fp - 30]. // We also hope that procedure constants // kept in registers are propagated to the corresponding call // sites. var vp = new ValuePropagator(program.SegmentMap, ssa, program.CallGraph, dynamicLinker, eventListener); vp.Transform(); DumpWatchedProcedure("vp", "After first VP", ssa.Procedure); // Fuse additions and subtractions that are linked by the carry flag. var larw = new LongAddRewriter(ssa, eventListener); larw.Transform(); // Propagate condition codes and registers. var cce = new ConditionCodeEliminator(program, ssa, eventListener); cce.Transform(); vp.Transform(); DumpWatchedProcedure("cce", "After CCE", ssa.Procedure); // Now compute SSA for the stack-based variables as well. That is: // mem[fp - 30] becomes wLoc30, while // mem[fp + 30] becomes wArg30. // This allows us to compute the dataflow of this procedure. sst.RenameFrameAccesses = true; sst.Transform(); DumpWatchedProcedure("ssaframe", "After SSA frame accesses", ssa.Procedure); var icrw = new IndirectCallRewriter(program, ssa, eventListener); while (!eventListener.IsCanceled() && icrw.Rewrite()) { vp.Transform(); sst.RenameFrameAccesses = true; sst.Transform(); } var fpuGuesser = new FpuStackReturnGuesser(ssa, eventListener); fpuGuesser.Transform(); // By placing use statements in the exit block, we will collect // reaching definitions in the use statements. sst.AddUsesToExitBlock(); sst.RemoveDeadSsaIdentifiers(); // Backpropagate stack pointer from procedure return. var spBackpropagator = new StackPointerBackpropagator(ssa, eventListener); spBackpropagator.BackpropagateStackPointer(); DumpWatchedProcedure("spbp", "After SP BP", ssa.Procedure); // Propagate those newly created stack-based identifiers. vp.Transform(); DumpWatchedProcedure("vp2", "After VP2", ssa.Procedure); return(sst); } else { // We are assuming phi functions are already generated. var sst = new SsaTransform(program, proc, sccProcs !, dynamicLinker, this.ProgramDataFlow); return(sst); } }
/// <summary> /// Processes procedures individually, building complex expression trees out /// of the simple, close-to-the-machine code generated by the disassembly. /// </summary> /// <param name="rl"></param> public void BuildExpressionTrees() { int i = 0; foreach (Procedure proc in program.Procedures.Values) { if (eventListener.IsCanceled()) { break; } eventListener.ShowProgress("Building complex expressions.", i, program.Procedures.Values.Count); ++i; try { var sst = BuildSsaTransform(proc); var ssa = sst.SsaState; var fuser = new UnalignedMemoryAccessFuser(ssa); fuser.Transform(); var vp = new ValuePropagator(program.Architecture, ssa, eventListener); sst.RenameFrameAccesses = true; var icrw = new IndirectCallRewriter(program, ssa, eventListener); while (!eventListener.IsCanceled() && icrw.Rewrite()) { vp.Transform(); sst.Transform(); } var cce = new ConditionCodeEliminator(ssa, program.Platform); cce.Transform(); //var cd = new ConstDivisionImplementedByMultiplication(ssa); //cd.Transform(); DeadCode.Eliminate(proc, ssa); vp.Transform(); DeadCode.Eliminate(proc, ssa); // Build expressions. A definition with a single use can be subsumed // into the using expression. var coa = new Coalescer(proc, ssa); coa.Transform(); DeadCode.Eliminate(proc, ssa); vp.Transform(); var liv = new LinearInductionVariableFinder( proc, ssa.Identifiers, new BlockDominatorGraph(proc.ControlGraph, proc.EntryBlock)); liv.Find(); foreach (KeyValuePair <LinearInductionVariable, LinearInductionVariableContext> de in liv.Contexts) { var str = new StrengthReduction(ssa, de.Key, de.Value); str.ClassifyUses(); str.ModifyUses(); } var opt = new OutParameterTransformer(proc, ssa.Identifiers); opt.Transform(); DeadCode.Eliminate(proc, ssa); // Definitions with multiple uses and variables joined by PHI functions become webs. var web = new WebBuilder(proc, ssa.Identifiers, program.InductionVariables); web.Transform(); ssa.ConvertBack(false); } catch (StatementCorrelatedException stex) { eventListener.Error( eventListener.CreateStatementNavigator(program, stex.Statement), stex, "An error occurred during data flow analysis."); } catch (Exception ex) { eventListener.Error( new NullCodeLocation(proc.Name), ex, "An error occurred during data flow analysis."); } } }
/// <summary> /// Processes procedures individually, building complex expression trees out /// of the simple, close-to-the-machine code generated by the disassembly. /// </summary> /// <param name="rl"></param> public void BuildExpressionTrees() { int i = 0; foreach (Procedure proc in program.Procedures.Values) { if (eventListener.IsCanceled()) break; eventListener.ShowProgress("Building complex expressions.", i, program.Procedures.Values.Count); ++i; try { var larw = new LongAddRewriter(proc, program.Architecture); larw.Transform(); Aliases alias = new Aliases(proc, program.Architecture, flow); alias.Transform(); var doms = new DominatorGraph<Block>(proc.ControlGraph, proc.EntryBlock); var sst = new SsaTransform(flow, proc, importResolver, doms, new HashSet<RegisterStorage>()); var ssa = sst.SsaState; var icrw = new IndirectCallRewriter(program, ssa, eventListener); icrw.Rewrite(); var cce = new ConditionCodeEliminator(ssa, program.Platform); cce.Transform(); //var cd = new ConstDivisionImplementedByMultiplication(ssa); //cd.Transform(); DeadCode.Eliminate(proc, ssa); var vp = new ValuePropagator(program.Architecture, ssa); vp.Transform(); DeadCode.Eliminate(proc, ssa); // Build expressions. A definition with a single use can be subsumed // into the using expression. var coa = new Coalescer(proc, ssa); coa.Transform(); DeadCode.Eliminate(proc, ssa); vp.Transform(); var liv = new LinearInductionVariableFinder( proc, ssa.Identifiers, new BlockDominatorGraph(proc.ControlGraph, proc.EntryBlock)); liv.Find(); foreach (KeyValuePair<LinearInductionVariable, LinearInductionVariableContext> de in liv.Contexts) { var str = new StrengthReduction(ssa, de.Key, de.Value); str.ClassifyUses(); str.ModifyUses(); } var opt = new OutParameterTransformer(proc, ssa.Identifiers); opt.Transform(); DeadCode.Eliminate(proc, ssa); // Definitions with multiple uses and variables joined by PHI functions become webs. var web = new WebBuilder(proc, ssa.Identifiers, program.InductionVariables); web.Transform(); ssa.ConvertBack(false); } catch (StatementCorrelatedException stex) { eventListener.Error( eventListener.CreateStatementNavigator(program, stex.Statement), stex, "An error occurred during data flow analysis."); } catch (Exception ex) { eventListener.Error( new NullCodeLocation(proc.Name), ex, "An error occurred during data flow analysis."); } } }
/// <summary> /// Converts all registers and stack accesses to SSA variables. /// </summary> /// <param name="proc"></param> /// <returns>The SsaTransform for the procedure.</returns> public SsaTransform ConvertToSsa(Procedure proc) { if (!program.NeedsSsaTransform) { // Some formats, like LLVM, already have phi functions. var sst = new SsaTransform(program, proc, sccProcs !, dynamicLinker, this.ProgramDataFlow); return(sst); } try { // Transform the procedure to SSA state. When encountering 'call' // instructions, they can be to functions already visited. If so, // they have a "ProcedureFlow" associated with them. If they have // not been visited, or are computed destinations (e.g. vtables) // they will have no "ProcedureFlow" associated with them yet, in // which case the the SSA treats the call as a "hell node". var sst = new SsaTransform(program, proc, sccProcs !, dynamicLinker, this.ProgramDataFlow); var ssa = sst.Transform(); DumpWatchedProcedure("ssa", "After SSA", ssa); // Merge unaligned memory accesses. var fuser = new UnalignedMemoryAccessFuser(ssa); fuser.Transform(); // Fuse additions and subtractions that are linked by the carry flag. var larw = new LongAddRewriter(ssa, eventListener); larw.Transform(); DumpWatchedProcedure("larw", "After long add rewriter", ssa); // After value propagation expressions like (x86) // mem[esp_42+4] will have been converted to mem[fp - 30]. // We also hope that procedure constants // kept in registers are propagated to the corresponding call // sites. var vp = new ValuePropagator(program.SegmentMap, ssa, program.CallGraph, dynamicLinker, eventListener); vp.Transform(); DumpWatchedProcedure("vp", "After first VP", ssa); // Value propagation may uncover more opportunities. larw = new LongAddRewriter(ssa, eventListener); larw.Transform(); DumpWatchedProcedure("larw2", "After second long add rewriter", ssa); // Eliminate condition codes by discovering uses of ccodes // and replacing them with higher-level constructs. var cce = new ConditionCodeEliminator(program, ssa, eventListener); cce.Transform(); vp.Transform(); DumpWatchedProcedure("cce", "After CCE", ssa); // Now compute SSA for the stack-based variables as well. That is: // mem[fp - 30] becomes wLoc30, while // mem[fp + 30] becomes wArg30. // This allows us to compute the dataflow of this procedure. sst.RenameFrameAccesses = true; sst.Transform(); DumpWatchedProcedure("ssaframe", "After SSA frame accesses", ssa); var icrw = new IndirectCallRewriter(program, ssa, eventListener); while (!eventListener.IsCanceled() && icrw.Rewrite()) { vp.Transform(); sst.RenameFrameAccesses = true; sst.Transform(); } var fpuGuesser = new FpuStackReturnGuesser(ssa, eventListener); fpuGuesser.Transform(); DumpWatchedProcedure("fpug", "After FPU stack guesser", ssa); // By placing use statements in the exit block, we will collect // reaching definitions in the use statements. sst.AddUsesToExitBlock(); sst.RemoveDeadSsaIdentifiers(); // Backpropagate stack pointer from procedure return. var spBackpropagator = new StackPointerBackpropagator(ssa, eventListener); spBackpropagator.BackpropagateStackPointer(); DumpWatchedProcedure("spbp", "After SP BP", ssa); // Propagate those newly created stack-based identifiers. vp.Transform(); DumpWatchedProcedure("vp2", "After VP2", ssa); return(sst); } catch (Exception ex) { var nl = Environment.NewLine; var banner = $"// {proc.Name} ==========={nl}{ex.Message}{nl}{ex.StackTrace}{nl}{nl}"; services.GetService <ITestGenerationService>()? .ReportProcedure($"analysis_{99:00}_crash.txt", banner, proc); throw; } }