diff --git a/.gitmodules b/.gitmodules index f2f13f7a..31197a77 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "dnlib"] path = dnlib - url = git@bitbucket.org:0xd4d/dnlib.git + url = git@github.com:0xd4d/dnlib.git diff --git a/AssemblyData/Properties/AssemblyInfo.cs b/AssemblyData/Properties/AssemblyInfo.cs index c13cb77c..66113581 100644 --- a/AssemblyData/Properties/AssemblyInfo.cs +++ b/AssemblyData/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/AssemblyServer-CLR20-x64/Properties/AssemblyInfo.cs b/AssemblyServer-CLR20-x64/Properties/AssemblyInfo.cs index 885556ce..44833b42 100644 --- a/AssemblyServer-CLR20-x64/Properties/AssemblyInfo.cs +++ b/AssemblyServer-CLR20-x64/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/AssemblyServer-CLR20/Properties/AssemblyInfo.cs b/AssemblyServer-CLR20/Properties/AssemblyInfo.cs index ad310d96..11597cc7 100644 --- a/AssemblyServer-CLR20/Properties/AssemblyInfo.cs +++ b/AssemblyServer-CLR20/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/AssemblyServer-CLR40-x64/Properties/AssemblyInfo.cs b/AssemblyServer-CLR40-x64/Properties/AssemblyInfo.cs index 655bdd3a..8b008978 100644 --- a/AssemblyServer-CLR40-x64/Properties/AssemblyInfo.cs +++ b/AssemblyServer-CLR40-x64/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/AssemblyServer-CLR40/Properties/AssemblyInfo.cs b/AssemblyServer-CLR40/Properties/AssemblyInfo.cs index 953e469f..23148956 100644 --- a/AssemblyServer-CLR40/Properties/AssemblyInfo.cs +++ b/AssemblyServer-CLR40/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/AssemblyServer-x64/Properties/AssemblyInfo.cs b/AssemblyServer-x64/Properties/AssemblyInfo.cs index 76ca6b81..88329d74 100644 --- a/AssemblyServer-x64/Properties/AssemblyInfo.cs +++ b/AssemblyServer-x64/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/AssemblyServer/Properties/AssemblyInfo.cs b/AssemblyServer/Properties/AssemblyInfo.cs index f3040761..a710db94 100644 --- a/AssemblyServer/Properties/AssemblyInfo.cs +++ b/AssemblyServer/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/LICENSE.de4dot.txt b/LICENSE.de4dot.txt index 7f5a9468..b8822f03 100644 --- a/LICENSE.de4dot.txt +++ b/LICENSE.de4dot.txt @@ -17,6 +17,6 @@ along with de4dot. If not, see . -Official site: https://bitbucket.org/0xd4d/de4dot +Official site: https://github.com/0xd4d/de4dot See the file COPYING for more details. diff --git a/LICENSE.dnlib.txt b/LICENSE.dnlib.txt index aafeb7bc..89b254d3 100644 --- a/LICENSE.dnlib.txt +++ b/LICENSE.dnlib.txt @@ -21,4 +21,4 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Official site: https://bitbucket.org/0xd4d/dnlib +Official site: https://github.com/0xd4d/dnlib diff --git a/Test.Rename.Dll/Properties/AssemblyInfo.cs b/Test.Rename.Dll/Properties/AssemblyInfo.cs index c469fe85..530ec319 100644 --- a/Test.Rename.Dll/Properties/AssemblyInfo.cs +++ b/Test.Rename.Dll/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/Test.Rename/Properties/AssemblyInfo.cs b/Test.Rename/Properties/AssemblyInfo.cs index 0b60d049..66842afd 100644 --- a/Test.Rename/Properties/AssemblyInfo.cs +++ b/Test.Rename/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/de4dot-x64/Properties/AssemblyInfo.cs b/de4dot-x64/Properties/AssemblyInfo.cs index 13667790..5f9cf404 100644 --- a/de4dot-x64/Properties/AssemblyInfo.cs +++ b/de4dot-x64/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/de4dot.blocks/DotNetUtils.cs b/de4dot.blocks/DotNetUtils.cs index 89bd176d..88ad2712 100644 --- a/de4dot.blocks/DotNetUtils.cs +++ b/de4dot.blocks/DotNetUtils.cs @@ -174,9 +174,9 @@ namespace de4dot.blocks { public static bool IsPinvokeMethod(MethodDef method, string dll, string funcName) { if (method == null) return false; - if (method.ImplMap == null || method.ImplMap.Name.String != funcName) + if (method.ImplMap == null) return false; - return GetDllName(dll).Equals(GetDllName(method.ImplMap.Module.Name.String), StringComparison.OrdinalIgnoreCase); + return method.ImplMap.IsPinvokeMethod(dll, funcName); } public static MethodDef GetMethod(ModuleDefMD module, IMethod method) { diff --git a/de4dot.blocks/GenericArgsSubstitutor.cs b/de4dot.blocks/GenericArgsSubstitutor.cs index 17d83e66..77e6b95a 100644 --- a/de4dot.blocks/GenericArgsSubstitutor.cs +++ b/de4dot.blocks/GenericArgsSubstitutor.cs @@ -19,6 +19,7 @@ using System.Collections.Generic; using dnlib.DotNet; +using dnlib.Threading; namespace de4dot.blocks { public struct GenericArgsSubstitutor { @@ -291,7 +292,7 @@ namespace de4dot.blocks { newSig.Params.Add(Create2(sig.Params[i])); newSig.GenParamCount = sig.GenParamCount; if (sig.ParamsAfterSentinel != null) { - newSig.ParamsAfterSentinel = new List(); + newSig.ParamsAfterSentinel = ThreadSafeListCreator.Create(); for (int i = 0; i < sig.ParamsAfterSentinel.Count; i++) newSig.ParamsAfterSentinel.Add(Create2(sig.ParamsAfterSentinel[i])); } diff --git a/de4dot.blocks/Properties/AssemblyInfo.cs b/de4dot.blocks/Properties/AssemblyInfo.cs index b05b48f5..3fca2b8e 100644 --- a/de4dot.blocks/Properties/AssemblyInfo.cs +++ b/de4dot.blocks/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/de4dot.blocks/cflow/BranchEmulator.cs b/de4dot.blocks/cflow/BranchEmulator.cs index 3e3f71a1..8d9269b9 100644 --- a/de4dot.blocks/cflow/BranchEmulator.cs +++ b/de4dot.blocks/cflow/BranchEmulator.cs @@ -95,6 +95,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareEq((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareEq((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareEq((Real8Value)val1, (Real8Value)val2)); else if (val1.IsNull() && val2.IsNull()) return EmulateBranch(2, true); else @@ -109,6 +111,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareNeq((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareNeq((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareNeq((Real8Value)val1, (Real8Value)val2)); else if (val1.IsNull() && val2.IsNull()) return EmulateBranch(2, false); else @@ -123,6 +127,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareGe((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareGe((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareGe((Real8Value)val1, (Real8Value)val2)); else return false; } @@ -135,6 +141,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareGe_Un((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareGe_Un((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareGe_Un((Real8Value)val1, (Real8Value)val2)); else return false; } @@ -147,6 +155,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareGt((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareGt((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareGt((Real8Value)val1, (Real8Value)val2)); else return false; } @@ -159,6 +169,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareGt_Un((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareGt_Un((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareGt_Un((Real8Value)val1, (Real8Value)val2)); else return false; } @@ -171,6 +183,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareLe((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareLe((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareLe((Real8Value)val1, (Real8Value)val2)); else return false; } @@ -183,6 +197,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareLe_Un((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareLe_Un((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareLe_Un((Real8Value)val1, (Real8Value)val2)); else return false; } @@ -195,6 +211,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareLt((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareLt((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareLt((Real8Value)val1, (Real8Value)val2)); else return false; } @@ -207,6 +225,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(2, Int32Value.CompareLt_Un((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) return EmulateBranch(2, Int64Value.CompareLt_Un((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + return EmulateBranch(2, Real8Value.CompareLt_Un((Real8Value)val1, (Real8Value)val2)); else return false; } @@ -218,6 +238,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(1, Int32Value.CompareFalse((Int32Value)val1)); else if (val1.IsInt64()) return EmulateBranch(1, Int64Value.CompareFalse((Int64Value)val1)); + else if (val1.IsReal8()) + return EmulateBranch(1, Real8Value.CompareFalse((Real8Value)val1)); else if (val1.IsNull()) return EmulateBranch(1, true); else if (val1.IsObject() || val1.IsString()) @@ -233,6 +255,8 @@ namespace de4dot.blocks.cflow { return EmulateBranch(1, Int32Value.CompareTrue((Int32Value)val1)); else if (val1.IsInt64()) return EmulateBranch(1, Int64Value.CompareTrue((Int64Value)val1)); + else if (val1.IsReal8()) + return EmulateBranch(1, Real8Value.CompareTrue((Real8Value)val1)); else if (val1.IsNull()) return EmulateBranch(1, false); else if (val1.IsObject() || val1.IsString()) diff --git a/de4dot.blocks/cflow/ConstantsFolder.cs b/de4dot.blocks/cflow/ConstantsFolder.cs index a740fa45..b5227acf 100644 --- a/de4dot.blocks/cflow/ConstantsFolder.cs +++ b/de4dot.blocks/cflow/ConstantsFolder.cs @@ -70,6 +70,96 @@ namespace de4dot.blocks.cflow { 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 (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 { @@ -84,6 +174,53 @@ namespace de4dot.blocks.cflow { 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; diff --git a/de4dot.blocks/cflow/InstructionEmulator.cs b/de4dot.blocks/cflow/InstructionEmulator.cs index f42bdff6..447812b5 100644 --- a/de4dot.blocks/cflow/InstructionEmulator.cs +++ b/de4dot.blocks/cflow/InstructionEmulator.cs @@ -52,8 +52,12 @@ namespace de4dot.blocks.cflow { } public void Initialize(MethodDef method, bool emulateFromFirstInstruction) { - this.parameterDefs = method.Parameters; - this.localDefs = method.Body.Variables; + Initialize(method, method.Parameters, method.Body.Variables, method.Body.InitLocals, emulateFromFirstInstruction); + } + + public void Initialize(MethodDef method, IList methodParameters, IList methodLocals, bool initLocals, bool emulateFromFirstInstruction) { + this.parameterDefs = methodParameters; + this.localDefs = methodLocals; valueStack.Initialize(); protectedStackValues.Clear(); @@ -75,7 +79,7 @@ namespace de4dot.blocks.cflow { args.Clear(); args.AddRange(cached_args); locals.Clear(); - locals.AddRange(method.Body.InitLocals && emulateFromFirstInstruction ? cached_zeroed_locals : cached_locals); + locals.AddRange(initLocals && emulateFromFirstInstruction ? cached_zeroed_locals : cached_locals); } public void SetProtected(Value value) { @@ -429,9 +433,9 @@ namespace de4dot.blocks.cflow { case Code.Unbox: - case Code.Conv_R_Un: - case Code.Conv_R4: - case Code.Conv_R8: + case Code.Conv_R_Un:Emulate_Conv_R_Un(instr); break; + case Code.Conv_R4: Emulate_Conv_R4(instr); break; + case Code.Conv_R8: Emulate_Conv_R8(instr); break; case Code.Arglist: case Code.Beq: @@ -782,6 +786,36 @@ namespace de4dot.blocks.cflow { } } + void Emulate_Conv_R_Un(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_R_Un((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_R_Un((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_R_Un((Real8Value)val1)); break; + default: valueStack.Push(Real8Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_R4(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_R4((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_R4((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_R4((Real8Value)val1)); break; + default: valueStack.Push(Real8Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_R8(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_R8((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_R8((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_R8((Real8Value)val1)); break; + default: valueStack.Push(Real8Value.CreateUnknown()); break; + } + } + void Emulate_Add(Instruction instr) { var val2 = valueStack.Pop(); var val1 = valueStack.Pop(); @@ -1064,6 +1098,8 @@ namespace de4dot.blocks.cflow { valueStack.Push(Int32Value.Ceq((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) valueStack.Push(Int64Value.Ceq((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Ceq((Real8Value)val1, (Real8Value)val2)); else if (val1.IsNull() && val2.IsNull()) valueStack.Push(Int32Value.One); else @@ -1078,6 +1114,8 @@ namespace de4dot.blocks.cflow { valueStack.Push(Int32Value.Cgt((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) valueStack.Push(Int64Value.Cgt((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Cgt((Real8Value)val1, (Real8Value)val2)); else valueStack.Push(Int32Value.CreateUnknownBool()); } @@ -1090,6 +1128,8 @@ namespace de4dot.blocks.cflow { valueStack.Push(Int32Value.Cgt_Un((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) valueStack.Push(Int64Value.Cgt_Un((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Cgt_Un((Real8Value)val1, (Real8Value)val2)); else valueStack.Push(Int32Value.CreateUnknownBool()); } @@ -1102,6 +1142,8 @@ namespace de4dot.blocks.cflow { valueStack.Push(Int32Value.Clt((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) valueStack.Push(Int64Value.Clt((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Clt((Real8Value)val1, (Real8Value)val2)); else valueStack.Push(Int32Value.CreateUnknownBool()); } @@ -1114,6 +1156,8 @@ namespace de4dot.blocks.cflow { valueStack.Push(Int32Value.Clt_Un((Int32Value)val1, (Int32Value)val2)); else if (val1.IsInt64() && val2.IsInt64()) valueStack.Push(Int64Value.Clt_Un((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Clt_Un((Real8Value)val1, (Real8Value)val2)); else valueStack.Push(Int32Value.CreateUnknownBool()); } diff --git a/de4dot.blocks/cflow/Int32Value.cs b/de4dot.blocks/cflow/Int32Value.cs index 0fb8d400..bdb60482 100644 --- a/de4dot.blocks/cflow/Int32Value.cs +++ b/de4dot.blocks/cflow/Int32Value.cs @@ -331,6 +331,24 @@ namespace de4dot.blocks.cflow { return new Int64Value((long)(uint)a.Value, a.ValidMask | (Int64Value.NO_UNKNOWN_BITS << 32)); } + public static Real8Value Conv_R_Un(Int32Value a) { + if (a.AllBitsValid()) + return new Real8Value((float)(uint)a.Value); + return Real8Value.CreateUnknown(); + } + + public static Real8Value Conv_R4(Int32Value a) { + if (a.AllBitsValid()) + return new Real8Value((float)(int)a.Value); + return Real8Value.CreateUnknown(); + } + + public static Real8Value Conv_R8(Int32Value a) { + if (a.AllBitsValid()) + return new Real8Value((double)(int)a.Value); + return Real8Value.CreateUnknown(); + } + public static Int32Value Add(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) return new Int32Value(a.Value + b.Value); @@ -556,7 +574,7 @@ namespace de4dot.blocks.cflow { return new Int32Value((int)((uint)a.Value >> shift), validMask); } - static Int32Value Create(Bool3 b) { + public static Int32Value Create(Bool3 b) { switch (b) { case Bool3.False: return Zero; case Bool3.True: return One; diff --git a/de4dot.blocks/cflow/Int64Value.cs b/de4dot.blocks/cflow/Int64Value.cs index 2ead373c..20303704 100644 --- a/de4dot.blocks/cflow/Int64Value.cs +++ b/de4dot.blocks/cflow/Int64Value.cs @@ -224,6 +224,24 @@ namespace de4dot.blocks.cflow { return a; } + public static Real8Value Conv_R_Un(Int64Value a) { + if (a.AllBitsValid()) + return new Real8Value((float)(ulong)a.Value); + return Real8Value.CreateUnknown(); + } + + public static Real8Value Conv_R4(Int64Value a) { + if (a.AllBitsValid()) + return new Real8Value((float)(long)a.Value); + return Real8Value.CreateUnknown(); + } + + public static Real8Value Conv_R8(Int64Value a) { + if (a.AllBitsValid()) + return new Real8Value((double)(long)a.Value); + return Real8Value.CreateUnknown(); + } + public static Int64Value Add(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) return new Int64Value(a.Value + b.Value); @@ -449,32 +467,24 @@ namespace de4dot.blocks.cflow { return new Int64Value((long)((ulong)a.Value >> shift), validMask); } - static Int32Value Create(Bool3 b) { - switch (b) { - case Bool3.False: return Int32Value.Zero; - case Bool3.True: return Int32Value.One; - default: return Int32Value.CreateUnknownBool(); - } - } - public static Int32Value Ceq(Int64Value a, Int64Value b) { - return Create(CompareEq(a, b)); + return Int32Value.Create(CompareEq(a, b)); } public static Int32Value Cgt(Int64Value a, Int64Value b) { - return Create(CompareGt(a, b)); + return Int32Value.Create(CompareGt(a, b)); } public static Int32Value Cgt_Un(Int64Value a, Int64Value b) { - return Create(CompareGt_Un(a, b)); + return Int32Value.Create(CompareGt_Un(a, b)); } public static Int32Value Clt(Int64Value a, Int64Value b) { - return Create(CompareLt(a, b)); + return Int32Value.Create(CompareLt(a, b)); } public static Int32Value Clt_Un(Int64Value a, Int64Value b) { - return Create(CompareLt_Un(a, b)); + return Int32Value.Create(CompareLt_Un(a, b)); } public static Bool3 CompareEq(Int64Value a, Int64Value b) { diff --git a/de4dot.blocks/cflow/Real8Value.cs b/de4dot.blocks/cflow/Real8Value.cs index 77206e5a..f7e17a35 100644 --- a/de4dot.blocks/cflow/Real8Value.cs +++ b/de4dot.blocks/cflow/Real8Value.cs @@ -168,6 +168,100 @@ namespace de4dot.blocks.cflow { return Int64Value.CreateUnknown(); } + public static Real8Value Conv_R_Un(Real8Value a) { + return CreateUnknown(); + } + + public static Real8Value Conv_R4(Real8Value a) { + if (a.IsValid) + return new Real8Value((float)a.Value); + return CreateUnknown(); + } + + public static Real8Value Conv_R8(Real8Value a) { + return a; + } + + public static Int32Value Ceq(Real8Value a, Real8Value b) { + return Int32Value.Create(CompareEq(a, b)); + } + + public static Int32Value Cgt(Real8Value a, Real8Value b) { + return Int32Value.Create(CompareGt(a, b)); + } + + public static Int32Value Cgt_Un(Real8Value a, Real8Value b) { + return Int32Value.Create(CompareGt_Un(a, b)); + } + + public static Int32Value Clt(Real8Value a, Real8Value b) { + return Int32Value.Create(CompareLt(a, b)); + } + + public static Int32Value Clt_Un(Real8Value a, Real8Value b) { + return Int32Value.Create(CompareLt_Un(a, b)); + } + + public static Bool3 CompareEq(Real8Value a, Real8Value b) { + if (a.IsValid && b.IsValid) + return a.Value == b.Value ? Bool3.True : Bool3.False; + return Bool3.Unknown; + } + + public static Bool3 CompareNeq(Real8Value a, Real8Value b) { + if (a.IsValid && b.IsValid) + return a.Value != b.Value ? Bool3.True : Bool3.False; + return Bool3.Unknown; + } + + public static Bool3 CompareGt(Real8Value a, Real8Value b) { + if (a.IsValid && b.IsValid) + return a.Value > b.Value ? Bool3.True : Bool3.False; + return Bool3.Unknown; + } + + public static Bool3 CompareGt_Un(Real8Value a, Real8Value b) { + return Bool3.Unknown; //TODO: + } + + public static Bool3 CompareGe(Real8Value a, Real8Value b) { + if (a.IsValid && b.IsValid) + return a.Value >= b.Value ? Bool3.True : Bool3.False; + return Bool3.Unknown; + } + + public static Bool3 CompareGe_Un(Real8Value a, Real8Value b) { + return Bool3.Unknown; //TODO: + } + + public static Bool3 CompareLe(Real8Value a, Real8Value b) { + if (a.IsValid && b.IsValid) + return a.Value <= b.Value ? Bool3.True : Bool3.False; + return Bool3.Unknown; + } + + public static Bool3 CompareLe_Un(Real8Value a, Real8Value b) { + return Bool3.Unknown; //TODO: + } + + public static Bool3 CompareLt(Real8Value a, Real8Value b) { + if (a.IsValid && b.IsValid) + return a.Value < b.Value ? Bool3.True : Bool3.False; + return Bool3.Unknown; + } + + public static Bool3 CompareLt_Un(Real8Value a, Real8Value b) { + return Bool3.Unknown; //TODO: + } + + public static Bool3 CompareTrue(Real8Value a) { + return Bool3.Unknown; + } + + public static Bool3 CompareFalse(Real8Value a) { + return Bool3.Unknown; + } + public override string ToString() { if (!IsValid) return ""; diff --git a/de4dot.code/DumpedMethodsRestorer.cs b/de4dot.code/DumpedMethodsRestorer.cs index ffd17f25..b7e8224c 100644 --- a/de4dot.code/DumpedMethodsRestorer.cs +++ b/de4dot.code/DumpedMethodsRestorer.cs @@ -61,15 +61,14 @@ namespace de4dot.code { return false; } - public bool HasMethodBody(uint rid) { - return GetDumpedMethod(rid) != null; - } - - public MethodBody GetMethodBody(uint rid, RVA rva, IList parameters) { + public bool GetMethodBody(uint rid, RVA rva, IList parameters, GenericParamContext gpContext, out MethodBody methodBody) { var dm = GetDumpedMethod(rid); - if (dm == null) - return null; - return MethodBodyReader.CreateCilBody(module, dm.code, dm.extraSections, parameters, dm.mhFlags, dm.mhMaxStack, dm.mhCodeSize, dm.mhLocalVarSigTok); + if (dm == null) { + methodBody = null; + return false; + } + methodBody = MethodBodyReader.CreateCilBody(module, dm.code, dm.extraSections, parameters, dm.mhFlags, dm.mhMaxStack, dm.mhCodeSize, dm.mhLocalVarSigTok, gpContext); + return true; } } } diff --git a/de4dot.code/Properties/AssemblyInfo.cs b/de4dot.code/Properties/AssemblyInfo.cs index 84584d69..625f0921 100644 --- a/de4dot.code/Properties/AssemblyInfo.cs +++ b/de4dot.code/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 4787d149..62ec8193 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -73,6 +73,7 @@ + diff --git a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs index 391869e3..96d06c58 100644 --- a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs +++ b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs @@ -29,7 +29,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { TypeDef cliSecureRtType; MethodDef postInitializeMethod; MethodDef initializeMethod; - MethodDef stringDecrypterMethod; + Dictionary stringDecrypterInfos = new Dictionary(); MethodDef loadMethod; bool foundSig; @@ -41,8 +41,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { get { return cliSecureRtType; } } - public MethodDef StringDecrypterMethod { - get { return stringDecrypterMethod; } + public IEnumerable StringDecrypterInfos { + get { return stringDecrypterInfos.Keys; } } public MethodDef PostInitializeMethod { @@ -66,7 +66,11 @@ namespace de4dot.code.deobfuscators.Agile_NET { cliSecureRtType = Lookup(oldOne.cliSecureRtType, "Could not find CliSecureRt type"); postInitializeMethod = Lookup(oldOne.postInitializeMethod, "Could not find postInitializeMethod method"); initializeMethod = Lookup(oldOne.initializeMethod, "Could not find initializeMethod method"); - stringDecrypterMethod = Lookup(oldOne.stringDecrypterMethod, "Could not find stringDecrypterMethod method"); + foreach (var info in oldOne.stringDecrypterInfos.Keys) { + var m = Lookup(info.Method, "Could not find string decrypter method"); + var f = Lookup(info.Field, "Could not find string decrypter field"); + stringDecrypterInfos[new StringDecrypterInfo(m, f)] = true; + } loadMethod = Lookup(oldOne.loadMethod, "Could not find loadMethod method"); foundSig = oldOne.foundSig; } @@ -100,11 +104,11 @@ namespace de4dot.code.deobfuscators.Agile_NET { if (!HasInitializeMethod(type, "_Initialize") && !HasInitializeMethod(type, "_Initialize64")) continue; - stringDecrypterMethod = FindStringDecrypterMethod(type); initializeMethod = calledMethod; postInitializeMethod = FindMethod(type, "System.Void", "PostInitialize", "()"); loadMethod = FindMethod(type, "System.IntPtr", "Load", "()"); cliSecureRtType = type; + FindStringDecrypters(); return true; } } @@ -112,6 +116,15 @@ namespace de4dot.code.deobfuscators.Agile_NET { return false; } + void FindStringDecrypters() { + AddStringDecrypterMethod(FindStringDecrypterMethod(cliSecureRtType)); + } + + void AddStringDecrypterMethod(MethodDef method) { + if (method != null) + stringDecrypterInfos[new StringDecrypterInfo(method)] = true; + } + static string[] requiredFields6 = new string[] { "System.Byte[]", }; @@ -134,7 +147,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { if (cs == null) continue; - stringDecrypterMethod = cs; + AddStringDecrypterMethod(cs); cliSecureRtType = type; return true; } @@ -208,7 +221,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { continue; cliSecureRtType = type; - stringDecrypterMethod = cs; + AddStringDecrypterMethod(cs); return; } } diff --git a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs index fd577753..a3e6a3f9 100644 --- a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs @@ -180,7 +180,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { FindCliSecureAttribute(); cliSecureRtType = new CliSecureRtType(module); cliSecureRtType.Find(ModuleBytes); - stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterMethod); + stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterInfos); stringDecrypter.Find(); resourceDecrypter = new ResourceDecrypter(module); resourceDecrypter.Find(); @@ -246,7 +246,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { base.DeobfuscateBegin(); cliSecureRtType.FindStringDecrypterMethod(); - stringDecrypter.Method = cliSecureRtType.StringDecrypterMethod; + stringDecrypter.AddDecrypterInfos(cliSecureRtType.StringDecrypterInfos); + stringDecrypter.Initialize(); AddAttributesToBeRemoved(cliSecureAttributes, "Obfuscator attribute"); @@ -265,7 +266,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { proxyCallFixer.Find(); - staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0])); + foreach (var info in stringDecrypter.StringDecrypterInfos) + staticStringInliner.Add(info.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0])); DeobfuscatedFile.StringDecryptersAdded(); if (options.DecryptMethods) { @@ -295,6 +297,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { } public override void DeobfuscateMethodEnd(Blocks blocks) { + if (Operations.DecryptStrings != OpDecryptString.None) + stringDecrypter.Deobfuscate(blocks); proxyCallFixer.Deobfuscate(blocks); RemoveStackFrameHelperCode(blocks); base.DeobfuscateMethodEnd(blocks); @@ -310,8 +314,14 @@ namespace de4dot.code.deobfuscators.Agile_NET { } if (CanRemoveStringDecrypterType) { AddTypeToBeRemoved(stringDecrypter.Type, "String decrypter type"); + foreach (var info in stringDecrypter.StringDecrypterInfos) { + if (info.Method.DeclaringType != cliSecureRtType.Type) + AddMethodToBeRemoved(info.Method, "String decrypter method"); + if (info.Field != null && info.Field.DeclaringType != stringDecrypter.Type) + AddFieldToBeRemoved(info.Field, "String decrypter field"); + } if (options.DecryptMethods) - AddTypeToBeRemoved(cliSecureRtType.Type, "Obfuscator type"); + AddTypeToBeRemoved(cliSecureRtType.Type ?? stringDecrypter.KeyArrayFieldType, "Obfuscator type"); } if (options.DecryptMethods) { AddResources("Obfuscator protection files"); @@ -327,8 +337,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { public override IEnumerable GetStringDecrypterMethods() { var list = new List(); - if (stringDecrypter.Method != null) - list.Add(stringDecrypter.Method.MDToken.ToInt32()); + foreach (var info in stringDecrypter.StringDecrypterInfos) + list.Add(info.Method.MDToken.ToInt32()); return list; } diff --git a/de4dot.code/deobfuscators/Agile_NET/ResourceDecrypter.cs b/de4dot.code/deobfuscators/Agile_NET/ResourceDecrypter.cs index c696f508..e1854f9c 100644 --- a/de4dot.code/deobfuscators/Agile_NET/ResourceDecrypter.cs +++ b/de4dot.code/deobfuscators/Agile_NET/ResourceDecrypter.cs @@ -62,10 +62,15 @@ namespace de4dot.code.deobfuscators.Agile_NET { FindResourceType(); } - static readonly string[] requiredFields = new string[] { + static readonly string[] requiredFields1 = new string[] { "System.Reflection.Assembly", "System.String[]", }; + static readonly string[] requiredFields2 = new string[] { + "System.Reflection.Assembly", + "System.String[]", + "System.Collections.Hashtable", + }; void FindResourceType() { var cctor = DotNetUtils.GetModuleTypeCctor(module); if (cctor == null) @@ -77,7 +82,9 @@ namespace de4dot.code.deobfuscators.Agile_NET { if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "()")) continue; var type = calledMethod.DeclaringType; - if (!new FieldTypes(type).Exactly(requiredFields)) + var fieldTypes = new FieldTypes(type); + if (!fieldTypes.Exactly(requiredFields1) && + !fieldTypes.Exactly(requiredFields2)) continue; var resolveHandler = DotNetUtils.GetMethod(type, "System.Reflection.Assembly", "(System.Object,System.ResolveEventArgs)"); diff --git a/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs index 91bf919f..d9d975d8 100644 --- a/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs @@ -18,38 +18,53 @@ */ using System; +using System.Collections.Generic; using System.Text; using dnlib.DotNet; +using dnlib.DotNet.Emit; +using de4dot.blocks; namespace de4dot.code.deobfuscators.Agile_NET { class StringDecrypter { ModuleDefMD module; TypeDef stringDecrypterType; - MethodDef stringDecrypterMethod; + FieldDef keyInitField; + FieldDef keyArrayField; + Dictionary stringDecrypterInfos = new Dictionary(); byte[] stringDecrypterKey; public bool Detected { - get { return stringDecrypterMethod != null; } + get { return stringDecrypterInfos.Count != 0; } } public TypeDef Type { get { return stringDecrypterType; } } - public MethodDef Method { - get { return stringDecrypterMethod; } - set { stringDecrypterMethod = value; } + public TypeDef KeyArrayFieldType { + get { return keyArrayField == null ? null : keyArrayField.DeclaringType; } } - public StringDecrypter(ModuleDefMD module, MethodDef stringDecrypterMethod) { + public IEnumerable StringDecrypterInfos { + get { return stringDecrypterInfos.Keys; } + } + + public StringDecrypter(ModuleDefMD module, IEnumerable stringDecrypterMethods) { this.module = module; - this.stringDecrypterMethod = stringDecrypterMethod; + foreach (var sdm in stringDecrypterMethods) + this.stringDecrypterInfos[sdm] = true; } public StringDecrypter(ModuleDefMD module, StringDecrypter oldOne) { this.module = module; stringDecrypterType = Lookup(oldOne.stringDecrypterType, "Could not find stringDecrypterType"); - stringDecrypterMethod = Lookup(oldOne.stringDecrypterMethod, "Could not find stringDecrypterMethod"); + keyInitField = Lookup(oldOne.keyInitField, "Could not find key init field"); + keyArrayField = Lookup(oldOne.keyArrayField, "Could not find key array field"); + foreach (var info in oldOne.stringDecrypterInfos.Keys) { + var m = Lookup(info.Method, "Could not find string decrypter method"); + var f = Lookup(info.Field, "Could not find string decrypter field"); + stringDecrypterInfos[new StringDecrypterInfo(m, f)] = true; + } stringDecrypterKey = oldOne.stringDecrypterKey; } @@ -57,6 +72,11 @@ namespace de4dot.code.deobfuscators.Agile_NET { return DeobUtils.Lookup(module, def, errorMessage); } + public void AddDecrypterInfos(IEnumerable infos) { + foreach (var info in infos) + stringDecrypterInfos[info] = true; + } + public void Find() { stringDecrypterKey = new byte[1] { 0xFF }; foreach (var type in module.Types) { @@ -64,6 +84,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { stringDecrypterType = type; foreach (var field in type.Fields) { if (field.FullName == " ::345" || field.FullName == "/D234 ::345") { + keyInitField = field; stringDecrypterKey = field.InitialValue; break; } @@ -73,6 +94,121 @@ namespace de4dot.code.deobfuscators.Agile_NET { } } + public void Initialize() { + if (keyInitField == null) + return; + + foreach (var type in module.Types) { + var cctor = type.FindStaticConstructor(); + if (cctor == null) + continue; + keyArrayField = GetKeyArrayField(cctor, keyInitField); + if (keyArrayField != null) + break; + } + if (keyArrayField == null) + return; + + foreach (var type in module.GetTypes()) { + FieldDef field; + var method = FindStringDecrypters(type, keyArrayField, out field); + if (method == null) + continue; + + stringDecrypterInfos[new StringDecrypterInfo(method, field)] = true; + } + } + + static FieldDef GetKeyArrayField(MethodDef method, FieldDef keyInitField) { + if (method == null || method.Body == null) + return null; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count; i++) { + if (instrs[i].OpCode.Code != Code.Ldtoken) + continue; + + i++; + for (; i < instrs.Count; i++) { + var instr = instrs[i]; + if (instr.OpCode.Code != Code.Stsfld) + continue; + var field = instr.Operand as FieldDef; + if (field == null || !field.IsStatic || field.DeclaringType != method.DeclaringType) + continue; + if (field.FieldSig.GetFieldType().GetFullName() != "System.Byte[]") + continue; + return field; + } + } + return null; + } + + static MethodDef FindStringDecrypters(TypeDef type, FieldDef keyArrayField, out FieldDef field) { + FieldDef foundField = null; + foreach (var method in type.Methods) { + if (!method.IsAssembly || !method.IsStatic) + continue; + if (!DotNetUtils.IsMethod(method, "System.String", "(System.String)")) + continue; + if (!method.HasBody) + continue; + + bool accessedArrayField = false; + foreach (var instr in method.Body.Instructions) { + var f = instr.Operand as FieldDef; + accessedArrayField |= f == keyArrayField; + if (f == null || f == keyArrayField || f == foundField) + continue; + if (DotNetUtils.DerivesFromDelegate(f.DeclaringType)) + continue; + if (f.FieldSig.GetFieldType().GetFullName() != "System.Collections.Hashtable" || + foundField != null) + goto exit; + foundField = f; + } + if (!accessedArrayField) + continue; + + field = foundField; + return method; + } + +exit: ; + field = null; + return null; + } + + public void Deobfuscate(Blocks blocks) { + if (!blocks.Method.IsStaticConstructor) + return; + + var decrypterFields = new Dictionary(stringDecrypterInfos.Count); + foreach (var info in stringDecrypterInfos.Keys) { + if (info.Field != null) + decrypterFields[info.Field] = true; + } + + foreach (var block in blocks.MethodBlocks.GetAllBlocks()) { + var instrs = block.Instructions; + for (int i = instrs.Count - 2; i >= 0; i--) { + var newobj = instrs[i]; + if (newobj.OpCode.Code != Code.Newobj) + continue; + var ctor = newobj.Operand as IMethod; + if (ctor == null || ctor.FullName != "System.Void System.Collections.Hashtable::.ctor()") + continue; + var stsfld = instrs[i + 1]; + if (stsfld.OpCode.Code != Code.Stsfld) + continue; + var field = stsfld.Operand as FieldDef; + if (field == null || !decrypterFields.ContainsKey(field)) + continue; + + block.Remove(i, 2); + } + } + } + public string Decrypt(string es) { if (stringDecrypterKey == null) throw new ApplicationException("Trying to decrypt strings when stringDecrypterKey is null (could not find it!)"); diff --git a/de4dot.code/deobfuscators/Agile_NET/StringDecrypterInfo.cs b/de4dot.code/deobfuscators/Agile_NET/StringDecrypterInfo.cs new file mode 100644 index 00000000..52dac2fb --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/StringDecrypterInfo.cs @@ -0,0 +1,52 @@ +/* + Copyright (C) 2011-2014 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 dnlib.DotNet; + +namespace de4dot.code.deobfuscators.Agile_NET { + class StringDecrypterInfo { + public readonly MethodDef Method; + public readonly FieldDef Field; + + public StringDecrypterInfo(MethodDef method) + : this(method, null) { + } + + public StringDecrypterInfo(MethodDef method, FieldDef field) { + this.Method = method; + this.Field = field; + } + + public override int GetHashCode() { + int hash = 0; + if (Method != null) + hash ^= Method.GetHashCode(); + if (Field != null) + hash ^= Field.GetHashCode(); + return hash; + } + + public override bool Equals(object obj) { + var other = obj as StringDecrypterInfo; + return other != null && + Method == other.Method && + Field == other.Field; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs index b9726049..3ee297b9 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs @@ -232,13 +232,14 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { if (numLocals < 0) throw new ApplicationException("Invalid number of locals"); + var gpContext = GenericParamContext.Create(cilMethod); for (int i = 0; i < numLocals; i++) - locals.Add(new Local(ReadTypeRef(reader))); + locals.Add(new Local(ReadTypeRef(reader, gpContext))); return locals; } - TypeSig ReadTypeRef(BinaryReader reader) { + TypeSig ReadTypeRef(BinaryReader reader, GenericParamContext gpContext) { var etype = (ElementType)reader.ReadInt32(); switch (etype) { case ElementType.Void: return module.CorLibTypes.Void; @@ -263,12 +264,12 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { case ElementType.ValueType: case ElementType.Var: case ElementType.MVar: - return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig(); + return (module.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef).ToTypeSig(); case ElementType.GenericInst: etype = (ElementType)reader.ReadInt32(); if (etype == ElementType.ValueType) - return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig(); + return (module.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef).ToTypeSig(); // ElementType.Class return module.CorLibTypes.Object; @@ -299,6 +300,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { if (numExceptions < 0) throw new ApplicationException("Invalid number of exception handlers"); + var gpContext = GenericParamContext.Create(cilMethod); for (int i = 0; i < numExceptions; i++) { var eh = new ExceptionHandler((ExceptionHandlerType)reader.ReadInt32()); eh.TryStart = GetInstruction(reader.ReadInt32()); @@ -306,7 +308,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { eh.HandlerStart = GetInstruction(reader.ReadInt32()); eh.HandlerEnd = GetInstructionEnd(reader.ReadInt32()); if (eh.HandlerType == ExceptionHandlerType.Catch) - eh.CatchType = module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + eh.CatchType = module.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; else if (eh.HandlerType == ExceptionHandlerType.Filter) eh.FilterStart = GetInstruction(reader.ReadInt32()); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs index ac039506..c50eedde 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs @@ -34,12 +34,13 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { } protected override List ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) { + var gpContext = GenericParamContext.Create(cilMethod); var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions)); var instrs = new List(); uint offset = 0; while (reader.BaseStream.Position < reader.BaseStream.Length) { int vmOpCode = reader.ReadUInt16(); - var instr = opCodeDetector.Handlers[vmOpCode].Read(reader, module); + var instr = opCodeDetector.Handlers[vmOpCode].Read(reader, module, gpContext); instr.Offset = offset; offset += (uint)GetInstructionSize(instr); SetCilToVmIndex(instr, instrs.Count); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs index 81475049..4c8b5407 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs @@ -29,7 +29,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { public string Name { get; set; } public OpCodeHandlerSigInfo OpCodeHandlerSigInfo { get; set; } public Predicate Check { get; set; } - public Func Read { get; set; } + public Func Read { get; set; } public bool Detect(UnknownHandlerInfo info) { var sigInfo = OpCodeHandlerSigInfo; @@ -68,7 +68,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { } static partial class OpCodeHandlers { - static Instruction arithmetic_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction arithmetic_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { switch (reader.ReadByte()) { case 0: return OpCodes.Add.ToInstruction(); case 1: return OpCodes.Add_Ovf.ToInstruction(); @@ -91,22 +91,22 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Type System.Reflection.Module::ResolveType(System.Int32)"); } - static Instruction newarr_read(BinaryReader reader, IInstructionOperandResolver resolver) { - return new Instruction(OpCodes.Newarr, resolver.ResolveToken(reader.ReadUInt32())); + static Instruction newarr_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { + return new Instruction(OpCodes.Newarr, resolver.ResolveToken(reader.ReadUInt32(), gpContext)); } - static Instruction box_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction box_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { var instr = new Instruction(); switch (reader.ReadByte()) { case 0: instr.OpCode = OpCodes.Box; break; case 1: instr.OpCode = OpCodes.Unbox_Any; break; default: throw new ApplicationException("Invalid opcode"); } - instr.Operand = resolver.ResolveToken(reader.ReadUInt32()); + instr.Operand = resolver.ResolveToken(reader.ReadUInt32(), gpContext); return instr; } - static Instruction call_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction call_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { var instr = new Instruction(); switch (reader.ReadByte()) { case 0: instr.OpCode = OpCodes.Newobj; break; @@ -114,22 +114,22 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { case 2: instr.OpCode = OpCodes.Callvirt; break; default: throw new ApplicationException("Invalid opcode"); } - instr.Operand = resolver.ResolveToken(reader.ReadUInt32()); + instr.Operand = resolver.ResolveToken(reader.ReadUInt32(), gpContext); return instr; } - static Instruction cast_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction cast_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { var instr = new Instruction(); switch (reader.ReadByte()) { case 0: instr.OpCode = OpCodes.Castclass; break; case 1: instr.OpCode = OpCodes.Isinst; break; default: throw new ApplicationException("Invalid opcode"); } - instr.Operand = resolver.ResolveToken(reader.ReadUInt32()); + instr.Operand = resolver.ResolveToken(reader.ReadUInt32(), gpContext); return instr; } - static Instruction compare_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction compare_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { int type = reader.ReadByte(); Instruction instr = new Instruction(); switch (type) { @@ -202,7 +202,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { new InstructionInfo1 { Type = 11, Second = true, Third = true, OpCode = OpCodes.Conv_Ovf_U_Un }, new InstructionInfo1 { Type = 12, Second = true, Third = true, OpCode = OpCodes.Conv_R_Un }, }; - static Instruction convert_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction convert_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { byte type = reader.ReadByte(); bool second = reader.ReadBoolean(); bool third = reader.ReadBoolean(); @@ -221,7 +221,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return instr; } - static Instruction dup_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction dup_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { switch (reader.ReadByte()) { case 0: return OpCodes.Dup.ToInstruction(); case 1: return OpCodes.Pop.ToInstruction(); @@ -259,7 +259,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { new InstructionInfo2 { First = true, Second = true, Value = 28, OpCode = OpCodes.Ldelem_Ref }, new InstructionInfo2 { First = true, Second = false, Value = 0, OpCode = OpCodes.Ldelem }, }; - static Instruction ldelem_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldelem_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { Instruction instr = null; bool first = reader.ReadBoolean(); bool second = reader.ReadBoolean(); @@ -273,7 +273,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { if (second) instr = new Instruction(info.OpCode); else - instr = new Instruction(info.OpCode, resolver.ResolveToken((uint)value)); + instr = new Instruction(info.OpCode, resolver.ResolveToken((uint)value, gpContext)); break; } if (instr == null) @@ -286,13 +286,13 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags)"); } - static Instruction endfinally_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction endfinally_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { return OpCodes.Endfinally.ToInstruction(); } - static Instruction ldfld_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldfld_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { byte b = reader.ReadByte(); - var field = resolver.ResolveToken(reader.ReadUInt32()) as IField; + var field = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IField; switch (b) { case 0: return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsfld, OpCodes.Ldfld, field)); case 1: return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsflda, OpCodes.Ldflda, field)); @@ -301,11 +301,11 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { } } - static Instruction initobj_read(BinaryReader reader, IInstructionOperandResolver resolver) { - return new Instruction(OpCodes.Initobj, resolver.ResolveToken(reader.ReadUInt32())); + static Instruction initobj_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { + return new Instruction(OpCodes.Initobj, resolver.ResolveToken(reader.ReadUInt32(), gpContext)); } - static Instruction ldloc_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldloc_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { bool isLdarg = reader.ReadBoolean(); ushort index = reader.ReadUInt16(); @@ -322,7 +322,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return instr; } - static Instruction ldloca_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldloca_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { Instruction instr = new Instruction(); if (reader.ReadBoolean()) { instr.OpCode = OpCodes.Ldarga; @@ -336,19 +336,19 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return instr; } - static Instruction ldelema_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldelema_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { return new Instruction(OpCodes.Ldelema, null); } - static Instruction ldlen_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldlen_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { return OpCodes.Ldlen.ToInstruction(); } - static Instruction ldobj_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldobj_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { return new Instruction(OpCodes.Ldobj, null); } - static Instruction ldstr_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldstr_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { return OpCodes.Ldstr.ToInstruction(reader.ReadString()); } @@ -356,8 +356,8 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MemberInfo System.Reflection.Module::ResolveMember(System.Int32)"); } - static Instruction ldtoken_read(BinaryReader reader, IInstructionOperandResolver resolver) { - return new Instruction(OpCodes.Ldtoken, resolver.ResolveToken(reader.ReadUInt32())); + static Instruction ldtoken_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { + return new Instruction(OpCodes.Ldtoken, resolver.ResolveToken(reader.ReadUInt32(), gpContext)); } static bool leave_check(UnknownHandlerInfo info) { @@ -366,12 +366,12 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { !DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MemberInfo System.Reflection.Module::ResolveMember(System.Int32)"); } - static Instruction leave_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction leave_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { int displacement = reader.ReadInt32(); return new Instruction(OpCodes.Leave, new TargetDisplOperand(displacement)); } - static Instruction ldc_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldc_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { switch ((ElementType)reader.ReadByte()) { case ElementType.I4: return Instruction.CreateLdcI4(reader.ReadInt32()); case ElementType.I8: return OpCodes.Ldc_I8.ToInstruction(reader.ReadInt64()); @@ -382,24 +382,24 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { } } - static Instruction ldftn_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ldftn_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { byte code = reader.ReadByte(); uint token = reader.ReadUInt32(); switch (code) { case 0: - return new Instruction(OpCodes.Ldftn, resolver.ResolveToken(token)); + return new Instruction(OpCodes.Ldftn, resolver.ResolveToken(token, gpContext)); case 1: reader.ReadInt32(); // token of newobj .ctor - return new Instruction(OpCodes.Ldvirtftn, resolver.ResolveToken(token)); + return new Instruction(OpCodes.Ldvirtftn, resolver.ResolveToken(token, gpContext)); default: throw new ApplicationException("Invalid opcode"); } } - static Instruction logical_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction logical_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { switch (reader.ReadByte()) { case 0: return OpCodes.And.ToInstruction(); case 1: return OpCodes.Or.ToInstruction(); @@ -425,7 +425,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return false; } - static Instruction nop_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction nop_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { return OpCodes.Nop.ToInstruction(); } @@ -433,7 +433,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodBase System.Reflection.Module::ResolveMethod(System.Int32)"); } - static Instruction ret_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction ret_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { reader.ReadInt32(); // token of current method return OpCodes.Ret.ToInstruction(); } @@ -442,11 +442,11 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return info.ExecuteMethod.Body.Variables.Count == 0; } - static Instruction rethrow_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction rethrow_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { return OpCodes.Rethrow.ToInstruction(); } - static Instruction stloc_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction stloc_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { bool isStarg = reader.ReadBoolean(); ushort index = reader.ReadUInt16(); @@ -464,11 +464,11 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return instr; } - static Instruction stobj_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction stobj_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { return new Instruction(OpCodes.Stobj, null); } - static Instruction switch_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction switch_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { int numTargets = reader.ReadInt32(); int[] targetDispls = new int[numTargets]; for (int i = 0; i < targetDispls.Length; i++) @@ -480,11 +480,11 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return !DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags)"); } - static Instruction throw_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction throw_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { return OpCodes.Throw.ToInstruction(); } - static Instruction neg_read(BinaryReader reader, IInstructionOperandResolver resolver) { + static Instruction neg_read(BinaryReader reader, IInstructionOperandResolver resolver, GenericParamContext gpContext) { switch (reader.ReadByte()) { case 0: return OpCodes.Neg.ToInstruction(); case 1: return OpCodes.Not.ToInstruction(); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM5.bin b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM5.bin new file mode 100644 index 00000000..1177d7fc Binary files /dev/null and b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM5.bin differ diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs index 56900000..1dec73de 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs @@ -53,10 +53,10 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { public readonly int BlockIndex; public int HashIndex; - public HandlerState(List blockSigInfos, int blockIndex, int instructionIndex) { + public HandlerState(List blockSigInfos, int blockIndex, int hashIndex) { this.BlockSigInfos = blockSigInfos; this.BlockIndex = blockIndex; - this.HashIndex = instructionIndex; + this.HashIndex = hashIndex; } public HandlerState Clone() { @@ -164,6 +164,8 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } } + if (nextState == null && findState.VisitedCompositeBlocks.Count != findState.CompositeState.BlockSigInfos.Count) + nextState = GetNextHandlerState(ref findState); if (nextState == null) { if (findState.VisitedCompositeBlocks.Count != findState.CompositeState.BlockSigInfos.Count) return false; @@ -181,6 +183,16 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } } + static HandlerState? GetNextHandlerState(ref FindHandlerState findState) { + for (int i = 0; i < findState.CompositeState.BlockSigInfos.Count; i++) { + if (findState.VisitedCompositeBlocks.ContainsKey(i)) + continue; + return new HandlerState(findState.CompositeState.BlockSigInfos, i, 0); + } + + return null; + } + static bool Compare(ref HandlerState handler, ref HandlerState composite) { var hhashes = handler.BlockSigInfos[handler.BlockIndex].Hashes; int hi = handler.HashIndex; diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs index 11ebea38..488f3e14 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs @@ -81,7 +81,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { foreach (var type in module.Types) { if (!type.IsPublic || !type.IsAbstract) continue; - if (type.HasFields || type.HasProperties || type.HasEvents) + if (type.HasProperties || type.HasEvents) continue; if (type.BaseType == null || type.BaseType.FullName != "System.Object") continue; diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs index 3a090f84..bea0bf1c 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18052 +// Runtime Version:4.0.30319.18444 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -99,5 +99,15 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { return ((byte[])(obj)); } } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] CSVM5 { + get { + object obj = ResourceManager.GetObject("CSVM5", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx index 17159be0..3a91280c 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx @@ -130,4 +130,8 @@ CSVM4.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CSVM5.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmToCilMethodConverter.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmToCilMethodConverter.cs index 9c25af10..b63d8c3c 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmToCilMethodConverter.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmToCilMethodConverter.cs @@ -35,7 +35,8 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { protected override List ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) { var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions)); var instrs = new List(); - var handlerInfoReader = new OpCodeHandlerInfoReader(module); + var gpContext = GenericParamContext.Create(cilMethod); + var handlerInfoReader = new OpCodeHandlerInfoReader(module, gpContext); int numVmInstrs = reader.ReadInt32(); var vmInstrs = new ushort[numVmInstrs]; diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs index dd92c2bc..f61c9691 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs @@ -27,9 +27,11 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { class OpCodeHandlerInfoReader { IInstructionOperandResolver resolver; Dictionary> readHandlers; + readonly GenericParamContext gpContext; - public OpCodeHandlerInfoReader(IInstructionOperandResolver resolver) { + public OpCodeHandlerInfoReader(IInstructionOperandResolver resolver, GenericParamContext gpContext) { this.resolver = resolver; + this.gpContext = gpContext; this.readHandlers = new Dictionary> { { HandlerTypeCode.Add, Handler_Add }, { HandlerTypeCode.Add_Ovf, Handler_Add_Ovf }, @@ -176,7 +178,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } Instruction Handler_Box(BinaryReader reader) { - var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + var type = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; return OpCodes.Box.ToInstruction(type); } @@ -193,17 +195,17 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } Instruction Handler_Call(BinaryReader reader) { - var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + var method = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod; return OpCodes.Call.ToInstruction(method); } Instruction Handler_Callvirt(BinaryReader reader) { - var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + var method = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod; return OpCodes.Callvirt.ToInstruction(method); } Instruction Handler_Castclass(BinaryReader reader) { - var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + var type = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; return OpCodes.Castclass.ToInstruction(type); } @@ -312,12 +314,12 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } Instruction Handler_Initobj(BinaryReader reader) { - var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + var type = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; return OpCodes.Initobj.ToInstruction(type); } Instruction Handler_Isinst(BinaryReader reader) { - var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + var type = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; return OpCodes.Isinst.ToInstruction(type); } @@ -349,17 +351,17 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } Instruction Handler_Ldfld_Ldsfld(BinaryReader reader) { - var field = resolver.ResolveToken(reader.ReadUInt32()) as IField; + var field = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IField; return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsfld, OpCodes.Ldfld, field)); } Instruction Handler_Ldflda_Ldsflda(BinaryReader reader) { - var field = resolver.ResolveToken(reader.ReadUInt32()) as IField; + var field = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IField; return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsflda, OpCodes.Ldflda, field)); } Instruction Handler_Ldftn(BinaryReader reader) { - var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + var method = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod; return OpCodes.Ldftn.ToInstruction(method); } @@ -384,12 +386,12 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } Instruction Handler_Ldtoken(BinaryReader reader) { - var member = resolver.ResolveToken(reader.ReadUInt32()) as ITokenOperand; + var member = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITokenOperand; return OpCodes.Ldtoken.ToInstruction(member); } Instruction Handler_Ldvirtftn(BinaryReader reader) { - var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + var method = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod; reader.ReadUInt32(); return OpCodes.Ldvirtftn.ToInstruction(method); } @@ -415,12 +417,12 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } Instruction Handler_Newarr(BinaryReader reader) { - var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + var type = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; return OpCodes.Newarr.ToInstruction(type); } Instruction Handler_Newobj(BinaryReader reader) { - var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + var method = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod; return OpCodes.Newobj.ToInstruction(method); } @@ -449,7 +451,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } Instruction Handler_Ret(BinaryReader reader) { - var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + var method = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod; return OpCodes.Ret.ToInstruction(); } @@ -478,7 +480,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } Instruction Handler_Stfld_Stsfld(BinaryReader reader) { - var field = resolver.ResolveToken(reader.ReadUInt32()) as IField; + var field = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as IField; return new Instruction(null, new FieldInstructionOperand(OpCodes.Stsfld, OpCodes.Stfld, field)); } @@ -517,7 +519,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { } Instruction Handler_Unbox_Any(BinaryReader reader) { - var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + var type = resolver.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; return OpCodes.Unbox_Any.ToInstruction(type); } diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs index f70dc4aa..37fa316d 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs @@ -75,6 +75,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { ReadOpCodeHandlerInfos(CsvmResources.CSVM2), ReadOpCodeHandlerInfos(CsvmResources.CSVM3), ReadOpCodeHandlerInfos(CsvmResources.CSVM4), + ReadOpCodeHandlerInfos(CsvmResources.CSVM5), }; static IList ReadOpCodeHandlerInfos(byte[] data) { diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs index a6431820..220044a3 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs @@ -33,11 +33,13 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { public MyDeobfuscator(ModuleDefMD module) { cliSecureRtType = new CliSecureRtType(module); cliSecureRtType.Find(null); - stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterMethod); + stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterInfos); stringDecrypter.Find(); cliSecureRtType.FindStringDecrypterMethod(); - stringDecrypter.Method = cliSecureRtType.StringDecrypterMethod; - staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0])); + stringDecrypter.AddDecrypterInfos(cliSecureRtType.StringDecrypterInfos); + stringDecrypter.Initialize(); + foreach (var info in stringDecrypter.StringDecrypterInfos) + staticStringInliner.Add(info.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0])); } void RestoreMethod(Blocks blocks) { @@ -252,7 +254,8 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { if (cctor == null) continue; requiredFields[0] = type.FullName; - if (!new FieldTypes(type).Exactly(requiredFields)) + var fieldTypes = new FieldTypes(type); + if (!fieldTypes.All(requiredFields)) continue; cflowDeobfuscator.Deobfuscate(cctor); diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/AntiDebugger.cs b/de4dot.code/deobfuscators/CryptoObfuscator/AntiDebugger.cs index c77115c0..5d0a84f5 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/AntiDebugger.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/AntiDebugger.cs @@ -70,7 +70,8 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { !ContainsString(method, "run under") && !ContainsString(method, "run with") && !ContainsString(method, "started under") && - !ContainsString(method, "{0} detected")) + !ContainsString(method, "{0} detected") && + !ContainsString(method, "{0} found")) continue; antiDebuggerType = type; diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/MethodBodyReader.cs b/de4dot.code/deobfuscators/CryptoObfuscator/MethodBodyReader.cs index 760ffcd3..4273dc8c 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/MethodBodyReader.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/MethodBodyReader.cs @@ -28,6 +28,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { class MethodBodyReader : MethodBodyReaderBase { ModuleDefMD module; ushort maxStackSize; + GenericParamContext gpContext; public MethodBodyReader(ModuleDefMD module, IBinaryReader reader) : base(reader) { @@ -35,6 +36,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { } public void Read(MethodDef method) { + this.gpContext = GenericParamContext.Create(method); this.parameters = method.Parameters; SetLocals(GetLocals(method)); @@ -58,15 +60,15 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { } protected override IField ReadInlineField(Instruction instr) { - return module.ResolveToken(reader.ReadUInt32()) as IField; + return module.ResolveToken(reader.ReadUInt32(), gpContext) as IField; } protected override IMethod ReadInlineMethod(Instruction instr) { - return module.ResolveToken(reader.ReadUInt32()) as IMethod; + return module.ResolveToken(reader.ReadUInt32(), gpContext) as IMethod; } protected override MethodSig ReadInlineSig(Instruction instr) { - var sas = module.ResolveStandAloneSig(MDToken.ToRID(reader.ReadUInt32())); + var sas = module.ResolveStandAloneSig(MDToken.ToRID(reader.ReadUInt32()), gpContext); return sas == null ? null : sas.MethodSig; } @@ -75,11 +77,11 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { } protected override ITokenOperand ReadInlineTok(Instruction instr) { - return module.ResolveToken(reader.ReadUInt32()) as ITokenOperand; + return module.ResolveToken(reader.ReadUInt32(), gpContext) as ITokenOperand; } protected override ITypeDefOrRef ReadInlineType(Instruction instr) { - return module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + return module.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; } void ReadExceptionHandlers(int numExceptionHandlers) { @@ -101,7 +103,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { switch (eh.HandlerType) { case ExceptionHandlerType.Catch: - eh.CatchType = module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + eh.CatchType = module.ResolveToken(reader.ReadUInt32(), gpContext) as ITypeDefOrRef; break; case ExceptionHandlerType.Filter: diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/ProxyCallFixer.cs b/de4dot.code/deobfuscators/CryptoObfuscator/ProxyCallFixer.cs index b296ff6c..d8ff5395 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/ProxyCallFixer.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/ProxyCallFixer.cs @@ -110,7 +110,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { MethodDef GetProxyCreateMethod(TypeDef type) { if (DotNetUtils.FindFieldType(type, "System.ModuleHandle", true) == null) return null; - if (type.Fields.Count < 1 || type.Fields.Count > 18) + if (type.Fields.Count < 1 || type.Fields.Count > 20) return null; MethodDef createMethod = null; diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/ResourceDecrypter.cs b/de4dot.code/deobfuscators/CryptoObfuscator/ResourceDecrypter.cs index b4288131..3449cc90 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/ResourceDecrypter.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/ResourceDecrypter.cs @@ -157,7 +157,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { foreach (var method in GetDecrypterMethods(type)) { if (method == null) continue; - if (!new LocalTypes(method).Exactly(requiredLocals_sl)) + if (!new LocalTypes(method).All(requiredLocals_sl)) continue; resourceDecrypterType = type; @@ -180,6 +180,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { } static bool CheckFlipBits(MethodDef method) { + int nots = 0; var instrs = method.Body.Instructions; for (int i = 0; i < instrs.Count - 1; i++) { var ldloc = instrs[i]; @@ -189,14 +190,11 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { if (local == null || local.Type.GetElementType().GetPrimitiveSize() < 0) continue; - var not = instrs[i + 1]; - if (not.OpCode.Code != Code.Not) - continue; - - return true; + if (instrs[i + 1].OpCode.Code == Code.Not) + nots++; } - return false; + return (nots & 1) == 1; } bool UpdateFlags(MethodDef method, ISimpleDeobfuscator simpleDeobfuscator) { diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/TamperDetection.cs b/de4dot.code/deobfuscators/CryptoObfuscator/TamperDetection.cs index 1a44a36a..79392332 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/TamperDetection.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/TamperDetection.cs @@ -84,7 +84,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { if (!method.IsStatic || !DotNetUtils.IsMethod(method, "System.Void", "()")) return false; - if (type.Methods.Count < 3 || type.Methods.Count > 27) + if (type.Methods.Count < 3 || type.Methods.Count > 31) return false; if (DotNetUtils.GetPInvokeMethod(type, "mscoree", "StrongNameSignatureVerificationEx") != null) { } diff --git a/de4dot.code/deobfuscators/Dotfuscator/Deobfuscator.cs b/de4dot.code/deobfuscators/Dotfuscator/Deobfuscator.cs index c5f3c319..0f95125f 100644 --- a/de4dot.code/deobfuscators/Dotfuscator/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Dotfuscator/Deobfuscator.cs @@ -25,7 +25,7 @@ namespace de4dot.code.deobfuscators.Dotfuscator { public class DeobfuscatorInfo : DeobfuscatorInfoBase { public const string THE_NAME = "Dotfuscator"; public const string THE_TYPE = "df"; - const string DEFAULT_REGEX = @"!^[a-z][a-z0-9]{0,2}$&!^A_[0-9]+$&" + DeobfuscatorBase.DEFAULT_ASIAN_VALID_NAME_REGEX; + const string DEFAULT_REGEX = @"!^(?:eval_)?[a-z][a-z0-9]{0,2}$&!^A_[0-9]+$&" + DeobfuscatorBase.DEFAULT_ASIAN_VALID_NAME_REGEX; public DeobfuscatorInfo() : base(DEFAULT_REGEX) { } diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/DynocodeService.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/DynocodeService.cs index 06371ab5..31cdb538 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/DynocodeService.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/DynocodeService.cs @@ -31,8 +31,10 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { public const int MSG_CALL_MOVE_NEXT = 4; Module reflObfModule; - IEnumerable ienumerable = null; - IEnumerator ienumerator = null; + object ienumerable = null; + object ienumerator = null; + MethodInfo mi_get_Current; + MethodInfo mi_MoveNext; [CreateUserGenericService] public static IUserGenericService Create() { @@ -72,7 +74,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { var ctor = reflObfModule.ResolveMethod((int)ctorToken) as ConstructorInfo; if (ctor == null) throw new ApplicationException(string.Format("Invalid ctor with token: {0:X8}", ctorToken)); - ienumerable = (IEnumerable)ctor.Invoke(args); + ienumerable = ctor.Invoke(args); } void WriteEnumerableField(uint fieldToken, object value) { @@ -83,15 +85,74 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { } void CreateEnumerator() { - ienumerator = ienumerable.GetEnumerator(); + foreach (var method in ienumerable.GetType().GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { + if (method.GetParameters().Length != 0) + continue; + var retType = method.ReturnType; + if (!retType.IsGenericType) + continue; + var genArgs = retType.GetGenericArguments(); + if (genArgs.Length != 1) + continue; + if (genArgs[0] != typeof(int)) + continue; + if (!FindEnumeratorMethods(retType)) + continue; + + ienumerator = method.Invoke(ienumerable, null); + return; + } + + throw new ApplicationException("No GetEnumerator() method found"); + } + + bool FindEnumeratorMethods(Type type) { + mi_get_Current = null; + mi_MoveNext = null; + + foreach (var method in ienumerable.GetType().GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { + if (Is_get_Current(method)) { + if (mi_get_Current != null) + return false; + mi_get_Current = method; + continue; + } + + if (Is_MoveNext(method)) { + if (mi_MoveNext != null) + return false; + mi_MoveNext = method; + continue; + } + } + + return mi_get_Current != null && mi_MoveNext != null; + } + + static bool Is_get_Current(MethodInfo method) { + if (method.GetParameters().Length != 0) + return false; + if (method.ReturnType != typeof(int)) + return false; + + return true; + } + + static bool Is_MoveNext(MethodInfo method) { + if (method.GetParameters().Length != 0) + return false; + if (method.ReturnType != typeof(bool)) + return false; + + return true; } int CallGetCurrent() { - return ienumerator.Current; + return (int)mi_get_Current.Invoke(ienumerator, null); } bool CallMoveNext() { - return ienumerator.MoveNext(); + return (bool)mi_MoveNext.Invoke(ienumerator, null); } public void Dispose() { diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs index da60a999..a16c6729 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs @@ -80,7 +80,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { } public int? ValidStringDecrypterValue { - get { return validStringDecrypterValue;} + get { return validStringDecrypterValue; } } public TypeDef Type { @@ -696,8 +696,8 @@ done: ; if (initValue2 == null || !initValue2.AllBitsValid()) return false; - int loopStart = GetIndexOfCall(instrs, index, leaveIndex, "System.Int32 System.Collections.Generic.IEnumerator`1::get_Current()"); - int loopEnd = GetIndexOfCall(instrs, loopStart, leaveIndex, "System.Boolean System.Collections.IEnumerator::MoveNext()"); + int loopStart = GetIndexOfCall(instrs, index, leaveIndex, "System.Int32", "()"); + int loopEnd = GetIndexOfCall(instrs, loopStart, leaveIndex, "System.Boolean", "()"); if (loopStart < 0 || loopEnd < 0) return false; loopStart++; @@ -720,7 +720,7 @@ done: ; return true; } - static int GetIndexOfCall(IList instrs, int startIndex, int endIndex, string fullMethodName) { + static int GetIndexOfCall(IList instrs, int startIndex, int endIndex, string returnType, string parameters) { if (startIndex < 0 || endIndex < 0) return -1; for (int i = startIndex; i < endIndex; i++) { @@ -728,7 +728,7 @@ done: ; if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt) continue; var method = instr.Operand as IMethod; - if (method == null || method.FullName != fullMethodName) + if (!DotNetUtils.IsMethod(method, returnType, parameters)) continue; return i; diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs index 980d9d03..9e6e6e73 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs @@ -722,13 +722,85 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { decryptStringMethod.Body.ExceptionHandlers.Count >= 2 && new LocalTypes(decryptStringMethod).All(locals35) && CheckTypeFields2(fields35)) { - return "3.5 - 4.1"; + return "3.5 - 4.2"; + } + + ///////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////// + + var fields43 = new string[] { + GetNestedTypeName(0), + GetNestedTypeName(1), + "System.Byte[]", + "System.Int16", + "System.Int32", + "System.Byte[]", + "System.Int32", + "System.Int32", + GetNestedTypeName(2), + }; + var locals43 = CreateLocalsArray( + "System.Boolean", + "System.Byte", + "System.Byte[]", + "System.Char[]", + FindEnumeratorName(decryptStringMethod), + GetNestedTypeName(0), + "System.Diagnostics.StackFrame", + "System.Diagnostics.StackTrace", + "System.Int16", + "System.Int32", + "System.Int64", + "System.IO.Stream", + "System.Reflection.Assembly", + "System.Reflection.AssemblyName", + "System.Reflection.MethodBase", + "System.String", + "System.Text.StringBuilder", + "System.Type" + ); + var olocals43 = CreateLocalsArray( + "System.Int32" + ); + if (otherMethods.Count == 1 && + decryptStringType.NestedTypes.Count == 3 && + DotNetUtils.IsMethod(otherMethods[0], "System.Void", "(System.Byte[],System.Int32,System.Byte[])") && + otherMethods[0].IsPrivate && + otherMethods[0].IsStatic && + new LocalTypes(otherMethods[0]).Exactly(olocals43) && + decryptStringMethod.IsNoInlining && + decryptStringMethod.IsAssembly && + !decryptStringMethod.IsSynchronized && + decryptStringMethod.Body.MaxStack >= 1 && + decryptStringMethod.Body.MaxStack <= 8 && + decryptStringMethod.Body.ExceptionHandlers.Count >= 2 && + new LocalTypes(decryptStringMethod).All(locals43) && + CheckTypeFields2(fields43)) { + return "4.3"; } } return null; } + static string FindEnumeratorName(MethodDef method) { + foreach (var local in method.Body.Variables) { + var gis = local.Type as GenericInstSig; + if (gis == null) + continue; + if (gis.FullName == "System.Collections.Generic.IEnumerator`1") + continue; + if (gis.GenericArguments.Count != 1) + continue; + if (gis.GenericArguments[0].GetFullName() != "System.Int32") + continue; + + return gis.FullName; + } + return null; + } + TypeDef GetNestedType(int n) { var type = stringDecrypter.Type; diff --git a/de4dot.code/deobfuscators/Goliath_NET/DecrypterBase.cs b/de4dot.code/deobfuscators/Goliath_NET/DecrypterBase.cs index a63d5078..ba4cc0b4 100644 --- a/de4dot.code/deobfuscators/Goliath_NET/DecrypterBase.cs +++ b/de4dot.code/deobfuscators/Goliath_NET/DecrypterBase.cs @@ -57,7 +57,7 @@ namespace de4dot.code.deobfuscators.Goliath_NET { } public TypeDef DelegateInitType { - get { return delegateInitType ?? FindDelegateInitType();} + get { return delegateInitType ?? FindDelegateInitType(); } } public TypeDef DelegateType { diff --git a/de4dot.code/deobfuscators/Goliath_NET/StrongNameChecker.cs b/de4dot.code/deobfuscators/Goliath_NET/StrongNameChecker.cs index 402afab8..e647d0aa 100644 --- a/de4dot.code/deobfuscators/Goliath_NET/StrongNameChecker.cs +++ b/de4dot.code/deobfuscators/Goliath_NET/StrongNameChecker.cs @@ -28,7 +28,7 @@ namespace de4dot.code.deobfuscators.Goliath_NET { MethodDef strongNameCheckMethod; public bool Detected { - get { return strongNameType != null;} + get { return strongNameType != null; } } public TypeDef Type { diff --git a/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs b/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs index ca1cd63c..3ee9ce6a 100644 --- a/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs @@ -122,7 +122,6 @@ namespace de4dot.code.deobfuscators.ILProtector { bool emailMe = false; foreach (var info in mainType.RuntimeFileInfos) { var version = info.GetVersion(); - emailMe |= version == null && System.IO.File.Exists(info.PathName); emailMe |= version != null && version == new Version(1, 0, 7, 0); Logger.v("Version: {0} ({1})", version == null ? "UNKNOWN" : version.ToString(), info.PathName); } diff --git a/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs index e6dec7b4..b90f1731 100644 --- a/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs @@ -40,6 +40,50 @@ namespace de4dot.code.deobfuscators.ILProtector { IDecrypter decrypter; bool methodReaderHasDelegateTypeFlag; + static class CodeAllocator { + [DllImport("kernel32")] + static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect); + + const uint PAGE_EXECUTE_READWRITE = 0x40; + const uint MEM_COMMIT = 0x00001000; + const int ALIGNMENT = 0x10; + + static IntPtr currentPage; + static int nextOffset; + static int pageSize; + + public static IntPtr Allocate(byte[] code) { + if (code == null || code.Length == 0) + return IntPtr.Zero; + + var addr = Allocate(code.Length); + Marshal.Copy(code, 0, addr, code.Length); + return addr; + } + + public static IntPtr Allocate(int size) { + if (size <= 0) + return IntPtr.Zero; + + size = (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1); + if (nextOffset + size > pageSize) + AllocNewPage(size); + + var data = new IntPtr(currentPage.ToInt64() + nextOffset); + nextOffset += size; + return data; + } + + static void AllocNewPage(int size) { + size = (size + 0xFFF) & ~0xFFF; + currentPage = VirtualAlloc(IntPtr.Zero, new UIntPtr((uint)size), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (currentPage == IntPtr.Zero) + throw new ApplicationException("VirtualAlloc() failed"); + pageSize = size; + nextOffset = 0; + } + } + interface IDecrypter { byte[] Decrypt(int methodId, uint rid); } @@ -323,7 +367,7 @@ namespace de4dot.code.deobfuscators.ILProtector { pDecryptCallback = new IntPtr(p + IntPtr.Size * 40); } - IntPtr GetStateAddr(object obj) { + public static IntPtr GetStateAddr(object obj) { var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; foreach (var fi in obj.GetType().GetFields(flags)) { if (fi.FieldType == typeof(IntPtr)) @@ -335,7 +379,6 @@ namespace de4dot.code.deobfuscators.ILProtector { public byte[] Decrypt(int methodId, uint rid) { decryptedData = null; currentILBytes = dmd.reflectionModule.ResolveMethod(0x06000000 + (int)rid).GetMethodBody().GetILAsByteArray(); - invoker.DynamicInvoke(new object[1] { methodId }); return decryptedData; } @@ -358,7 +401,7 @@ namespace de4dot.code.deobfuscators.ILProtector { public unsafe DecrypterV2_0_12_0(DynamicMethodsDecrypter dmd) : base(dmd) { getCallerMethodAsILByteArrayDelegate = GetCallerMethodAsILByteArray; - decryptCallbackDelegate = MyDecryptCallback; + decryptCallbackDelegate = DecryptCallback; *(IntPtr*)pGetILBytes = Marshal.GetFunctionPointerForDelegate(getCallerMethodAsILByteArrayDelegate); *(IntPtr*)pDecryptCallback = Marshal.GetFunctionPointerForDelegate(decryptCallbackDelegate); @@ -368,7 +411,7 @@ namespace de4dot.code.deobfuscators.ILProtector { return currentILBytes; } - unsafe bool MyDecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId) { + unsafe bool DecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId) { SaveDecryptedData(pMethodCode, methodSize); return true; } @@ -386,7 +429,7 @@ namespace de4dot.code.deobfuscators.ILProtector { public unsafe DecrypterV2_0_12_3(DynamicMethodsDecrypter dmd) : base(dmd) { getCallerMethodAsILByteArrayDelegate = GetCallerMethodAsILByteArray; - decryptCallbackDelegate = MyDecryptCallback; + decryptCallbackDelegate = DecryptCallback; *(IntPtr*)pGetILBytes = Marshal.GetFunctionPointerForDelegate(getCallerMethodAsILByteArrayDelegate); *(IntPtr*)pDecryptCallback = Marshal.GetFunctionPointerForDelegate(decryptCallbackDelegate); @@ -396,12 +439,176 @@ namespace de4dot.code.deobfuscators.ILProtector { return currentILBytes; } - unsafe bool MyDecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId, IntPtr e) { + unsafe bool DecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId, IntPtr e) { SaveDecryptedData(pMethodCode, methodSize); return true; } } + abstract class DecrypterV2_0_13_0_Base : IDecrypter { + protected readonly DynamicMethodsDecrypter dmd; + protected byte[] currentILBytes; + byte[] decryptedData; + readonly Delegate invoker; + + readonly GetCallerMethodAsILByteArrayDelegate getCallerMethodAsILByteArrayDelegate; + readonly DecryptCallbackDelegate decryptCallbackDelegate; + readonly IgnoreDelegate ignoreDelegate; + + [DllImport("kernel32")] + static extern bool GetModuleHandleEx(uint dwFlags, IntPtr lpModuleName, out IntPtr phModule); + + [return: MarshalAs(UnmanagedType.SafeArray)] + delegate byte[] GetCallerMethodAsILByteArrayDelegate(IntPtr a, int skipFrames, ref int flags, IntPtr d); + unsafe delegate bool DecryptCallbackDelegate(IntPtr a, byte* pMethodCode, int methodSize, int methodId, IntPtr e); + delegate IntPtr IgnoreDelegate(IntPtr a, IntPtr b); + + public unsafe DecrypterV2_0_13_0_Base(DynamicMethodsDecrypter dmd) { + this.dmd = dmd; + this.invoker = (Delegate)dmd.invokerFieldInfo.GetValue(null); + + byte* p = (byte*)DecrypterBaseV2_0_12_x.GetStateAddr(invoker.Target); + byte* pis = GetAddr(*(byte**)p); + p = *(byte**)pis; + byte* pam = *(byte**)(p + IntPtr.Size * 2); + p = *(byte**)(p + ((Environment.Version.Major - 2) / 2 * IntPtr.Size)); + p += IntPtr.Size * 8 + 0x18; + p = LookUp(p, AppDomain.CurrentDomain.Id); + p = *(byte**)(p + IntPtr.Size * 16 + 0x18); + byte* pd = p + IntPtr.Size * 2; + p = *(byte**)(p + IntPtr.Size * 13); + + getCallerMethodAsILByteArrayDelegate = GetCallerMethodAsILByteArray; + decryptCallbackDelegate = DecryptCallback; + ignoreDelegate = IgnoreMethod; + + byte* pm = p + 0x28 * IntPtr.Size; + *(IntPtr*)(p + 0x29 * IntPtr.Size) = Marshal.GetFunctionPointerForDelegate(getCallerMethodAsILByteArrayDelegate); + *(IntPtr*)(p + 0x2A * IntPtr.Size) = Marshal.GetFunctionPointerForDelegate(decryptCallbackDelegate); + if (IntPtr.Size == 4) + *(IntPtr*)(p + 0x2B * IntPtr.Size) = Marshal.GetFunctionPointerForDelegate(ignoreDelegate); + InitCode(GetModuleHandle(pis), pam, pd, pm); + } + + static unsafe byte* GetModuleHandle(byte* addr) { + IntPtr hModule; + if (!GetModuleHandleEx(4, new IntPtr(addr), out hModule)) + throw new ApplicationException("GetModuleHandleEx() failed"); + return (byte*)hModule; + } + + protected unsafe abstract void InitCode(byte* ba, byte* pam, byte* pd, byte* pm); + + static unsafe byte* GetAddr(byte* p) { + if (IntPtr.Size == 4) { + for (int i = 0; i < 20; i++, p++) { + if (*p == 0xA1) + return *(byte**)(p + 1); + } + } + else { + for (int i = 0; i < 20; i++, p++) + if (*p == 0x4C && p[1] == 0x8B && p[2] == 0x15) + return p + 7 + *(int*)(p + 3); + } + return null; + } + + static unsafe byte* LookUp(byte* p, int key) { + p = *(byte**)(p + IntPtr.Size); + p = *(byte**)(p + IntPtr.Size); + + int f1 = 0; + int f2 = IntPtr.Size * 2; + int f3 = IntPtr.Size * 3; + int f4 = IntPtr.Size * 4; + int f5 = IntPtr.Size * 5 + 1; + + byte* res = null; + while (true) { + if (*(p + f5) != 0) + break; + int k = *(int*)(p + f3); + if (k < key) + p = *(byte**)(p + f2); + else { + res = p; + p = *(byte**)(p + f1); + } + } + return *(byte**)(res + f4); + } + + byte[] aryDummy = new byte[7]; + IntPtr dummy; + public unsafe byte[] Decrypt(int methodId, uint rid) { + fixed (byte* p = &aryDummy[0]) { + dummy = new IntPtr(p); + decryptedData = null; + currentILBytes = dmd.reflectionModule.ResolveMethod(0x06000000 + (int)rid).GetMethodBody().GetILAsByteArray(); + invoker.DynamicInvoke(new object[1] { methodId }); + } + dummy = IntPtr.Zero; + return decryptedData; + } + + byte[] GetCallerMethodAsILByteArray(IntPtr a, int skipFrames, ref int flags, IntPtr d) { + flags = 2; + return currentILBytes; + } + + unsafe bool DecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId, IntPtr e) { + decryptedData = new byte[methodSize]; + Marshal.Copy(new IntPtr(pMethodCode), decryptedData, 0, decryptedData.Length); + return true; + } + + IntPtr IgnoreMethod(IntPtr a, IntPtr b) { + return dummy; + } + } + + class DecrypterV2_0_13_0 : DecrypterV2_0_13_0_Base { + public unsafe DecrypterV2_0_13_0(DynamicMethodsDecrypter dmd) + : base(dmd) { + } + + static readonly byte[] initCode_x86 = new byte[] { + 0x8B, 0xCC, 0x8B, 0x41, 0x04, 0xFF, 0x71, 0x10, + 0xFF, 0x71, 0x0C, 0xFF, 0x71, 0x08, 0xFF, 0x51, + 0x14, 0xC2, 0x14, 0x00, + }; + unsafe delegate void InitCode32Delegate(byte* pppam, byte* m, IntPtr s, byte* pd, byte* f); + unsafe delegate void InitCode64Delegate(byte* pppam, byte* m, IntPtr s, byte* pd); + protected unsafe override void InitCode(byte* ba, byte* pam, byte* pd, byte* pm) { + byte* ppam = (byte*)&pam; + byte* pppam = (byte*)&ppam; + if (IntPtr.Size == 4) { + var del = (InitCode32Delegate)Marshal.GetDelegateForFunctionPointer(CodeAllocator.Allocate(initCode_x86), typeof(InitCode32Delegate)); + del(pppam, pm, new IntPtr(IntPtr.Size * 4), pd, ba + 0x00012500); + } + else { + var del = (InitCode64Delegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(ba + 0x00014CF0), typeof(InitCode64Delegate)); + del(pppam, pm, new IntPtr(IntPtr.Size * 4), pd); + } + } + } + + class DecrypterV2_0_13_1 : DecrypterV2_0_13_0_Base { + public unsafe DecrypterV2_0_13_1(DynamicMethodsDecrypter dmd) + : base(dmd) { + } + + unsafe delegate void InitCodeDelegate(byte* pppam, byte* m, IntPtr s, byte* pd); + protected unsafe override void InitCode(byte* ba, byte* pam, byte* pd, byte* pm) { + int rva = IntPtr.Size == 4 ? 0x00013650 : 0x00016B50; + var del = (InitCodeDelegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(ba + rva), typeof(InitCodeDelegate)); + byte* ppam = (byte*)&pam; + byte* pppam = (byte*)&ppam; + del(pppam, pm, new IntPtr(IntPtr.Size * 4), pd); + } + } + public bool MethodReaderHasDelegateTypeFlag { get { return methodReaderHasDelegateTypeFlag; } } @@ -456,9 +663,13 @@ namespace de4dot.code.deobfuscators.ILProtector { methodReaderHasDelegateTypeFlag = true; if (version < new Version(2, 0, 12, 3)) - return CreateDecrypterV2_0_12_0(); - if (version < new Version(2, 0, 12, 4)) - return CreateDecrypterV2_0_12_3(); + return new DecrypterV2_0_12_0(this); + if (version == new Version(2, 0, 12, 3)) + return new DecrypterV2_0_12_3(this); + if (version == new Version(2, 0, 13, 0)) + return new DecrypterV2_0_13_0(this); + if (version == new Version(2, 0, 13, 1)) + return new DecrypterV2_0_13_1(this); return null; } @@ -503,14 +714,6 @@ namespace de4dot.code.deobfuscators.ILProtector { return new DecrypterV2_0_9_0(this, delegateField); } - IDecrypter CreateDecrypterV2_0_12_0() { - return new DecrypterV2_0_12_0(this); - } - - IDecrypter CreateDecrypterV2_0_12_3() { - return new DecrypterV2_0_12_3(this); - } - static readonly byte[] ilpPublicKeyToken = new byte[8] { 0x20, 0x12, 0xD3, 0xC0, 0x55, 0x1F, 0xE0, 0x3D }; static Assembly GetProtectAssembly() { foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { diff --git a/de4dot.code/deobfuscators/ILProtector/MethodReader.cs b/de4dot.code/deobfuscators/ILProtector/MethodReader.cs index 700cdaf5..a20345ee 100644 --- a/de4dot.code/deobfuscators/ILProtector/MethodReader.cs +++ b/de4dot.code/deobfuscators/ILProtector/MethodReader.cs @@ -32,6 +32,7 @@ namespace de4dot.code.deobfuscators.ILProtector { MethodFlags flags; TypeDef delegateType; bool hasDelegateTypeFlag; + GenericParamContext gpContext; [Flags] enum MethodFlags { @@ -76,7 +77,8 @@ namespace de4dot.code.deobfuscators.ILProtector { this.module = module; } - public void Read() { + public void Read(MethodDef method) { + gpContext = GenericParamContext.Create(method); flags = (MethodFlags)reader.ReadByte(); if (HasDelegateType) { delegateType = Resolve(ReadTypeToken()); @@ -108,7 +110,7 @@ namespace de4dot.code.deobfuscators.ILProtector { } T Resolve(int token) { - return (T)module.ResolveToken(token); + return (T)module.ResolveToken(token, gpContext); } int ReadTypeToken() { @@ -182,7 +184,7 @@ namespace de4dot.code.deobfuscators.ILProtector { var token = reader.ReadUInt32(); if (MDToken.ToTable(token) != Table.StandAloneSig) return null; - var sas = module.ResolveStandAloneSig(MDToken.ToRID(token)); + var sas = module.ResolveStandAloneSig(MDToken.ToRID(token), gpContext); return sas == null ? null : sas.MethodSig; } diff --git a/de4dot.code/deobfuscators/ILProtector/MethodsDecrypterBase.cs b/de4dot.code/deobfuscators/ILProtector/MethodsDecrypterBase.cs index 8b27224a..6bda873d 100644 --- a/de4dot.code/deobfuscators/ILProtector/MethodsDecrypterBase.cs +++ b/de4dot.code/deobfuscators/ILProtector/MethodsDecrypterBase.cs @@ -87,7 +87,7 @@ namespace de4dot.code.deobfuscators.ILProtector { methodInfos.Remove(methodId.Value); var methodReader = new MethodReader(module, methodInfo.data, parameters); methodReader.HasDelegateTypeFlag = MethodReaderHasDelegateTypeFlag; - methodReader.Read(); + methodReader.Read(method); RestoreMethod(method, methodReader); if (methodReader.DelegateType != null) diff --git a/de4dot.code/deobfuscators/ILProtector/RuntimeFileInfo.cs b/de4dot.code/deobfuscators/ILProtector/RuntimeFileInfo.cs index 2cef7981..4f9eff07 100644 --- a/de4dot.code/deobfuscators/ILProtector/RuntimeFileInfo.cs +++ b/de4dot.code/deobfuscators/ILProtector/RuntimeFileInfo.cs @@ -64,6 +64,8 @@ namespace de4dot.code.deobfuscators.ILProtector { new VersionInfo(new Version(2, 0, 12, 0), new byte[] { 0x63, 0x8B, 0x5C, 0xE9, 0x89, 0x83, 0x57, 0x9D, 0xDC, 0xC3, 0xBD, 0xD9, 0xDB, 0x54, 0xBE, 0x66 }), new VersionInfo(new Version(2, 0, 12, 2), new byte[] { 0xD5, 0x46, 0x38, 0xC7, 0x48, 0xF6, 0x3C, 0x1C, 0x1E, 0x7F, 0x3B, 0x7B, 0x5B, 0xE0, 0x49, 0x46 }), new VersionInfo(new Version(2, 0, 12, 3), new byte[] { 0x35, 0xA3, 0x53, 0xE9, 0x9E, 0x30, 0x6E, 0x9C, 0x0F, 0x46, 0x20, 0x9A, 0x91, 0xD2, 0x95, 0x18 }), + new VersionInfo(new Version(2, 0, 13, 0), new byte[] { 0x66, 0x21, 0xA1, 0x1F, 0x8F, 0x4A, 0xD2, 0xF8, 0x68, 0xEE, 0xD5, 0xD9, 0xC8, 0xB8, 0x17, 0xC7 }), + new VersionInfo(new Version(2, 0, 13, 1), new byte[] { 0xDF, 0x7A, 0xBF, 0x8B, 0xAD, 0x2B, 0x94, 0x6F, 0x37, 0xD9, 0x4B, 0xFC, 0x42, 0x7F, 0x0B, 0x37 }), }; static readonly VersionInfo[] versionInfo64 = new VersionInfo[] { @@ -89,6 +91,8 @@ namespace de4dot.code.deobfuscators.ILProtector { new VersionInfo(new Version(2, 0, 12, 0), new byte[] { 0x5F, 0x42, 0xA5, 0x6C, 0x19, 0xC6, 0x73, 0x9E, 0xE6, 0x74, 0x62, 0x3B, 0x8A, 0x51, 0xBB, 0x93 }), new VersionInfo(new Version(2, 0, 12, 2), new byte[] { 0x10, 0x91, 0xED, 0x05, 0x9C, 0x31, 0x0B, 0x63, 0x76, 0xD7, 0x4A, 0xEC, 0xDE, 0x99, 0x6D, 0xD0 }), new VersionInfo(new Version(2, 0, 12, 3), new byte[] { 0x38, 0x86, 0xE0, 0xBF, 0xC6, 0x64, 0xB9, 0xA0, 0x07, 0xED, 0xDB, 0x02, 0x40, 0xD0, 0x57, 0xE8 }), + new VersionInfo(new Version(2, 0, 13, 0), new byte[] { 0xF0, 0x13, 0xC4, 0x6F, 0x31, 0x0F, 0x61, 0xEA, 0x89, 0x1E, 0x8A, 0x95, 0x8C, 0xBE, 0x2E, 0x44 }), + new VersionInfo(new Version(2, 0, 13, 1), new byte[] { 0xD4, 0x71, 0x75, 0xE2, 0xB1, 0xA5, 0xAE, 0xF5, 0x32, 0xD7, 0x72, 0xDE, 0x93, 0xDC, 0x0B, 0x68 }), }; public RuntimeFileInfo(MethodDef protectMethod) { diff --git a/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs b/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs index d1f4808d..28c2dd99 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs @@ -112,6 +112,11 @@ namespace de4dot.code.deobfuscators.MaxtoCode { }, // 526BC020 // 526BDD12 + // 5296E242 + // 52B2B2A3 + // 52B3043C + // 53172907 + // 531729C4 new EncryptionInfo { MagicLo = 0x9A683B87, MagicHi = 0x928ECDA3, @@ -177,6 +182,11 @@ namespace de4dot.code.deobfuscators.MaxtoCode { // 5166DB4F // 526BC020 // 526BDD12 + // 5296E242 + // 52B2B2A3 + // 52B3043C + // 53172907 + // 531729C4 new EncryptionInfo { MagicLo = 0x1A731B13, MagicHi = 0x1723891F, diff --git a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs index 92c01b54..a3eedb04 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs @@ -194,6 +194,10 @@ namespace de4dot.code.deobfuscators.MaxtoCode { decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v5, Decrypt2_v2, Decrypt3_v6, Decrypt1_v9, Decrypt6, Decrypt8_v8, Decrypt9_v9, Decrypt7, Decrypt5 }, new uint[] { 0x513D4492, 0x5113E277 })); decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt3_v6, Decrypt2_v2, Decrypt4_v8, Decrypt1_v10, Decrypt8_v9, Decrypt9_v10, Decrypt6, Decrypt7, Decrypt5 }, new uint[] { 0x526BDD12 })); decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt1_v10, Decrypt4_v8, Decrypt2_v2, Decrypt3_v6, Decrypt6, Decrypt8_v9, Decrypt9_v10, Decrypt7, Decrypt5 }, new uint[] { 0x526BC020 })); + decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt3_v7, Decrypt2_v6, Decrypt4_v9, Decrypt1_v11, Decrypt8_v10, Decrypt11_v1, Decrypt6, Decrypt7, Decrypt5 }, new uint[] { 0x5296E242, 0x52B3043C })); + decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v10, Decrypt1_v12, Decrypt3_v8, Decrypt2_v7, Decrypt6, Decrypt8_v11, Decrypt9_v11, Decrypt7, Decrypt5 }, new uint[] { 0x531729C4 })); + decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt1_v13, Decrypt4_v11, Decrypt2_v8, Decrypt3_v9, Decrypt6, Decrypt8_v11, Decrypt9_v12, Decrypt7, Decrypt5 }, new uint[] { 0x52B2B2A3 })); + decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt2_v9, Decrypt3_v10, Decrypt1_v10, Decrypt4_v12, Decrypt8_v12, Decrypt9_v13, Decrypt6, Decrypt7, Decrypt5 }, new uint[] { 0x53172907 })); break; case EncryptionVersion.Unknown: @@ -404,6 +408,18 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return Decrypt1(encrypted, 0x11, 0x11, 0x400); } + byte[] Decrypt1_v11(byte[] encrypted) { + return Decrypt1(encrypted, 0x13, 0x13, 0x400); + } + + byte[] Decrypt1_v12(byte[] encrypted) { + return Decrypt1(encrypted, 0x12, 0x12, 0x200); + } + + byte[] Decrypt1_v13(byte[] encrypted) { + return Decrypt1(encrypted, 0x11, 0x11, 0x200); + } + byte[] Decrypt1(byte[] encrypted, int keyStart, int keyReset, int keyEnd) { var decrypted = new byte[encrypted.Length]; for (int i = 0, ki = keyStart; i < decrypted.Length; i++) { @@ -434,6 +450,22 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return Decrypt2(encrypted, 0x00FA + 0x63); } + byte[] Decrypt2_v6(byte[] encrypted) { + return Decrypt2(encrypted, 0x00FA + 0x0B); + } + + byte[] Decrypt2_v7(byte[] encrypted) { + return Decrypt2(encrypted, 0x00FA + 0x0E); + } + + byte[] Decrypt2_v8(byte[] encrypted) { + return Decrypt2(encrypted, 0x00FA + 0x0D); + } + + byte[] Decrypt2_v9(byte[] encrypted) { + return Decrypt2(encrypted, 0x00FA + 0x0C); + } + byte[] Decrypt2(byte[] encrypted, int offset) { if ((encrypted.Length & 7) != 0) throw new ApplicationException("Invalid encryption #2 length"); @@ -481,6 +513,22 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return Decrypt3(encrypted, 0x015E + 0x7F); } + byte[] Decrypt3_v7(byte[] encrypted) { + return Decrypt3(encrypted, 0x015E + 0x0D); + } + + byte[] Decrypt3_v8(byte[] encrypted) { + return Decrypt3(encrypted, 0x015E + 0x0F); + } + + byte[] Decrypt3_v9(byte[] encrypted) { + return Decrypt3(encrypted, 0x015E + 0x12); + } + + byte[] Decrypt3_v10(byte[] encrypted) { + return Decrypt3(encrypted, 0x015E + 0x0E); + } + static readonly byte[] decrypt3Shifts = new byte[16] { 5, 11, 14, 21, 6, 20, 17, 29, 4, 10, 3, 2, 7, 1, 26, 18 }; byte[] Decrypt3(byte[] encrypted, int offset) { if ((encrypted.Length & 7) != 0) @@ -542,6 +590,22 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return Decrypt4(encrypted, 9, 9, 0x100); } + byte[] Decrypt4_v9(byte[] encrypted) { + return Decrypt4(encrypted, 0x0B, 0x0B, 0x150); + } + + byte[] Decrypt4_v10(byte[] encrypted) { + return Decrypt4(encrypted, 0x10, 0x10, 0x120); + } + + byte[] Decrypt4_v11(byte[] encrypted) { + return Decrypt4(encrypted, 0x0F, 0x0E, 0x120); + } + + byte[] Decrypt4_v12(byte[] encrypted) { + return Decrypt4(encrypted, 0x0C, 0x0C, 0x150); + } + byte[] Decrypt4(byte[] encrypted, int keyStart, int keyReset, int keyEnd) { var decrypted = new byte[encrypted.Length / 3 * 2 + 1]; @@ -599,6 +663,18 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return Decrypt8(encrypted, 0xA, 0xA, 0x600); } + byte[] Decrypt8_v10(byte[] encrypted) { + return Decrypt8(encrypted, 0x14, 0x14, 0x600); + } + + byte[] Decrypt8_v11(byte[] encrypted) { + return Decrypt8(encrypted, 0x19, 0x19, 0x500); + } + + byte[] Decrypt8_v12(byte[] encrypted) { + return Decrypt8(encrypted, 0x14, 0x14, 0x600); + } + byte[] Decrypt8(byte[] encrypted, int keyStart, int keyReset, int keyEnd) { var decrypted = new byte[encrypted.Length]; int ki = keyStart; @@ -636,6 +712,18 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return Decrypt9(encrypted, 5, 5, 0x510); } + byte[] Decrypt9_v11(byte[] encrypted) { + return Decrypt9(encrypted, 0x19, 0x19, 0x500); + } + + byte[] Decrypt9_v12(byte[] encrypted) { + return Decrypt9(encrypted, 0x19, 0x19, 0x500); + } + + byte[] Decrypt9_v13(byte[] encrypted) { + return Decrypt9(encrypted, 5, 5, 0x510); + } + byte[] Decrypt9(byte[] encrypted, int keyStart, int keyReset, int keyEnd) { var decrypted = new byte[encrypted.Length]; int ki = keyStart; @@ -695,6 +783,38 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return dest; } + byte[] Decrypt11_v1(byte[] encrypted) { + return Decrypt11(encrypted, 5, 5, 0x510); + } + + byte[] Decrypt11(byte[] encrypted, int keyStart, int keyReset, int keyEnd) { + byte[] dest = new byte[encrypted.Length]; + + for (int i = 0, ki = keyStart; i < encrypted.Length; i++, ki++) { + if (ki >= keyEnd) + ki = keyStart; + + byte b; + switch (i % 3) { + case 0: + dest[i] = (byte)(encrypted[i] ^ mcKey.ReadByte(ki)); + break; + + case 1: + b = (byte)(encrypted[i] ^ mcKey.ReadByte(ki)); + dest[i] = (byte)((b << 4) | (b >> 4)); + break; + + case 2: + b = encrypted[i]; + dest[i] = (byte)((b << 4) | (b >> 4)); + break; + } + } + + return dest; + } + byte[] blowfishKey; byte[] GetBlowfishKey() { if (blowfishKey != null) diff --git a/de4dot.code/deobfuscators/MethodsDecrypter.cs b/de4dot.code/deobfuscators/MethodsDecrypter.cs index fd5bdd11..8db05e0e 100644 --- a/de4dot.code/deobfuscators/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/MethodsDecrypter.cs @@ -17,6 +17,8 @@ along with de4dot. If not, see . */ +using System; +using System.Runtime.Remoting; using dnlib.DotNet; using AssemblyData; using de4dot.code.AssemblyClient; @@ -30,6 +32,20 @@ namespace de4dot.code.deobfuscators { } public static DumpedMethods Decrypt(ServerClrVersion serverVersion, string filename, byte[] moduleCctorBytes) { + Exception lastEx = null; + for (int i = 0; i < 5; i++) { + try { + return Decrypt2(serverVersion, filename, moduleCctorBytes); + } + catch (RemotingException ex) { + lastEx = ex; + continue; + } + } + throw lastEx; + } + + static DumpedMethods Decrypt2(ServerClrVersion serverVersion, string filename, byte[] moduleCctorBytes) { using (var client = new NewProcessAssemblyClientFactory(serverVersion).Create(AssemblyServiceType.MethodDecrypter)) { client.Connect(); client.WaitConnected(); diff --git a/de4dot.code/deobfuscators/ProxyCallFixerBase.cs b/de4dot.code/deobfuscators/ProxyCallFixerBase.cs index 2a14920d..238e91c8 100644 --- a/de4dot.code/deobfuscators/ProxyCallFixerBase.cs +++ b/de4dot.code/deobfuscators/ProxyCallFixerBase.cs @@ -155,14 +155,15 @@ namespace de4dot.code.deobfuscators { }); } - protected static bool FixProxyCalls(Dictionary> removeInfos) { + protected bool FixProxyCalls(MethodDef method, Dictionary> removeInfos) { + var gpContext = GenericParamContext.Create(method); foreach (var block in removeInfos.Keys) { var list = removeInfos[block]; var removeIndexes = new List(list.Count); foreach (var info in list) { if (info.IsCall) { var opcode = info.DelegateInfo.callOpcode; - var newInstr = Instruction.Create(opcode, info.DelegateInfo.methodRef); + var newInstr = Instruction.Create(opcode, ReResolve(info.DelegateInfo.methodRef, gpContext)); block.Replace(info.Index, 1, newInstr); } else @@ -174,6 +175,12 @@ namespace de4dot.code.deobfuscators { return removeInfos.Count > 0; } + + IMethod ReResolve(IMethod method, GenericParamContext gpContext) { + if (method.IsMethodSpec || method.IsMemberRef) + method = module.ResolveToken(method.MDToken.Raw, gpContext) as IMethod ?? method; + return method; + } } // Fixes proxy calls that call the delegate inline in the code, eg.: @@ -279,7 +286,7 @@ namespace de4dot.code.deobfuscators { } } - return FixProxyCalls(removeInfos); + return FixProxyCalls(blocks.Method, removeInfos); } protected virtual BlockInstr FindProxyCall(DelegateInfo di, Block block, int index) { @@ -481,7 +488,7 @@ namespace de4dot.code.deobfuscators { } } - return FixProxyCalls(removeInfos); + return FixProxyCalls(blocks.Method, removeInfos); } } diff --git a/de4dot.code/deobfuscators/SmartAssembly/Deobfuscator.cs b/de4dot.code/deobfuscators/SmartAssembly/Deobfuscator.cs index b4c9b6b3..560d5c7e 100644 --- a/de4dot.code/deobfuscators/SmartAssembly/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/SmartAssembly/Deobfuscator.cs @@ -239,7 +239,7 @@ namespace de4dot.code.deobfuscators.SmartAssembly { var attrs2 = new Dictionary(); foreach (var cattr in cattrs) { - if (!DotNetUtils.IsMethod(cattr.Constructor as IMethod, "System.Void", "(System.Int32)")) + if (!DotNetUtils.IsMethod(cattr.Constructor, "System.Void", "(System.Int32)")) continue; var attrType = cattr.AttributeType as TypeDef; if (attrType == null) diff --git a/de4dot.code/deobfuscators/Spices_Net/ResourceNamesRestorer.cs b/de4dot.code/deobfuscators/Spices_Net/ResourceNamesRestorer.cs index 37553866..40846bca 100644 --- a/de4dot.code/deobfuscators/Spices_Net/ResourceNamesRestorer.cs +++ b/de4dot.code/deobfuscators/Spices_Net/ResourceNamesRestorer.cs @@ -17,6 +17,7 @@ along with de4dot. If not, see . */ +using System; using System.Collections.Generic; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -83,12 +84,40 @@ namespace de4dot.code.deobfuscators.Spices_Net { return true; } - public void RenameResources() { - if (resourceManagerType == null && componentResourceManagerType == null) - return; + class ResourceDictionary { + struct Key { + public readonly uint hash; + public readonly string ns; + public Key(uint hash, string ns) { + this.hash = hash; + this.ns = ns; + } - var numToResource = new Dictionary(module.Resources.Count); - foreach (var resource in module.Resources) { + public override int GetHashCode() { + return (int)(hash ^ ns.GetHashCode()); + } + + public override bool Equals(object obj) { + if (!(obj is Key)) + return false; + var other = (Key)obj; + return hash == other.hash && + ns == other.ns; + } + + public override string ToString() { + if (ns == string.Empty) + return string.Format("{0}", hash); + return string.Format("{0}.{1}", ns, hash); + } + } + Dictionary resources = new Dictionary(); + + public int Count { + get { return resources.Count; } + } + + public bool Add(Resource resource) { var name = resource.Name.String; int index = name.LastIndexOf('.'); string ext; @@ -98,35 +127,55 @@ namespace de4dot.code.deobfuscators.Spices_Net { ext = name.Substring(index + 1); uint extNum; if (!uint.TryParse(ext, out extNum)) - continue; - numToResource[extNum] = resource; + return false; + var ns = index < 0 ? string.Empty : name.Substring(0, index); + + resources.Add(new Key(extNum, ns), resource); + return true; } + public Resource GetAndRemove(uint hash, string ns) { + var key = new Key(hash, ns); + Resource resource; + if (resources.TryGetValue(key, out resource)) + resources.Remove(key); + return resource; + } + } + + public void RenameResources() { + if (resourceManagerType == null && componentResourceManagerType == null) + return; + + var rsrcDict = new ResourceDictionary(); + foreach (var resource in module.Resources) + rsrcDict.Add(resource); + if (module.Assembly != null) - Rename(numToResource, "", module.Assembly.Name + ".g"); + Rename(rsrcDict, "", module.Assembly.Name + ".g"); foreach (var type in callsResourceManager.Keys) - Rename(numToResource, type); + Rename(rsrcDict, type); - if (numToResource.Count != 0) { + if (rsrcDict.Count != 0) { foreach (var type in module.GetTypes()) { - if (numToResource.Count == 0) + if (rsrcDict.Count == 0) break; if (!IsWinFormType(type)) continue; - Rename(numToResource, type); + Rename(rsrcDict, type); } } - if (numToResource.Count != 0) { + if (rsrcDict.Count != 0) { foreach (var type in module.GetTypes()) { - if (numToResource.Count == 0) + if (rsrcDict.Count == 0) break; - Rename(numToResource, type); + Rename(rsrcDict, type); } } - if (numToResource.Count != 0) + if (rsrcDict.Count != 0) Logger.e("Couldn't restore all renamed resource names"); } @@ -151,18 +200,17 @@ namespace de4dot.code.deobfuscators.Spices_Net { return false; } - static bool Rename(Dictionary numToResource, TypeDef type) { - return Rename(numToResource, "", type.FullName) || - Rename(numToResource, "", type.FullName + ".g") || - Rename(numToResource, type.Namespace, type.Name) || - Rename(numToResource, type.Namespace, type.Name + ".g"); + static bool Rename(ResourceDictionary rsrcDict, TypeDef type) { + if (!IsWinFormType(type) && Rename(rsrcDict, "", type.FullName)) + return true; + return Rename(rsrcDict, type.Namespace, type.Name); } - static bool Rename(Dictionary numToResource, string ns, string name) { + static bool Rename(ResourceDictionary rsrcDict, string ns, string name) { var resourceName = name + ".resources"; uint hash = GetResourceHash(resourceName); - Resource resource; - if (!numToResource.TryGetValue(hash, out resource)) + var resource = rsrcDict.GetAndRemove(hash, ns); + if (resource == null) return false; int index = resource.Name.String.LastIndexOf('.'); @@ -176,13 +224,12 @@ namespace de4dot.code.deobfuscators.Spices_Net { newName = resourceNamespace + "." + resourceName; } if (resourceNamespace != ns) - return false; + throw new ApplicationException("Invalid resource namespace"); Logger.v("Restoring resource name: '{0}' => '{1}'", Utils.RemoveNewlines(resource.Name), Utils.RemoveNewlines(newName)); resource.Name = newName; - numToResource.Remove(hash); return true; } diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs index eccc159e..9e46e331 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -333,6 +333,9 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { break; } + if (methodsDecrypter.DecrypterTypeVersion != DnrDecrypterType.V1) + return DeobfuscatorInfo.THE_NAME; + if (methodsDecrypter.Method == null) { if (minVer >= 3800) return DeobfuscatorInfo.THE_NAME + " >= 3.8"; @@ -361,6 +364,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { bool compileMethodHasConstant_0x70000000 = DeobUtils.HasInteger(compileMethod, 0x70000000); // 4.0-4.1 DeobfuscatedFile.Deobfuscate(methodsDecrypter.Method); bool hasCorEnableProfilingString = FindString(methodsDecrypter.Method, "Cor_Enable_Profiling"); // 4.1-4.4 + bool hasCatchString = FindString(methodsDecrypter.Method, "catch: "); // <= 4.7 if (compileMethodHasConstant_0x70000000) { if (hasCorEnableProfilingString) @@ -368,7 +372,6 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return DeobfuscatorInfo.THE_NAME + " 4.0"; } if (!hasCorEnableProfilingString) { - // 4.x or 4.5 - 4.6 bool callsReverse = DotNetUtils.CallsMethod(methodsDecrypter.Method, "System.Void System.Array::Reverse(System.Array)"); if (!callsReverse) return DeobfuscatorInfo.THE_NAME + " 4.0 - 4.4"; @@ -382,7 +385,9 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { case 10: if (!hasSymmetricAlgorithm) return DeobfuscatorInfo.THE_NAME + " 4.6"; - return DeobfuscatorInfo.THE_NAME + " 4.7"; + if (hasCatchString) + return DeobfuscatorInfo.THE_NAME + " 4.7"; + return DeobfuscatorInfo.THE_NAME + " 4.8"; } } else { @@ -392,7 +397,9 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { case 9: if (!hasSymmetricAlgorithm) return DeobfuscatorInfo.THE_NAME + " 4.6"; - return DeobfuscatorInfo.THE_NAME + " 4.7"; + if (hasCatchString) + return DeobfuscatorInfo.THE_NAME + " 4.7"; + return DeobfuscatorInfo.THE_NAME + " 4.8"; } } diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs index 4ea0301a..285ed802 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs @@ -24,13 +24,24 @@ using System.Security.Cryptography; using dnlib.DotNet; using dnlib.DotNet.Emit; using de4dot.blocks; +using de4dot.blocks.cflow; namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { + enum DnrDecrypterType { + Unknown, + V1, + V2, + } + class EncryptedResource { ModuleDefMD module; MethodDef resourceDecrypterMethod; EmbeddedResource encryptedDataResource; - byte[] key, iv; + IDecrypter decrypter; + + public DnrDecrypterType DecrypterTypeVersion { + get { return decrypter == null ? DnrDecrypterType.Unknown : decrypter.DecrypterType; } + } public TypeDef Type { get { return resourceDecrypterMethod == null ? null : resourceDecrypterMethod.DeclaringType; } @@ -58,8 +69,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { resourceDecrypterMethod = Lookup(oldOne.resourceDecrypterMethod, "Could not find resource decrypter method"); if (oldOne.encryptedDataResource != null) encryptedDataResource = DotNetUtils.GetResource(module, oldOne.encryptedDataResource.Name.String) as EmbeddedResource; - key = oldOne.key; - iv = oldOne.iv; + this.decrypter = oldOne.decrypter; if (encryptedDataResource == null && oldOne.encryptedDataResource != null) throw new ApplicationException("Could not initialize EncryptedResource"); @@ -69,30 +79,12 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return DeobUtils.Lookup(module, def, errorMessage); } - public bool CouldBeResourceDecrypter(MethodDef method, IEnumerable additionalTypes) { + public bool CouldBeResourceDecrypter(MethodDef method, IList additionalTypes) { return CouldBeResourceDecrypter(method, additionalTypes, true); } - public bool CouldBeResourceDecrypter(MethodDef method, IEnumerable additionalTypes, bool checkResource) { - if (!method.IsStatic) - return false; - if (method.Body == null) - return false; - - var localTypes = new LocalTypes(method); - var requiredTypes = new List { - "System.Byte[]", - "System.IO.BinaryReader", - "System.IO.MemoryStream", - "System.Security.Cryptography.CryptoStream", - "System.Security.Cryptography.ICryptoTransform", - }; - requiredTypes.AddRange(additionalTypes); - if (!localTypes.All(requiredTypes)) - return false; - if (!localTypes.Exists("System.Security.Cryptography.RijndaelManaged") && - !localTypes.Exists("System.Security.Cryptography.AesManaged") && - !localTypes.Exists("System.Security.Cryptography.SymmetricAlgorithm")) + public bool CouldBeResourceDecrypter(MethodDef method, IList additionalTypes, bool checkResource) { + if (GetDecrypterType(method, additionalTypes) == DnrDecrypterType.Unknown) return false; if (checkResource && FindMethodsDecrypterResource(method) == null) @@ -101,6 +93,24 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return true; } + public DnrDecrypterType GuessDecrypterType() { + return GetDecrypterType(resourceDecrypterMethod, null); + } + + static DnrDecrypterType GetDecrypterType(MethodDef method, IList additionalTypes) { + if (method == null || !method.IsStatic || method.Body == null) + return DnrDecrypterType.Unknown; + + if (additionalTypes == null) + additionalTypes = new string[0]; + var localTypes = new LocalTypes(method); + if (DecrypterV1.CouldBeResourceDecrypter(method, localTypes, additionalTypes)) + return DnrDecrypterType.V1; + else if (DecrypterV2.CouldBeResourceDecrypter(method, localTypes, additionalTypes)) + return DnrDecrypterType.V2; + return DnrDecrypterType.Unknown; + } + public void Initialize(ISimpleDeobfuscator simpleDeobfuscator) { if (resourceDecrypterMethod == null) return; @@ -111,10 +121,10 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { if (encryptedDataResource == null) return; - key = ArrayFinder.GetInitializedByteArray(resourceDecrypterMethod, 32); + var key = ArrayFinder.GetInitializedByteArray(resourceDecrypterMethod, 32); if (key == null) throw new ApplicationException("Could not find resource decrypter key"); - iv = ArrayFinder.GetInitializedByteArray(resourceDecrypterMethod, 16); + var iv = ArrayFinder.GetInitializedByteArray(resourceDecrypterMethod, 16); if (iv == null) throw new ApplicationException("Could not find resource decrypter IV"); if (NeedReverse()) @@ -126,6 +136,13 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { iv[i * 2 + 1] = publicKeyToken.Data[i]; } } + + var decrypterType = GetDecrypterType(resourceDecrypterMethod, new string[0]); + switch (decrypterType) { + case DnrDecrypterType.V1: decrypter = new DecrypterV1(iv, key); break; + case DnrDecrypterType.V2: decrypter = new DecrypterV2(iv, key, resourceDecrypterMethod); break; + default: throw new ApplicationException("Unknown decrypter type"); + } } static int[] pktIndexes = new int[16] { 1, 0, 3, 1, 5, 2, 7, 3, 9, 4, 11, 5, 13, 6, 15, 7 }; @@ -162,22 +179,244 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return null; } - public byte[] Decrypt() { - if (encryptedDataResource == null || key == null || iv == null) - throw new ApplicationException("Can't decrypt resource"); - - return DeobUtils.AesDecrypt(encryptedDataResource.GetResourceData(), key, iv); + interface IDecrypter { + DnrDecrypterType DecrypterType { get; } + byte[] Decrypt(EmbeddedResource resource); + byte[] Encrypt(byte[] data); } - public byte[] Encrypt(byte[] data) { - if (key == null || iv == null) - throw new ApplicationException("Can't encrypt resource"); + class DecrypterV1 : IDecrypter { + readonly byte[] key, iv; - using (var aes = new RijndaelManaged { Mode = CipherMode.CBC }) { - using (var transform = aes.CreateEncryptor(key, iv)) { - return transform.TransformFinalBlock(data, 0, data.Length); + public DnrDecrypterType DecrypterType { + get { return DnrDecrypterType.V1; } + } + + public DecrypterV1(byte[] iv, byte[] key) { + this.iv = iv; + this.key = key; + } + + public static bool CouldBeResourceDecrypter(MethodDef method, LocalTypes localTypes, IList additionalTypes) { + var requiredTypes = new List { + "System.Byte[]", + "System.IO.BinaryReader", + "System.IO.MemoryStream", + "System.Security.Cryptography.CryptoStream", + "System.Security.Cryptography.ICryptoTransform", + }; + requiredTypes.AddRange(additionalTypes); + if (!localTypes.All(requiredTypes)) + return false; + if (!localTypes.Exists("System.Security.Cryptography.RijndaelManaged") && + !localTypes.Exists("System.Security.Cryptography.AesManaged") && + !localTypes.Exists("System.Security.Cryptography.SymmetricAlgorithm")) + return false; + + return true; + } + + public byte[] Decrypt(EmbeddedResource resource) { + return DeobUtils.AesDecrypt(resource.GetResourceData(), key, iv); + } + + public byte[] Encrypt(byte[] data) { + using (var aes = new RijndaelManaged { Mode = CipherMode.CBC }) { + using (var transform = aes.CreateEncryptor(key, iv)) { + return transform.TransformFinalBlock(data, 0, data.Length); + } } } } + + class DecrypterV2 : IDecrypter { + readonly byte[] key, iv; + readonly MethodDef method; + List instructions; + readonly List locals; + readonly InstructionEmulator instrEmulator = new InstructionEmulator(); + Local emuLocal; + + public DnrDecrypterType DecrypterType { + get { return DnrDecrypterType.V2; } + } + + public DecrypterV2(byte[] iv, byte[] key, MethodDef method) { + this.iv = iv; + this.key = key; + this.method = method; + this.locals = new List(method.Body.Variables); + if (!Initialize()) + throw new ApplicationException("Could not initialize decrypter"); + } + + public static bool CouldBeResourceDecrypter(MethodDef method, LocalTypes localTypes, IList additionalTypes) { + var requiredTypes = new List { + "System.UInt32", + "System.String", + "System.Int32", + "System.Byte[]", + "System.IO.BinaryReader", + }; + requiredTypes.AddRange(additionalTypes); + if (!localTypes.All(requiredTypes)) + return false; + + return true; + } + + bool Initialize() { + for (int i = 0; i < iv.Length; i++) + key[i] ^= iv[i]; + + var origInstrs = method.Body.Instructions; + + int emuStartIndex; + if (!FindStart(origInstrs, out emuStartIndex, out emuLocal)) + return false; + int emuEndIndex; + if (!FindEnd(origInstrs, emuStartIndex, out emuEndIndex)) + return false; + + int count = emuEndIndex - emuStartIndex + 1; + instructions = new List(count); + for (int i = 0; i < count; i++) + instructions.Add(origInstrs[emuStartIndex + i].Clone()); + + return true; + } + + bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) { + for (int i = 0; i + 8 < instrs.Count; i++) { + if (instrs[i].OpCode.Code != Code.Conv_U) + continue; + if (instrs[i + 1].OpCode.Code != Code.Ldelem_U1) + continue; + if (instrs[i + 2].OpCode.Code != Code.Or) + continue; + if (CheckLocal(instrs[i + 3], false) == null) + continue; + Local local; + if ((local = CheckLocal(instrs[i + 4], true)) == null) + continue; + if (CheckLocal(instrs[i + 5], true) == null) + continue; + if (instrs[i + 6].OpCode.Code != Code.Add) + continue; + if (CheckLocal(instrs[i + 7], false) != local) + continue; + var instr = instrs[i + 8]; + int newStartIndex = i + 8; + if (instr.IsBr()) { + instr = instr.Operand as Instruction; + newStartIndex = instrs.IndexOf(instr); + } + if (newStartIndex < 0 || instr == null) + continue; + if (CheckLocal(instr, true) != local) + continue; + + startIndex = newStartIndex; + tmpLocal = local; + return true; + } + + startIndex = 0; + tmpLocal = null; + return false; + } + + bool FindEnd(IList instrs, int startIndex, out int endIndex) { + for (int i = startIndex; i < instrs.Count; i++) { + var instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + break; + if (instr.IsStloc() && instr.GetLocal(locals) == emuLocal) { + endIndex = i - 1; + return true; + } + } + + endIndex = 0; + return false; + } + + Local CheckLocal(Instruction instr, bool isLdloc) { + if (isLdloc && !instr.IsLdloc()) + return null; + else if (!isLdloc && !instr.IsStloc()) + return null; + + return instr.GetLocal(locals); + } + + public byte[] Decrypt(EmbeddedResource resource) { + var encrypted = resource.GetResourceData(); + var decrypted = new byte[encrypted.Length]; + + uint sum = 0; + for (int i = 0; i < encrypted.Length; i += 4) { + sum = CalculateMagic(sum + ReadUInt32(key, i % key.Length)); + WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); + } + + return decrypted; + } + + uint CalculateMagic(uint input) { + instrEmulator.Initialize(method, method.Parameters, locals, method.Body.InitLocals, false); + instrEmulator.SetLocal(emuLocal, new Int32Value((int)input)); + + foreach (var instr in instructions) + instrEmulator.Emulate(instr); + + var tos = instrEmulator.Pop() as Int32Value; + if (tos == null || !tos.AllBitsValid()) + throw new ApplicationException("Couldn't calculate magic value"); + return (uint)tos.Value; + } + + static uint ReadUInt32(byte[] ary, int index) { + int sizeLeft = ary.Length - index; + if (sizeLeft >= 4) + return BitConverter.ToUInt32(ary, index); + switch (sizeLeft) { + case 1: return ary[index]; + case 2: return (uint)(ary[index] | (ary[index + 1] << 8)); + case 3: return (uint)(ary[index] | (ary[index + 1] << 8) | (ary[index + 2] << 16)); + default: throw new ApplicationException("Can't read data"); + } + } + + static void WriteUInt32(byte[] ary, int index, uint value) { + int sizeLeft = ary.Length - index; + if (sizeLeft >= 1) + ary[index] = (byte)value; + if (sizeLeft >= 2) + ary[index + 1] = (byte)(value >> 8); + if (sizeLeft >= 3) + ary[index + 2] = (byte)(value >> 16); + if (sizeLeft >= 4) + ary[index + 3] = (byte)(value >> 24); + } + + public byte[] Encrypt(byte[] data) { + //TODO: Support re-encryption + Logger.e("Re-encryption is not supported. Assembly will probably crash at runtime."); + return (byte[])data.Clone(); + } + } + + public byte[] Decrypt() { + if (encryptedDataResource == null || decrypter == null) + throw new ApplicationException("Can't decrypt resource"); + return decrypter.Decrypt(encryptedDataResource); + } + + public byte[] Encrypt(byte[] data) { + if (decrypter == null) + throw new ApplicationException("Can't encrypt resource"); + return decrypter.Encrypt(data); + } } } diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MetadataTokenObfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MetadataTokenObfuscator.cs index 825580d2..9c3a8cd9 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MetadataTokenObfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MetadataTokenObfuscator.cs @@ -76,6 +76,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { if (type == null) return; + var gpContext = GenericParamContext.Create(blocks.Method); foreach (var block in blocks.MethodBlocks.GetAllBlocks()) { var instrs = block.Instructions; for (int i = 0; i < instrs.Count - 1; i++) { @@ -98,7 +99,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { uint token = (uint)(int)instrs[i].Operand; instrs[i] = new Instr(OpCodes.Nop.ToInstruction()); - instrs[i + 1] = new Instr(new Instruction(OpCodes.Ldtoken, module.ResolveToken(token) as ITokenOperand)); + instrs[i + 1] = new Instr(new Instruction(OpCodes.Ldtoken, module.ResolveToken(token, gpContext) as ITokenOperand)); } } } diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs index fedb2f96..b0801aa3 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs @@ -57,6 +57,10 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { get { return encryptedResource.Resource; } } + public DnrDecrypterType DecrypterTypeVersion { + get { return encryptedResource.GuessDecrypterType(); } + } + public MethodsDecrypter(ModuleDefMD module) { this.module = module; this.encryptedResource = new EncryptedResource(module); @@ -301,10 +305,10 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { if (instructions[i].OpCode.Code != Code.Ldind_I8) continue; var ldci4 = instructions[i + 1]; - if (!ldci4.IsLdcI4()) - continue; - - return ldci4.GetLdcI4Value(); + if (ldci4.IsLdcI4()) + return ldci4.GetLdcI4Value(); + if (ldci4.OpCode.Code == Code.Ldc_I8) + return (long)ldci4.Operand; } return 0; } diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs index b4f83831..a747dabc 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs @@ -70,21 +70,37 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { if (!DotNetUtils.IsMethod(method, "System.Reflection.Assembly", "(System.Object,System.ResolveEventArgs)") && !DotNetUtils.IsMethod(method, "System.Reflection.Assembly", "(System.Object,System.Object)")) continue; - if (!encryptedResource.CouldBeResourceDecrypter(method, additionalTypes, false)) + var initMethod = GetResourceDecrypterInitMethod(method, additionalTypes, false); + if (initMethod == null) continue; - encryptedResource.Method = method; + encryptedResource.Method = initMethod; return; } } } + MethodDef GetResourceDecrypterInitMethod(MethodDef method, string[] additionalTypes, bool checkResource) { + if (encryptedResource.CouldBeResourceDecrypter(method, additionalTypes, checkResource)) + return method; + + foreach (var calledMethod in DotNetUtils.GetCalledMethods(module, method)) { + if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "()")) + continue; + if (encryptedResource.CouldBeResourceDecrypter(calledMethod, additionalTypes, checkResource)) + return calledMethod; + } + + return null; + } + bool CheckFields(IList fields) { - if (fields.Count != 3) + if (fields.Count != 3 && fields.Count != 4) return false; + int numBools = fields.Count == 3 ? 1 : 2; var fieldTypes = new FieldTypes(fields); - if (fieldTypes.Count("System.Boolean") != 1) + if (fieldTypes.Count("System.Boolean") != numBools) return false; if (fieldTypes.Count("System.Object") == 2) return true; diff --git a/de4dot.code/renamer/DerivedFrom.cs b/de4dot.code/renamer/DerivedFrom.cs index fefc1b8e..abe4ddc4 100644 --- a/de4dot.code/renamer/DerivedFrom.cs +++ b/de4dot.code/renamer/DerivedFrom.cs @@ -40,6 +40,12 @@ namespace de4dot.code.renamer { } public bool Check(MTypeDef type) { + return Check(type, 0); + } + + public bool Check(MTypeDef type, int recurseCount) { + if (recurseCount >= 100) + return false; if (results.ContainsKey(type)) return results[type]; @@ -53,7 +59,7 @@ namespace de4dot.code.renamer { val = false; } else - val = Check(type.baseType.typeDef); + val = Check(type.baseType.typeDef, recurseCount + 1); results[type] = val; return val; diff --git a/de4dot.code/renamer/ExistingNames.cs b/de4dot.code/renamer/ExistingNames.cs index 5f4e1287..a0716f7d 100644 --- a/de4dot.code/renamer/ExistingNames.cs +++ b/de4dot.code/renamer/ExistingNames.cs @@ -62,6 +62,8 @@ namespace de4dot.code.renamer { } public void Merge(ExistingNames other) { + if (this == other) + return; foreach (var key in other.allNames.Keys) allNames[key] = true; } diff --git a/de4dot.code/renamer/Renamer.cs b/de4dot.code/renamer/Renamer.cs index 28d1d7d9..0a72cb7c 100644 --- a/de4dot.code/renamer/Renamer.cs +++ b/de4dot.code/renamer/Renamer.cs @@ -1520,6 +1520,29 @@ namespace de4dot.code.renamer { return null; } + internal static ITypeDefOrRef GetScopeType(TypeSig typeSig) { + if (typeSig == null) + return null; + var scopeType = typeSig.ScopeType; + if (scopeType != null) + return scopeType; + + for (int i = 0; i < 100; i++) { + var nls = typeSig as NonLeafSig; + if (nls == null) + break; + typeSig = nls.Next; + } + + switch (typeSig.GetElementType()) { + case ElementType.MVar: + case ElementType.Var: + return new TypeSpecUser(typeSig); + default: + return null; + } + } + string GetNewPropertyNamePrefix(MethodNameGroup group) { const string defaultVal = "Prop_"; @@ -1527,7 +1550,7 @@ namespace de4dot.code.renamer { if (propType == null) return defaultVal; - var elementType = propType.ScopeType.ToTypeSig(false).RemovePinnedAndModifiers(); + var elementType = GetScopeType(propType).ToTypeSig(false).RemovePinnedAndModifiers(); if (propType is GenericInstSig || elementType is GenericSig) return defaultVal; diff --git a/de4dot.code/renamer/TypeNames.cs b/de4dot.code/renamer/TypeNames.cs index 05ab7a0d..0a9adc28 100644 --- a/de4dot.code/renamer/TypeNames.cs +++ b/de4dot.code/renamer/TypeNames.cs @@ -44,7 +44,7 @@ namespace de4dot.code.renamer { string prefix = GetPrefix(typeRef); - var elementType = typeRef.ScopeType; + var elementType = Renamer.GetScopeType(typeRef); if (elementType == null && IsFnPtrSig(typeRef)) return fnPtrNameCreator.Create(); if (IsGenericParam(elementType)) @@ -113,6 +113,8 @@ namespace de4dot.code.renamer { protected abstract string FixName(string prefix, string name); public virtual TypeNames Merge(TypeNames other) { + if (this == other) + return this; foreach (var pair in other.typeNames) { NameCreator nc; if (typeNames.TryGetValue(pair.Key, out nc)) diff --git a/de4dot.code/renamer/VariableNameState.cs b/de4dot.code/renamer/VariableNameState.cs index 4e2ea22d..029c433d 100644 --- a/de4dot.code/renamer/VariableNameState.cs +++ b/de4dot.code/renamer/VariableNameState.cs @@ -61,6 +61,8 @@ namespace de4dot.code.renamer { } public VariableNameState Merge(VariableNameState other) { + if (this == other) + return this; existingVariableNames.Merge(other.existingVariableNames); existingMethodNames.Merge(other.existingMethodNames); existingPropertyNames.Merge(other.existingPropertyNames); diff --git a/de4dot.code/renamer/asmmodules/MemberRefFinder.cs b/de4dot.code/renamer/asmmodules/MemberRefFinder.cs index 3aeff2a9..71425395 100644 --- a/de4dot.code/renamer/asmmodules/MemberRefFinder.cs +++ b/de4dot.code/renamer/asmmodules/MemberRefFinder.cs @@ -285,9 +285,44 @@ namespace de4dot.code.renamer.asmmodules { void Add(DeclSecurity decl) { if (decl == null) return; + Add(decl.SecurityAttributes); Add(decl.CustomAttributes); } + void Add(IEnumerable secAttrs) { + if (secAttrs == null) + return; + foreach (var secAttr in secAttrs) + Add(secAttr); + } + + void Add(SecurityAttribute secAttr) { + if (secAttr == null) + return; + Add(secAttr.AttributeType); + Add(secAttr.NamedArguments); + } + + void Add(ITypeDefOrRef tdr) { + var td = tdr as TypeDef; + if (td != null) { + Add(td); + return; + } + + var tr = tdr as TypeRef; + if (tr != null) { + Add(tr); + return; + } + + var ts = tdr as TypeSpec; + if (ts != null) { + Add(ts); + return; + } + } + void Add(IEnumerable eds) { if (eds == null) return; @@ -326,6 +361,7 @@ namespace de4dot.code.renamer.asmmodules { Add(fd.CustomAttributes); Add(fd.Signature); Add(fd.DeclaringType); + Add(fd.MarshalType); } void Add(IEnumerable gps) { @@ -489,6 +525,22 @@ namespace de4dot.code.renamer.asmmodules { return; Add(pd.DeclaringMethod); Add(pd.CustomAttributes); + Add(pd.MarshalType); + } + + void Add(MarshalType mt) { + if (mt == null) + return; + + switch (mt.NativeType) { + case NativeType.SafeArray: + Add(((SafeArrayMarshalType)mt).UserDefinedSubType); + break; + + case NativeType.CustomMarshaler: + Add(((CustomMarshalType)mt).CustomMarshaler); + break; + } } void Add(IEnumerable mos) { diff --git a/de4dot.cui/Program.cs b/de4dot.cui/Program.cs index 54610085..44885e47 100644 --- a/de4dot.cui/Program.cs +++ b/de4dot.cui/Program.cs @@ -74,7 +74,7 @@ namespace de4dot.cui { Logger.n(""); Logger.n("de4dot v{0} Copyright (C) 2011-2014 de4dot@gmail.com", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version); - Logger.n("Latest version and source code: https://bitbucket.org/0xd4d/de4dot"); + Logger.n("Latest version and source code: https://github.com/0xd4d/de4dot"); Logger.n(""); var options = new FilesDeobfuscator.Options(); diff --git a/de4dot.cui/Properties/AssemblyInfo.cs b/de4dot.cui/Properties/AssemblyInfo.cs index b208b36e..981bd097 100644 --- a/de4dot.cui/Properties/AssemblyInfo.cs +++ b/de4dot.cui/Properties/AssemblyInfo.cs @@ -30,7 +30,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] [assembly: InternalsVisibleTo("de4dot, PublicKey=00240000048000009400000006020000002400005253413100040000010001007b5ffd8f48f1397cd4e21c9e30a5cb36b2c013d6f20688c90e3f0c2d24e6d67cbeea7a6ec3faf9ba081f3d6b6fbe389677adbb8337d3a16187cd13b16a34008a22b89089da41c4a08fd35615c77de0827adcca6d49b08c0ed3e0404a1c44b7d083be614acb1779e4fb275e14427f3687f375d03f3b465c8a6cdeebd1f8c7f4ea")] [assembly: InternalsVisibleTo("de4dot-x64, PublicKey=00240000048000009400000006020000002400005253413100040000010001007b5ffd8f48f1397cd4e21c9e30a5cb36b2c013d6f20688c90e3f0c2d24e6d67cbeea7a6ec3faf9ba081f3d6b6fbe389677adbb8337d3a16187cd13b16a34008a22b89089da41c4a08fd35615c77de0827adcca6d49b08c0ed3e0404a1c44b7d083be614acb1779e4fb275e14427f3687f375d03f3b465c8a6cdeebd1f8c7f4ea")] diff --git a/de4dot.mdecrypt/DynamicMethodsDecrypter.cs b/de4dot.mdecrypt/DynamicMethodsDecrypter.cs index 24fa5816..58557ff5 100644 --- a/de4dot.mdecrypt/DynamicMethodsDecrypter.cs +++ b/de4dot.mdecrypt/DynamicMethodsDecrypter.cs @@ -596,6 +596,9 @@ namespace de4dot.mdecrypt { new PatchInfo(0x000110A5, new byte[] { 0x33, 0xC0, 0xC2, 0x04, 0x00 }, new byte[] { 0xE9, 0x36, 0x3A, 0x00, 0x00 }), new PatchInfo(0x000110AF, new byte[] { 0x33, 0xC0, 0xC2, 0x04, 0x00 }, new byte[] { 0xE9, 0x4C, 0x3C, 0x00, 0x00 }), new PatchInfo(0x000110AA, new byte[] { 0x33, 0xC0, 0xC2, 0x04, 0x00 }, new byte[] { 0xE9, 0xF1, 0x3A, 0x00, 0x00 }), + new PatchInfo(0x00011019, new byte[] { 0x33, 0xC0, 0xC2, 0x04, 0x00 }, new byte[] { 0xE9, 0x12, 0x4B, 0x00, 0x00 }), + new PatchInfo(0x00011019, new byte[] { 0x33, 0xC0, 0xC2, 0x04, 0x00 }, new byte[] { 0xE9, 0x02, 0x4B, 0x00, 0x00 }), + new PatchInfo(0x00011019, new byte[] { 0x33, 0xC0, 0xC2, 0x04, 0x00 }, new byte[] { 0xE9, 0xA2, 0x4B, 0x00, 0x00 }), }; static unsafe bool PatchCM(IntPtr addr, IntPtr origValue, IntPtr newValue) { diff --git a/de4dot.mdecrypt/Properties/AssemblyInfo.cs b/de4dot.mdecrypt/Properties/AssemblyInfo.cs index a148ef31..0a392fad 100644 --- a/de4dot.mdecrypt/Properties/AssemblyInfo.cs +++ b/de4dot.mdecrypt/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/de4dot/Properties/AssemblyInfo.cs b/de4dot/Properties/AssemblyInfo.cs index aea00aa1..27c485e3 100644 --- a/de4dot/Properties/AssemblyInfo.cs +++ b/de4dot/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.3.3405")] -[assembly: AssemblyFileVersion("3.0.3.3405")] +[assembly: AssemblyVersion("3.1.41592.3405")] +[assembly: AssemblyFileVersion("3.1.41592.3405")] diff --git a/dnlib b/dnlib index d854e20b..4abc5988 160000 --- a/dnlib +++ b/dnlib @@ -1 +1 @@ -Subproject commit d854e20b2d4245245efc19e676e6862cdbd162f6 +Subproject commit 4abc5988d3cc4e8ae4cb7ac77f3018ffb54200c7