/* Copyright (C) 2011-2015 de4dot@gmail.com This file is part of de4dot. de4dot is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. de4dot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with de4dot. If not, see . */ using System; using System.Collections.Generic; using dnlib.DotNet; using dnlib.DotNet.Emit; using de4dot.blocks; namespace de4dot.blocks.cflow { // Very simple constants folder which is all that's needed at the moment class ConstantsFolder : BlockDeobfuscator { InstructionEmulator instructionEmulator = new InstructionEmulator(); IList args; public bool DisableNewCode { get; set; } protected override void Initialize(List allBlocks) { base.Initialize(allBlocks); args = blocks.Method.Parameters; } protected override bool Deobfuscate(Block block) { bool modified = false; instructionEmulator.Initialize(blocks, allBlocks[0] == block); var instrs = block.Instructions; for (int i = 0; i < instrs.Count; i++) { var instr = instrs[i]; switch (instr.OpCode.Code) { case Code.Ldarg: case Code.Ldarg_0: case Code.Ldarg_1: case Code.Ldarg_2: case Code.Ldarg_3: case Code.Ldarg_S: modified |= FixLoadInstruction(block, i, instructionEmulator.GetArg(instr.Instruction.GetParameter(args))); break; case Code.Ldloc: case Code.Ldloc_0: case Code.Ldloc_1: case Code.Ldloc_2: case Code.Ldloc_3: case Code.Ldloc_S: modified |= FixLoadInstruction(block, i, instructionEmulator.GetLocal(instr.Instruction.GetLocal(blocks.Locals))); break; case Code.Ldarga: case Code.Ldarga_S: instructionEmulator.MakeArgUnknown((Parameter)instr.Operand); break; case Code.Ldloca: case Code.Ldloca_S: instructionEmulator.MakeLocalUnknown((Local)instr.Operand); break; case Code.Add: case Code.Add_Ovf: case Code.Add_Ovf_Un: case Code.And: case Code.Ceq: case Code.Cgt: case Code.Cgt_Un: case Code.Clt: case Code.Clt_Un: case Code.Conv_I: case Code.Conv_I1: case Code.Conv_I2: case Code.Conv_I4: case Code.Conv_I8: case Code.Conv_Ovf_I: case Code.Conv_Ovf_I_Un: case Code.Conv_Ovf_I1: case Code.Conv_Ovf_I1_Un: case Code.Conv_Ovf_I2: case Code.Conv_Ovf_I2_Un: case Code.Conv_Ovf_I4: case Code.Conv_Ovf_I4_Un: case Code.Conv_Ovf_I8: case Code.Conv_Ovf_I8_Un: case Code.Conv_Ovf_U: case Code.Conv_Ovf_U_Un: case Code.Conv_Ovf_U1: case Code.Conv_Ovf_U1_Un: case Code.Conv_Ovf_U2: case Code.Conv_Ovf_U2_Un: case Code.Conv_Ovf_U4: case Code.Conv_Ovf_U4_Un: case Code.Conv_Ovf_U8: case Code.Conv_Ovf_U8_Un: case Code.Conv_R_Un: case Code.Conv_R4: case Code.Conv_R8: case Code.Conv_U: case Code.Conv_U1: case Code.Conv_U2: case Code.Conv_U4: case Code.Conv_U8: case Code.Div: case Code.Div_Un: case Code.Dup: case Code.Mul: case Code.Mul_Ovf: case Code.Mul_Ovf_Un: case Code.Neg: case Code.Not: case Code.Or: case Code.Rem: case Code.Rem_Un: case Code.Shl: case Code.Shr: case Code.Shr_Un: case Code.Sub: case Code.Sub_Ovf: case Code.Sub_Ovf_Un: case Code.Xor: if (DisableNewCode) break; if (i + 1 < instrs.Count && instrs[i + 1].OpCode.Code == Code.Pop) break; if (!VerifyValidArgs(instr.Instruction)) break; instructionEmulator.Emulate(instr.Instruction); var tos = instructionEmulator.Peek(); Instruction newInstr = null; if (tos.IsInt32()) { var val = (Int32Value)tos; if (val.AllBitsValid()) newInstr = Instruction.CreateLdcI4(val.Value); } else if (tos.IsInt64()) { var val = (Int64Value)tos; if (val.AllBitsValid()) newInstr = OpCodes.Ldc_I8.ToInstruction(val.Value); } else if (tos.IsReal8()) { var val = (Real8Value)tos; if (val.IsValid) newInstr = GetLoadRealInstruction(val.Value); } if (newInstr != null) { block.Insert(i + 1, Instruction.Create(OpCodes.Pop)); block.Insert(i + 2, newInstr); i += 2; modified = true; } continue; } try { instructionEmulator.Emulate(instr.Instruction); } catch (NullReferenceException) { // Here if eg. invalid metadata token in a call instruction (operand is null) break; } } return modified; } bool VerifyValidArgs(Instruction instr) { int pushes, pops; instr.CalculateStackUsage(out pushes, out pops); if (pops < 0) return false; bool retVal; Value val2, val1; switch (pops) { case 0: return true; case 1: val1 = instructionEmulator.Pop(); retVal = VerifyValidArg(val1); instructionEmulator.Push(val1); return retVal; case 2: val2 = instructionEmulator.Pop(); val1 = instructionEmulator.Pop(); retVal = VerifyValidArg(val2) && VerifyValidArg(val1); instructionEmulator.Push(val1); instructionEmulator.Push(val2); return retVal; } return false; } static bool VerifyValidArg(Value value) { if (value.IsInt32()) return ((Int32Value)value).AllBitsValid(); if (value.IsInt64()) return ((Int64Value)value).AllBitsValid(); if (value.IsReal8()) return ((Real8Value)value).IsValid; return false; } static Instruction GetLoadRealInstruction(double value) { var floatVal = (float)value; if (floatVal == value || double.IsNaN(value)) return OpCodes.Ldc_R4.ToInstruction(floatVal); return OpCodes.Ldc_R8.ToInstruction(value); } bool FixLoadInstruction(Block block, int index, Value value) { if (value.IsInt32()) { var intValue = (Int32Value)value; if (!intValue.AllBitsValid()) return false; block.Instructions[index] = new Instr(Instruction.CreateLdcI4(intValue.Value)); return true; } else if (value.IsInt64()) { var intValue = (Int64Value)value; if (!intValue.AllBitsValid()) return false; block.Instructions[index] = new Instr(OpCodes.Ldc_I8.ToInstruction(intValue.Value)); return true; } return false; } } }