diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs index ff2eae1ea..665b2a8ec 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs @@ -106,6 +106,10 @@ public bool Restore(MethodDef method) { if (arrayType == null) break; operandType = arrayType.Next; + if (!operandType.IsValueType) { + newOpCode = OpCodes.Ldelem_Ref; + operandType = null; + } break; case Code.Ldobj: diff --git a/de4dot.code/deobfuscators/MethodStack.cs b/de4dot.code/deobfuscators/MethodStack.cs index 39de3f709..20d60bc11 100644 --- a/de4dot.code/deobfuscators/MethodStack.cs +++ b/de4dot.code/deobfuscators/MethodStack.cs @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License */ using System.Collections.Generic; +using de4dot.blocks; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -38,6 +39,7 @@ public PushedArgs(int numArgs) { public void Add(Instruction instr) => args[nextIndex--] = instr; public void Set(int i, Instruction instr) => args[i] = instr; + internal void Pop() => args[++nextIndex] = null; public Instruction Get(int i) { if (0 <= i && i < args.Count) @@ -46,19 +48,6 @@ public Instruction Get(int i) { } public Instruction GetEnd(int i) => Get(args.Count - 1 - i); - - public void FixDups() { - Instruction prev = null, instr; - for (int i = 0; i < NumValidArgs; i++, prev = instr) { - instr = args[i]; - if (instr == null || prev == null) - continue; - if (instr.OpCode.Code != Code.Dup) - continue; - args[i] = prev; - instr = prev; - } - } } public static class MethodStack { @@ -78,48 +67,144 @@ public static PushedArgs GetPushedArgInstructions(IList instruction // May not return all args. The args are returned in reverse order. static PushedArgs GetPushedArgInstructions(IList instructions, int index, int numArgs) { var pushedArgs = new PushedArgs(numArgs); + if (!pushedArgs.CanAddMore) return pushedArgs; - Instruction instr; - int skipPushes = 0; - while (index >= 0 && pushedArgs.CanAddMore) { - instr = GetPreviousInstruction(instructions, ref index); - if (instr == null) - break; - - instr.CalculateStackUsage(false, out int pushes, out int pops); - if (pops == -1) - break; - if (instr.OpCode.Code == Code.Dup) { - pushes = 1; - pops = 0; - } - if (pushes > 1) - break; - - if (skipPushes > 0) { - skipPushes -= pushes; - if (skipPushes < 0) + Dictionary branches = null; + var states = new Stack(); + var state = new State(index, null, 0, 0, 1, new HashSet()); + var isBacktrack = false; + states.Push(state.Clone()); + while (true) { + while (state.index >= 0) { + if (branches != null && branches.TryGetValue(state.index, out var branch) && state.visited.Add(state.index)) { + branch.current = 0; + var brState = state.Clone(); + brState.branch = branch; + states.Push(brState); + } + if (!isBacktrack) + state.index--; + isBacktrack = false; + var update = UpdateState(instructions, state, pushedArgs); + if (update == Update.Finish) + return pushedArgs; + if (update == Update.Fail) break; - skipPushes += pops; } + + if (states.Count == 0) + return pushedArgs; + + var prevValidArgs = state.validArgs; + state = states.Pop(); + if (state.validArgs < prevValidArgs) + for (int i = state.validArgs + 1; i <= prevValidArgs; i++) + pushedArgs.Pop(); + + if (branches == null) + branches = GetBranches(instructions); else { - if (pushes == 1) - pushedArgs.Add(instr); - skipPushes += pops; + isBacktrack = true; + state.index = state.branch.Variants[state.branch.current++]; + if (state.branch.current < state.branch.Variants.Count) + states.Push(state.Clone()); + else + state.branch = null; } } - instr = pushedArgs.Get(0); - if (instr != null && instr.OpCode.Code == Code.Dup) { - instr = GetPreviousInstruction(instructions, ref index); - if (instr != null) { - instr.CalculateStackUsage(false, out int pushes, out int pops); - if (pushes == 1 && pops == 0) - pushedArgs.Set(0, instr); + + } + + class Branch { + public int current; + public List Variants { get; } + public Branch() => Variants = new List(); + } + + class State { + public int index; + public Branch branch; + public int validArgs; + public int skipPushes; + public int addPushes; + public HashSet visited; + + public State(int index, Branch branch, int validArgs, int skipPushes, int addPushes, HashSet visited) { + this.index = index; + this.branch = branch; + this.validArgs = validArgs; + this.skipPushes = skipPushes; + this.addPushes = addPushes; + this.visited = visited; + } + + public State Clone() => new State(index, branch, validArgs, skipPushes, addPushes, new HashSet(visited)); + } + + enum Update { Ok, Fail, Finish }; + + private static Update UpdateState(IList instructions, State state, PushedArgs pushedArgs) { + if (state.index < 0 || state.index >= instructions.Count) + return Update.Fail; + var instr = instructions[state.index]; + if (!Instr.IsFallThrough(instr.OpCode)) + return Update.Fail; + instr.CalculateStackUsage(false, out int pushes, out int pops); + if (pops == -1) + return Update.Fail; + var isDup = instr.OpCode.Code == Code.Dup; + if (isDup) { + pushes = 1; + pops = 0; + } + if (pushes > 1) + return Update.Fail; + + if (state.skipPushes > 0) { + state.skipPushes -= pushes; + if (state.skipPushes < 0) + return Update.Fail; + state.skipPushes += pops; + } + else { + if (pushes == 1) { + if (isDup) + state.addPushes++; + else { + for (; state.addPushes > 0; state.addPushes--) { + pushedArgs.Add(instr); + state.validArgs++; + if (!pushedArgs.CanAddMore) + return Update.Finish; + } + state.addPushes = 1; + } } + state.skipPushes += pops; } - pushedArgs.FixDups(); + return Update.Ok; + } - return pushedArgs; + private static IList CacheInstructions = null; + private static Dictionary CacheBranches = null; + + // cache last branches based on instructions object + private static Dictionary GetBranches(IList instructions) { + if (CacheInstructions == instructions) return CacheBranches; + CacheInstructions = instructions; + CacheBranches = new Dictionary(); + for (int b = 0; b < instructions.Count; b++) { + var br = instructions[b]; + if (br.Operand is Instruction target) { + var t = instructions.IndexOf(target); + if (!CacheBranches.TryGetValue(t, out var branch)) { + branch = new Branch(); + CacheBranches.Add(t, branch); + } + branch.Variants.Add(b); + } + } + return CacheBranches; } public static TypeSig GetLoadedType(MethodDef method, IList instructions, int instrIndex) => @@ -282,24 +367,5 @@ static ByRefSig CreateByRefType(TypeSig elementType) { return new ByRefSig(elementType); } - static Instruction GetPreviousInstruction(IList instructions, ref int instrIndex) { - while (true) { - instrIndex--; - if (instrIndex < 0) - return null; - var instr = instructions[instrIndex]; - if (instr.OpCode.Code == Code.Nop) - continue; - if (instr.OpCode.OpCodeType == OpCodeType.Prefix) - continue; - switch (instr.OpCode.FlowControl) { - case FlowControl.Next: - case FlowControl.Call: - return instr; - default: - return null; - } - } - } } }