From b65e9a59dfbe793363a599494b4ea501c7e179dc Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 8 Nov 2013 11:05:17 +0100 Subject: [PATCH] Support latest Agile.NET --- de4dot.code/de4dot.code.csproj | 35 +- .../Agile_NET/CliSecureRtType.cs | 28 +- .../deobfuscators/Agile_NET/Deobfuscator.cs | 23 +- .../{v1 => }/CilOperandInstructionRestorer.cs | 70 +- .../Agile_NET/vm/{v1 => }/CsvmDataReader.cs | 2 +- .../Agile_NET/vm/{v1 => }/CsvmMethodData.cs | 2 +- .../vm/CsvmToCilMethodConverterBase.cs | 417 ++++++++++ .../vm/{v1/VmOperands.cs => VmOperand.cs} | 56 +- .../deobfuscators/Agile_NET/vm/v1/Csvm.cs | 2 +- .../vm/v1/CsvmToCilMethodConverter.cs | 397 +-------- .../Agile_NET/vm/v1/OpCodeHandler.cs | 139 ++-- .../Agile_NET/vm/v1/OpCodeHandlers.cs | 2 +- .../vm/v1/VmOpCodeHandlerDetector.cs | 2 +- .../Agile_NET/vm/v2/CSVM1_v2.bin | Bin 0 -> 9520 bytes .../Agile_NET/vm/v2/CSVM2_v2.bin | Bin 0 -> 9520 bytes .../Agile_NET/vm/v2/CSVM3_v2.bin | Bin 0 -> 9580 bytes .../vm/v2/CompositeHandlerDetector.cs | 335 ++++++++ .../Agile_NET/vm/v2/CompositeOpCodeHandler.cs | 67 ++ .../deobfuscators/Agile_NET/vm/v2/Csvm.cs | 164 ++++ .../deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs | 779 ++++++++++++++++++ .../Agile_NET/vm/v2/CsvmResources.Designer.cs | 93 +++ .../Agile_NET/vm/v2/CsvmResources.resx | 130 +++ .../vm/v2/CsvmToCilMethodConverter.cs | 64 ++ .../Agile_NET/vm/v2/HandlerTypeCode.cs | 102 +++ .../Agile_NET/vm/v2/MethodFinder.cs | 109 +++ .../Agile_NET/vm/v2/MethodSigInfoCreator.cs | 737 +++++++++++++++++ .../Agile_NET/vm/v2/OpCodeHandler.cs | 38 + .../Agile_NET/vm/v2/OpCodeHandlerInfo.cs | 123 +++ .../vm/v2/OpCodeHandlerInfoReader.cs | 528 ++++++++++++ .../Agile_NET/vm/v2/OpCodeHandlerInfos.cs | 98 +++ .../deobfuscators/Agile_NET/vm/v2/VmOpCode.cs | 44 + .../vm/v2/VmOpCodeHandlerDetector.cs | 297 +++++++ de4dot.code/deobfuscators/NullStream.cs | 87 ++ de4dot.mdecrypt/DynamicMethodsDecrypter.cs | 94 ++- 34 files changed, 4497 insertions(+), 567 deletions(-) rename de4dot.code/deobfuscators/Agile_NET/vm/{v1 => }/CilOperandInstructionRestorer.cs (59%) rename de4dot.code/deobfuscators/Agile_NET/vm/{v1 => }/CsvmDataReader.cs (96%) rename de4dot.code/deobfuscators/Agile_NET/vm/{v1 => }/CsvmMethodData.cs (95%) create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs rename de4dot.code/deobfuscators/Agile_NET/vm/{v1/VmOperands.cs => VmOperand.cs} (51%) create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM1_v2.bin create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM2_v2.bin create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM3_v2.bin create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeOpCodeHandler.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/Csvm.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmToCilMethodConverter.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/HandlerTypeCode.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodFinder.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodSigInfoCreator.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandler.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfo.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCode.cs create mode 100644 de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs create mode 100644 de4dot.code/deobfuscators/NullStream.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index c5a3f7a4..086e236c 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -66,17 +66,37 @@ - + + + + - - - + + + + + + True + True + CsvmResources.resx + + + + + + + + + + + + @@ -233,6 +253,7 @@ + @@ -351,6 +372,12 @@ dnlib + + + ResXFileCodeGenerator + CsvmResources.Designer.cs + + mkdir "..\$(OutDir)..\LICENSES" diff --git a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs index 6f6f233e..3eb06f57 100644 --- a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs +++ b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs @@ -88,19 +88,6 @@ namespace de4dot.code.deobfuscators.Agile_NET { static readonly string[] requiredFields1 = new string[] { "System.Boolean", }; - static readonly string[] requiredFields2 = new string[] { - "System.Boolean", - "System.Reflection.Assembly", - }; - static readonly string[] requiredFields3 = new string[] { - "System.Boolean", - "System.Byte[]", - }; - static readonly string[] requiredFields4 = new string[] { - "System.Boolean", - "System.Reflection.Assembly", - "System.Byte[]", - }; bool Find2() { foreach (var cctor in DeobUtils.GetInitCctors(module, 3)) { foreach (var calledMethod in DotNetUtils.GetCalledMethods(module, cctor)) { @@ -108,8 +95,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { if (type.IsPublic) continue; var fieldTypes = new FieldTypes(type); - if (!fieldTypes.Exactly(requiredFields1) && !fieldTypes.Exactly(requiredFields2) && - !fieldTypes.Exactly(requiredFields3) && !fieldTypes.Exactly(requiredFields4)) + if (!fieldTypes.All(requiredFields1)) continue; if (!HasInitializeMethod(type, "_Initialize") && !HasInitializeMethod(type, "_Initialize64")) continue; @@ -126,11 +112,19 @@ namespace de4dot.code.deobfuscators.Agile_NET { return false; } + static string[] requiredFields6 = new string[] { + "System.Byte[]", + }; + static string[] requiredFields7 = new string[] { + "System.Byte[]", + "System.Collections.Hashtable", + }; bool Find3() { foreach (var type in module.Types) { - if (type.Fields.Count != 1) + if (type.Fields.Count < 1 || type.Fields.Count > 2) continue; - if (type.Fields[0].FieldSig.GetFieldType().GetFullName() != "System.Byte[]") + var fieldTypes = new FieldTypes(type); + if (!fieldTypes.Exactly(requiredFields6) && !fieldTypes.Exactly(requiredFields7)) continue; if (type.Methods.Count != 2) continue; diff --git a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs index 0533d003..b0ac7bde 100644 --- a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs @@ -85,7 +85,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { ResourceDecrypter resourceDecrypter; StackFrameHelper stackFrameHelper; - vm.v1.Csvm csvm; + vm.v1.Csvm csvmV1; + vm.v2.Csvm csvmV2; internal class Options : OptionsBase { public bool DecryptMethods { get; set; } @@ -166,7 +167,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { ToInt32(stringDecrypter.Detected) + ToInt32(proxyCallFixer.Detected) + ToInt32(resourceDecrypter.Detected) + - ToInt32(csvm.Detected); + ToInt32(csvmV1.Detected) + + ToInt32(csvmV2.Detected); if (sum > 0) val += 100 + 10 * (sum - 1); if (cliSecureAttributes.Count != 0) @@ -185,8 +187,10 @@ namespace de4dot.code.deobfuscators.Agile_NET { resourceDecrypter.Find(); proxyCallFixer = new ProxyCallFixer(module); proxyCallFixer.FindDelegateCreator(); - csvm = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module); - csvm.Find(); + csvmV1 = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module); + csvmV1.Find(); + csvmV2 = new vm.v2.Csvm(DeobfuscatedFile.DeobfuscatorContext, module); + csvmV2.Find(); } void FindCliSecureAttribute() { @@ -227,7 +231,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { newOne.stringDecrypter = new StringDecrypter(module, stringDecrypter); newOne.resourceDecrypter = new ResourceDecrypter(module, resourceDecrypter); newOne.proxyCallFixer = new ProxyCallFixer(module, proxyCallFixer); - newOne.csvm = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module, csvm); + newOne.csvmV1 = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module, csvmV1); + newOne.csvmV2 = new vm.v2.Csvm(DeobfuscatedFile.DeobfuscatorContext, module, csvmV2); return newOne; } @@ -270,9 +275,11 @@ namespace de4dot.code.deobfuscators.Agile_NET { FindPossibleNamesToRemove(cliSecureRtType.LoadMethod); } - if (options.RestoreVmCode) { - if (csvm.Restore()) - AddResourceToBeRemoved(csvm.Resource, "CSVM data resource"); + if (options.RestoreVmCode && (csvmV1.Detected || csvmV2.Detected)) { + if (csvmV1.Detected && csvmV1.Restore()) + AddResourceToBeRemoved(csvmV1.Resource, "CSVM data resource"); + else if (csvmV2.Detected && csvmV2.Restore()) + AddResourceToBeRemoved(csvmV2.Resource, "CSVM data resource"); else { Logger.e("Couldn't restore VM methods. Use --dont-rename or it will not run"); PreserveTokensAndTypes(); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CilOperandInstructionRestorer.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs similarity index 59% rename from de4dot.code/deobfuscators/Agile_NET/vm/v1/CilOperandInstructionRestorer.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs index 69b93d5c..4df28e4c 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CilOperandInstructionRestorer.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs @@ -21,11 +21,9 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; using de4dot.blocks; -namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { +namespace de4dot.code.deobfuscators.Agile_NET.vm { // Tries to restore the operands of the following CIL instructions: - // ldelema - // ldobj - // stobj + // ldelema, ldelem.*, stelem.*, ldobj, stobj class CilOperandInstructionRestorer { MethodDef method; @@ -42,10 +40,66 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { if (instr.Operand != null) continue; - TypeSig operandType = null; + TypeSig operandType = null, operandTypeTmp; + OpCode newOpCode = null; + SZArraySig arrayType; switch (instr.OpCode.Code) { + case Code.Ldelem: + arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig; + if (arrayType == null) + break; + operandTypeTmp = arrayType.Next; + if (operandTypeTmp == null) + newOpCode = OpCodes.Ldelem_Ref; + else { + switch (operandTypeTmp.ElementType) { + case ElementType.I: newOpCode = OpCodes.Ldelem_I; break; + case ElementType.I1: newOpCode = OpCodes.Ldelem_I1; break; + case ElementType.I2: newOpCode = OpCodes.Ldelem_I2; break; + case ElementType.I4: newOpCode = OpCodes.Ldelem_I4; break; + case ElementType.I8: newOpCode = OpCodes.Ldelem_I8; break; + case ElementType.U: newOpCode = OpCodes.Ldelem_I; break; + case ElementType.U1: newOpCode = OpCodes.Ldelem_U1; break; + case ElementType.U2: newOpCode = OpCodes.Ldelem_U2; break; + case ElementType.U4: newOpCode = OpCodes.Ldelem_U4; break; + case ElementType.U8: newOpCode = OpCodes.Ldelem_I8; break; + case ElementType.R4: newOpCode = OpCodes.Ldelem_R4; break; + case ElementType.R8: newOpCode = OpCodes.Ldelem_R8; break; + default: newOpCode = OpCodes.Ldelem_Ref; break; + //TODO: Ldelem + } + } + break; + + case Code.Stelem: + arrayType = MethodStack.GetLoadedType(method, instrs, i, 2) as SZArraySig; + if (arrayType == null) + break; + operandTypeTmp = arrayType.Next; + if (operandTypeTmp == null) + newOpCode = OpCodes.Stelem_Ref; + else { + switch (operandTypeTmp.ElementType) { + case ElementType.U: + case ElementType.I: newOpCode = OpCodes.Stelem_I; break; + case ElementType.U1: + case ElementType.I1: newOpCode = OpCodes.Stelem_I1; break; + case ElementType.U2: + case ElementType.I2: newOpCode = OpCodes.Stelem_I2; break; + case ElementType.U4: + case ElementType.I4: newOpCode = OpCodes.Stelem_I4; break; + case ElementType.U8: + case ElementType.I8: newOpCode = OpCodes.Stelem_I8; break; + case ElementType.R4: newOpCode = OpCodes.Stelem_R4; break; + case ElementType.R8: newOpCode = OpCodes.Stelem_R8; break; + default: newOpCode = OpCodes.Stelem_Ref; break; + //TODO: Stelem + } + } + break; + case Code.Ldelema: - var arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig; + arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig; if (arrayType == null) break; operandType = arrayType.Next; @@ -64,12 +118,14 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { default: continue; } - if (!IsValidType(operandType)) { + if (newOpCode == null && !IsValidType(operandType)) { atLeastOneFailed = true; continue; } instr.Operand = operandType.ToTypeDefOrRef(); + if (newOpCode != null) + instr.OpCode = newOpCode; } return !atLeastOneFailed; diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmDataReader.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmDataReader.cs similarity index 96% rename from de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmDataReader.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/CsvmDataReader.cs index 6a2753d8..60e810f5 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmDataReader.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmDataReader.cs @@ -24,7 +24,7 @@ using dnlib.IO; using dnlib.DotNet; using de4dot.blocks; -namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { +namespace de4dot.code.deobfuscators.Agile_NET.vm { class CsvmDataReader { IBinaryReader reader; diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmMethodData.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmMethodData.cs similarity index 95% rename from de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmMethodData.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/CsvmMethodData.cs index 99928842..0e67d178 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmMethodData.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmMethodData.cs @@ -19,7 +19,7 @@ using System; -namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { +namespace de4dot.code.deobfuscators.Agile_NET.vm { class CsvmMethodData { public Guid Guid { get; set; } public int Token { get; set; } diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs new file mode 100644 index 00000000..00757dab --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs @@ -0,0 +1,417 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Agile_NET.vm { + abstract class CsvmToCilMethodConverterBase { + readonly IDeobfuscatorContext deobfuscatorContext; + readonly protected ModuleDefMD module; + readonly CilOperandInstructionRestorer operandRestorer = new CilOperandInstructionRestorer(); + readonly Dictionary cilToVmIndex = new Dictionary(); + readonly Dictionary vmIndexToCil = new Dictionary(); + + public CsvmToCilMethodConverterBase(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module) { + this.deobfuscatorContext = deobfuscatorContext; + this.module = module; + } + + protected void SetCilToVmIndex(Instruction instr, int vmIndex) { + cilToVmIndex[instr] = vmIndex; + } + + protected void SetVmIndexToCil(Instruction instr, int vmIndex) { + vmIndexToCil[vmIndex] = instr; + } + + public void Convert(MethodDef cilMethod, CsvmMethodData csvmMethod) { + cilToVmIndex.Clear(); + vmIndexToCil.Clear(); + + var newInstructions = ReadInstructions(cilMethod, csvmMethod); + var newLocals = ReadLocals(cilMethod, csvmMethod); + var newExceptions = ReadExceptions(cilMethod, csvmMethod); + + FixInstructionOperands(newInstructions); + FixLocals(newInstructions, cilMethod.Body.Variables); + FixArgs(newInstructions, cilMethod); + + DotNetUtils.RestoreBody(cilMethod, newInstructions, newExceptions); + + if (!operandRestorer.Restore(cilMethod)) + Logger.w("Failed to restore one or more instruction operands in CSVM method {0:X8}", cilMethod.MDToken.ToInt32()); + RestoreConstrainedPrefix(cilMethod); + } + + void FixLocals(IList instrs, IList locals) { + foreach (var instr in instrs) { + var op = instr.Operand as LocalOperand; + if (op == null) + continue; + + UpdateLocalInstruction(instr, locals[op.Local], op.Local); + } + } + + static void UpdateLocalInstruction(Instruction instr, Local local, int index) { + object operand = null; + OpCode opcode; + + switch (instr.OpCode.Code) { + case Code.Ldloc_S: + case Code.Ldloc: + if (index == 0) + opcode = OpCodes.Ldloc_0; + else if (index == 1) + opcode = OpCodes.Ldloc_1; + else if (index == 2) + opcode = OpCodes.Ldloc_2; + else if (index == 3) + opcode = OpCodes.Ldloc_3; + else if (byte.MinValue <= index && index <= byte.MaxValue) { + opcode = OpCodes.Ldloc_S; + operand = local; + } + else { + opcode = OpCodes.Ldloc; + operand = local; + } + break; + + case Code.Stloc: + case Code.Stloc_S: + if (index == 0) + opcode = OpCodes.Stloc_0; + else if (index == 1) + opcode = OpCodes.Stloc_1; + else if (index == 2) + opcode = OpCodes.Stloc_2; + else if (index == 3) + opcode = OpCodes.Stloc_3; + else if (byte.MinValue <= index && index <= byte.MaxValue) { + opcode = OpCodes.Stloc_S; + operand = local; + } + else { + opcode = OpCodes.Stloc; + operand = local; + } + break; + + case Code.Ldloca: + case Code.Ldloca_S: + if (byte.MinValue <= index && index <= byte.MaxValue) { + opcode = OpCodes.Ldloca_S; + operand = local; + } + else { + opcode = OpCodes.Ldloca; + operand = local; + } + break; + + default: + throw new ApplicationException("Invalid opcode"); + } + + instr.OpCode = opcode; + instr.Operand = operand; + } + + void FixArgs(IList instrs, MethodDef method) { + foreach (var instr in instrs) { + var op = instr.Operand as ArgOperand; + if (op == null) + continue; + + UpdateArgInstruction(instr, method.Parameters[op.Arg], op.Arg); + } + } + + static void UpdateArgInstruction(Instruction instr, Parameter arg, int index) { + switch (instr.OpCode.Code) { + case Code.Ldarg: + case Code.Ldarg_S: + if (index == 0) { + instr.OpCode = OpCodes.Ldarg_0; + instr.Operand = null; + } + else if (index == 1) { + instr.OpCode = OpCodes.Ldarg_1; + instr.Operand = null; + } + else if (index == 2) { + instr.OpCode = OpCodes.Ldarg_2; + instr.Operand = null; + } + else if (index == 3) { + instr.OpCode = OpCodes.Ldarg_3; + instr.Operand = null; + } + else if (byte.MinValue <= index && index <= byte.MaxValue) { + instr.OpCode = OpCodes.Ldarg_S; + instr.Operand = arg; + } + else { + instr.OpCode = OpCodes.Ldarg; + instr.Operand = arg; + } + break; + + case Code.Starg: + case Code.Starg_S: + if (byte.MinValue <= index && index <= byte.MaxValue) { + instr.OpCode = OpCodes.Starg_S; + instr.Operand = arg; + } + else { + instr.OpCode = OpCodes.Starg; + instr.Operand = arg; + } + break; + + case Code.Ldarga: + case Code.Ldarga_S: + if (byte.MinValue <= index && index <= byte.MaxValue) { + instr.OpCode = OpCodes.Ldarga_S; + instr.Operand = arg; + } + else { + instr.OpCode = OpCodes.Ldarga; + instr.Operand = arg; + } + break; + + default: + throw new ApplicationException("Invalid opcode"); + } + } + + protected abstract List ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod); + + protected static int GetInstructionSize(Instruction instr) { + var opcode = instr.OpCode; + if (opcode == null) + return 5; // Load store/field + var op = instr.Operand as SwitchTargetDisplOperand; + if (op == null) + return instr.GetSize(); + return instr.OpCode.Size + (op.TargetDisplacements.Length + 1) * 4; + } + + List ReadLocals(MethodDef cilMethod, CsvmMethodData csvmMethod) { + var locals = new List(); + var reader = new BinaryReader(new MemoryStream(csvmMethod.Locals)); + + if (csvmMethod.Locals.Length == 0) + return locals; + + // v6.0.0.5 sometimes duplicates the last two locals so only check for a negative value. + int numLocals = reader.ReadInt32(); + if (numLocals < 0) + throw new ApplicationException("Invalid number of locals"); + + for (int i = 0; i < numLocals; i++) + locals.Add(new Local(ReadTypeRef(reader))); + + return locals; + } + + TypeSig ReadTypeRef(BinaryReader reader) { + var etype = (ElementType)reader.ReadInt32(); + switch (etype) { + case ElementType.Void: return module.CorLibTypes.Void; + case ElementType.Boolean: return module.CorLibTypes.Boolean; + case ElementType.Char: return module.CorLibTypes.Char; + case ElementType.I1: return module.CorLibTypes.SByte; + case ElementType.U1: return module.CorLibTypes.Byte; + case ElementType.I2: return module.CorLibTypes.Int16; + case ElementType.U2: return module.CorLibTypes.UInt16; + case ElementType.I4: return module.CorLibTypes.Int32; + case ElementType.U4: return module.CorLibTypes.UInt32; + case ElementType.I8: return module.CorLibTypes.Int64; + case ElementType.U8: return module.CorLibTypes.UInt64; + case ElementType.R4: return module.CorLibTypes.Single; + case ElementType.R8: return module.CorLibTypes.Double; + case ElementType.String: return module.CorLibTypes.String; + case ElementType.TypedByRef: return module.CorLibTypes.TypedReference; + case ElementType.I: return module.CorLibTypes.IntPtr; + case ElementType.U: return module.CorLibTypes.UIntPtr; + case ElementType.Object: return module.CorLibTypes.Object; + + case ElementType.ValueType: + case ElementType.Var: + case ElementType.MVar: + return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig(); + + case ElementType.GenericInst: + etype = (ElementType)reader.ReadInt32(); + if (etype == ElementType.ValueType) + return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig(); + // ElementType.Class + return module.CorLibTypes.Object; + + case ElementType.Ptr: + case ElementType.Class: + case ElementType.Array: + case ElementType.FnPtr: + case ElementType.SZArray: + case ElementType.ByRef: + case ElementType.CModReqd: + case ElementType.CModOpt: + case ElementType.Internal: + case ElementType.Sentinel: + case ElementType.Pinned: + default: + return module.CorLibTypes.Object; + } + } + + List ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod) { + var reader = new BinaryReader(new MemoryStream(csvmMethod.Exceptions)); + var ehs = new List(); + + if (reader.BaseStream.Length == 0) + return ehs; + + int numExceptions = reader.ReadInt32(); + if (numExceptions < 0) + throw new ApplicationException("Invalid number of exception handlers"); + + for (int i = 0; i < numExceptions; i++) { + var eh = new ExceptionHandler((ExceptionHandlerType)reader.ReadInt32()); + eh.TryStart = GetInstruction(reader.ReadInt32()); + eh.TryEnd = GetInstructionEnd(reader.ReadInt32()); + eh.HandlerStart = GetInstruction(reader.ReadInt32()); + eh.HandlerEnd = GetInstructionEnd(reader.ReadInt32()); + if (eh.HandlerType == ExceptionHandlerType.Catch) + eh.CatchType = module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + else if (eh.HandlerType == ExceptionHandlerType.Filter) + eh.FilterStart = GetInstruction(reader.ReadInt32()); + + ehs.Add(eh); + } + + return ehs; + } + + Instruction GetInstruction(int vmIndex) { + return vmIndexToCil[vmIndex]; + } + + Instruction GetInstructionEnd(int vmIndex) { + vmIndex++; + Instruction instr; + vmIndexToCil.TryGetValue(vmIndex, out instr); + return instr; + } + + Instruction GetInstruction(Instruction source, int displ) { + int vmIndex = cilToVmIndex[source]; + return vmIndexToCil[vmIndex + displ]; + } + + void FixInstructionOperands(IList instrs) { + foreach (var instr in instrs) { + var op = instr.Operand as IVmOperand; + if (op != null) + instr.Operand = FixOperand(instrs, instr, op); + } + } + + object FixOperand(IList instrs, Instruction instr, IVmOperand vmOperand) { + if (vmOperand is TargetDisplOperand) + return GetInstruction(instr, ((TargetDisplOperand)vmOperand).Displacement); + + if (vmOperand is SwitchTargetDisplOperand) { + var targetDispls = ((SwitchTargetDisplOperand)vmOperand).TargetDisplacements; + Instruction[] targets = new Instruction[targetDispls.Length]; + for (int i = 0; i < targets.Length; i++) + targets[i] = GetInstruction(instr, targetDispls[i]); + return targets; + } + + if (vmOperand is ArgOperand || vmOperand is LocalOperand) + return vmOperand; + + if (vmOperand is FieldInstructionOperand) { + var fieldInfo = (FieldInstructionOperand)vmOperand; + return FixLoadStoreFieldInstruction(instr, fieldInfo.Field, fieldInfo.StaticOpCode, fieldInfo.InstanceOpCode); + } + + throw new ApplicationException(string.Format("Unknown operand type: {0}", vmOperand.GetType())); + } + + IField FixLoadStoreFieldInstruction(Instruction instr, IField fieldRef, OpCode staticInstr, OpCode instanceInstr) { + var field = deobfuscatorContext.ResolveField(fieldRef); + bool isStatic; + if (field == null) { + Logger.w("Could not resolve field {0:X8}. Assuming it's not static.", fieldRef == null ? 0 : fieldRef.MDToken.Raw); + isStatic = false; + } + else + isStatic = field.IsStatic; + instr.OpCode = isStatic ? staticInstr : instanceInstr; + return fieldRef; + } + + static void RestoreConstrainedPrefix(MethodDef method) { + if (method == null || method.Body == null) + return; + + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count; i++) { + var instr = instrs[i]; + if (instr.OpCode.Code != Code.Callvirt) + continue; + + var calledMethod = instr.Operand as IMethod; + if (calledMethod == null) + continue; + var sig = calledMethod.MethodSig; + if (sig == null || !sig.HasThis) + continue; + var thisType = MethodStack.GetLoadedType(method, instrs, i, sig.Params.Count) as ByRefSig; + if (thisType == null) + continue; + if (HasPrefix(instrs, i, Code.Constrained)) + continue; + instrs.Insert(i, OpCodes.Constrained.ToInstruction(thisType.Next.ToTypeDefOrRef())); + i++; + } + } + + static bool HasPrefix(IList instrs, int index, Code prefix) { + index--; + for (; index >= 0; index--) { + var instr = instrs[index]; + if (instr.OpCode.OpCodeType != OpCodeType.Prefix) + break; + if (instr.OpCode.Code == prefix) + return true; + } + return false; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOperands.cs b/de4dot.code/deobfuscators/Agile_NET/vm/VmOperand.cs similarity index 51% rename from de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOperands.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/VmOperand.cs index 108bfff2..03cde6c4 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOperands.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/VmOperand.cs @@ -17,66 +17,50 @@ along with de4dot. If not, see . */ -namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.Agile_NET.vm { interface IVmOperand { } - class TokenOperand : IVmOperand { - public int token; // any type of token - public TokenOperand(int token) { - this.token = token; - } - } - class TargetDisplOperand : IVmOperand { - public int displacement; // number of instructions from current instr + public readonly int Displacement; // number of VM instructions from current VM instr public TargetDisplOperand(int displacement) { - this.displacement = displacement; + this.Displacement = displacement; } } class SwitchTargetDisplOperand : IVmOperand { - public int[] targetDisplacements; // number of instructions from current instr + public readonly int[] TargetDisplacements; // number of VM instructions from current VM instr public SwitchTargetDisplOperand(int[] targetDisplacements) { - this.targetDisplacements = targetDisplacements; + this.TargetDisplacements = targetDisplacements; } } class ArgOperand : IVmOperand { - public ushort arg; + public readonly ushort Arg; public ArgOperand(ushort arg) { - this.arg = arg; + this.Arg = arg; } } class LocalOperand : IVmOperand { - public ushort local; + public readonly ushort Local; public LocalOperand(ushort local) { - this.local = local; + this.Local = local; } } - // OpCode must be changed to ldfld / ldsfld - class LoadFieldOperand : IVmOperand { - public int token; - public LoadFieldOperand(int token) { - this.token = token; - } - } + class FieldInstructionOperand : IVmOperand { + public readonly OpCode StaticOpCode; + public readonly OpCode InstanceOpCode; + public readonly IField Field; - // OpCode must be changed to ldflda / ldsflda - class LoadFieldAddressOperand : IVmOperand { - public int token; - public LoadFieldAddressOperand(int token) { - this.token = token; - } - } - - // OpCode must be changed to stfld / stsfld - class StoreFieldOperand : IVmOperand { - public int token; - public StoreFieldOperand(int token) { - this.token = token; + public FieldInstructionOperand(OpCode staticOpCode, OpCode instanceOpCode, IField field) { + this.StaticOpCode = staticOpCode; + this.InstanceOpCode = instanceOpCode; + this.Field = field; } } } diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs index 055edbec..91339051 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs @@ -140,7 +140,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { var vmModulePath = Path.Combine(Path.GetDirectoryName(module.Location), vmFilename); Logger.v("CSVM filename: {0}", vmFilename); - var dataKey = "cs cached VmOpCodeHandlerDetector"; + var dataKey = "cs cached VmOpCodeHandlerDetector v1"; var dict = (Dictionary)deobfuscatorContext.GetData(dataKey); if (dict == null) deobfuscatorContext.SetData(dataKey, dict = new Dictionary(StringComparer.OrdinalIgnoreCase)); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs index e93d1d02..93516c18 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs @@ -25,411 +25,28 @@ using dnlib.DotNet.Emit; using de4dot.blocks; namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { - class CsvmToCilMethodConverter { - IDeobfuscatorContext deobfuscatorContext; - ModuleDefMD module; + class CsvmToCilMethodConverter : CsvmToCilMethodConverterBase { VmOpCodeHandlerDetector opCodeDetector; - CilOperandInstructionRestorer operandRestorer = new CilOperandInstructionRestorer(); - public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector) { - this.deobfuscatorContext = deobfuscatorContext; - this.module = module; + public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector) + : base(deobfuscatorContext, module) { this.opCodeDetector = opCodeDetector; } - public void Convert(MethodDef cilMethod, CsvmMethodData csvmMethod) { - var newInstructions = ReadInstructions(cilMethod, csvmMethod); - var newLocals = ReadLocals(cilMethod, csvmMethod); - var newExceptions = ReadExceptions(cilMethod, csvmMethod, newInstructions); - - FixInstructionOperands(newInstructions); - FixLocals(newInstructions, cilMethod.Body.Variables); - FixArgs(newInstructions, cilMethod); - - DotNetUtils.RestoreBody(cilMethod, newInstructions, newExceptions); - - if (!operandRestorer.Restore(cilMethod)) - Logger.w("Failed to restore one or more instruction operands in CSVM method {0:X8}", cilMethod.MDToken.ToInt32()); - RestoreConstrainedPrefix(cilMethod); - } - - void FixLocals(IList instrs, IList locals) { - foreach (var instr in instrs) { - var op = instr.Operand as LocalOperand; - if (op == null) - continue; - - UpdateLocalInstruction(instr, locals[op.local], op.local); - } - } - - static void UpdateLocalInstruction(Instruction instr, Local local, int index) { - object operand = null; - OpCode opcode; - - switch (instr.OpCode.Code) { - case Code.Ldloc_S: - case Code.Ldloc: - if (index == 0) - opcode = OpCodes.Ldloc_0; - else if (index == 1) - opcode = OpCodes.Ldloc_1; - else if (index == 2) - opcode = OpCodes.Ldloc_2; - else if (index == 3) - opcode = OpCodes.Ldloc_3; - else if (byte.MinValue <= index && index <= byte.MaxValue) { - opcode = OpCodes.Ldloc_S; - operand = local; - } - else { - opcode = OpCodes.Ldloc; - operand = local; - } - break; - - case Code.Stloc: - case Code.Stloc_S: - if (index == 0) - opcode = OpCodes.Stloc_0; - else if (index == 1) - opcode = OpCodes.Stloc_1; - else if (index == 2) - opcode = OpCodes.Stloc_2; - else if (index == 3) - opcode = OpCodes.Stloc_3; - else if (byte.MinValue <= index && index <= byte.MaxValue) { - opcode = OpCodes.Stloc_S; - operand = local; - } - else { - opcode = OpCodes.Stloc; - operand = local; - } - break; - - case Code.Ldloca: - case Code.Ldloca_S: - if (byte.MinValue <= index && index <= byte.MaxValue) { - opcode = OpCodes.Ldloca_S; - operand = local; - } - else { - opcode = OpCodes.Ldloca; - operand = local; - } - break; - - default: - throw new ApplicationException("Invalid opcode"); - } - - instr.OpCode = opcode; - instr.Operand = operand; - } - - void FixArgs(IList instrs, MethodDef method) { - foreach (var instr in instrs) { - var op = instr.Operand as ArgOperand; - if (op == null) - continue; - - UpdateArgInstruction(instr, method.Parameters[op.arg], op.arg); - } - } - - static void UpdateArgInstruction(Instruction instr, Parameter arg, int index) { - switch (instr.OpCode.Code) { - case Code.Ldarg: - case Code.Ldarg_S: - if (index == 0) { - instr.OpCode = OpCodes.Ldarg_0; - instr.Operand = null; - } - else if (index == 1) { - instr.OpCode = OpCodes.Ldarg_1; - instr.Operand = null; - } - else if (index == 2) { - instr.OpCode = OpCodes.Ldarg_2; - instr.Operand = null; - } - else if (index == 3) { - instr.OpCode = OpCodes.Ldarg_3; - instr.Operand = null; - } - else if (byte.MinValue <= index && index <= byte.MaxValue) { - instr.OpCode = OpCodes.Ldarg_S; - instr.Operand = arg; - } - else { - instr.OpCode = OpCodes.Ldarg; - instr.Operand = arg; - } - break; - - case Code.Starg: - case Code.Starg_S: - if (byte.MinValue <= index && index <= byte.MaxValue) { - instr.OpCode = OpCodes.Starg_S; - instr.Operand = arg; - } - else { - instr.OpCode = OpCodes.Starg; - instr.Operand = arg; - } - break; - - case Code.Ldarga: - case Code.Ldarga_S: - if (byte.MinValue <= index && index <= byte.MaxValue) { - instr.OpCode = OpCodes.Ldarga_S; - instr.Operand = arg; - } - else { - instr.OpCode = OpCodes.Ldarga; - instr.Operand = arg; - } - break; - - default: - throw new ApplicationException("Invalid opcode"); - } - } - - List ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) { + protected override List ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) { 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); + var instr = opCodeDetector.Handlers[vmOpCode].Read(reader, module); instr.Offset = offset; offset += (uint)GetInstructionSize(instr); + SetCilToVmIndex(instr, instrs.Count); + SetVmIndexToCil(instr, instrs.Count); instrs.Add(instr); } return instrs; } - - static int GetInstructionSize(Instruction instr) { - var opcode = instr.OpCode; - if (opcode == null) - return 5; // Load store/field - var op = instr.Operand as SwitchTargetDisplOperand; - if (op == null) - return instr.GetSize(); - return instr.OpCode.Size + (op.targetDisplacements.Length + 1) * 4; - } - - List ReadLocals(MethodDef cilMethod, CsvmMethodData csvmMethod) { - var locals = new List(); - var reader = new BinaryReader(new MemoryStream(csvmMethod.Locals)); - - if (csvmMethod.Locals.Length == 0) - return locals; - - // v6.0.0.5 sometimes duplicates the last two locals so only check for a negative value. - int numLocals = reader.ReadInt32(); - if (numLocals < 0) - throw new ApplicationException("Invalid number of locals"); - - for (int i = 0; i < numLocals; i++) - locals.Add(new Local(ReadTypeRef(reader))); - - return locals; - } - - TypeSig ReadTypeRef(BinaryReader reader) { - var etype = (ElementType)reader.ReadInt32(); - switch (etype) { - case ElementType.Void: return module.CorLibTypes.Void; - case ElementType.Boolean: return module.CorLibTypes.Boolean; - case ElementType.Char: return module.CorLibTypes.Char; - case ElementType.I1: return module.CorLibTypes.SByte; - case ElementType.U1: return module.CorLibTypes.Byte; - case ElementType.I2: return module.CorLibTypes.Int16; - case ElementType.U2: return module.CorLibTypes.UInt16; - case ElementType.I4: return module.CorLibTypes.Int32; - case ElementType.U4: return module.CorLibTypes.UInt32; - case ElementType.I8: return module.CorLibTypes.Int64; - case ElementType.U8: return module.CorLibTypes.UInt64; - case ElementType.R4: return module.CorLibTypes.Single; - case ElementType.R8: return module.CorLibTypes.Double; - case ElementType.String: return module.CorLibTypes.String; - case ElementType.TypedByRef: return module.CorLibTypes.TypedReference; - case ElementType.I: return module.CorLibTypes.IntPtr; - case ElementType.U: return module.CorLibTypes.UIntPtr; - case ElementType.Object: return module.CorLibTypes.Object; - - case ElementType.ValueType: - case ElementType.Var: - case ElementType.MVar: - return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig(); - - case ElementType.GenericInst: - etype = (ElementType)reader.ReadInt32(); - if (etype == ElementType.ValueType) - return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig(); - // ElementType.Class - return module.CorLibTypes.Object; - - case ElementType.Ptr: - case ElementType.Class: - case ElementType.Array: - case ElementType.FnPtr: - case ElementType.SZArray: - case ElementType.ByRef: - case ElementType.CModReqd: - case ElementType.CModOpt: - case ElementType.Internal: - case ElementType.Sentinel: - case ElementType.Pinned: - default: - return module.CorLibTypes.Object; - } - } - - List ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod, List cilInstructions) { - var reader = new BinaryReader(new MemoryStream(csvmMethod.Exceptions)); - var ehs = new List(); - - if (reader.BaseStream.Length == 0) - return ehs; - - int numExceptions = reader.ReadInt32(); - if (numExceptions < 0) - throw new ApplicationException("Invalid number of exception handlers"); - - for (int i = 0; i < numExceptions; i++) { - var eh = new ExceptionHandler((ExceptionHandlerType)reader.ReadInt32()); - eh.TryStart = GetInstruction(cilInstructions, reader.ReadInt32()); - eh.TryEnd = GetInstructionEnd(cilInstructions, reader.ReadInt32()); - eh.HandlerStart = GetInstruction(cilInstructions, reader.ReadInt32()); - eh.HandlerEnd = GetInstructionEnd(cilInstructions, reader.ReadInt32()); - if (eh.HandlerType == ExceptionHandlerType.Catch) - eh.CatchType = module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; - else if (eh.HandlerType == ExceptionHandlerType.Filter) - eh.FilterStart = GetInstruction(cilInstructions, reader.ReadInt32()); - - ehs.Add(eh); - } - - return ehs; - } - - static Instruction GetInstruction(IList instrs, int index) { - return instrs[index]; - } - - static Instruction GetInstructionEnd(IList instrs, int index) { - index++; - if (index == instrs.Count) - return null; - return instrs[index]; - } - - static Instruction GetInstruction(IList instrs, Instruction source, int displ) { - int sourceIndex = instrs.IndexOf(source); - if (sourceIndex < 0) - throw new ApplicationException("Could not find source instruction"); - return instrs[sourceIndex + displ]; - } - - void FixInstructionOperands(IList instrs) { - foreach (var instr in instrs) { - var op = instr.Operand as IVmOperand; - if (op != null) - instr.Operand = FixOperand(instrs, instr, op); - } - } - - object FixOperand(IList instrs, Instruction instr, IVmOperand vmOperand) { - if (vmOperand is TokenOperand) - return GetMemberRef(((TokenOperand)vmOperand).token); - - if (vmOperand is TargetDisplOperand) - return GetInstruction(instrs, instr, ((TargetDisplOperand)vmOperand).displacement); - - if (vmOperand is SwitchTargetDisplOperand) { - var targetDispls = ((SwitchTargetDisplOperand)vmOperand).targetDisplacements; - Instruction[] targets = new Instruction[targetDispls.Length]; - for (int i = 0; i < targets.Length; i++) - targets[i] = GetInstruction(instrs, instr, targetDispls[i]); - return targets; - } - - if (vmOperand is ArgOperand || vmOperand is LocalOperand) - return vmOperand; - - if (vmOperand is LoadFieldOperand) - return FixLoadStoreFieldInstruction(instr, ((LoadFieldOperand)vmOperand).token, OpCodes.Ldsfld, OpCodes.Ldfld); - - if (vmOperand is LoadFieldAddressOperand) - return FixLoadStoreFieldInstruction(instr, ((LoadFieldAddressOperand)vmOperand).token, OpCodes.Ldsflda, OpCodes.Ldflda); - - if (vmOperand is StoreFieldOperand) - return FixLoadStoreFieldInstruction(instr, ((StoreFieldOperand)vmOperand).token, OpCodes.Stsfld, OpCodes.Stfld); - - throw new ApplicationException(string.Format("Unknown operand type: {0}", vmOperand.GetType())); - } - - IField FixLoadStoreFieldInstruction(Instruction instr, int token, OpCode staticInstr, OpCode instanceInstr) { - var fieldRef = module.ResolveToken(token) as IField; - var field = deobfuscatorContext.ResolveField(fieldRef); - bool isStatic; - if (field == null) { - Logger.w("Could not resolve field {0:X8}. Assuming it's not static.", token); - isStatic = false; - } - else - isStatic = field.IsStatic; - instr.OpCode = isStatic ? staticInstr : instanceInstr; - return fieldRef; - } - - ITokenOperand GetMemberRef(int token) { - var memberRef = module.ResolveToken(token) as ITokenOperand; - if (memberRef == null) - throw new ApplicationException(string.Format("Could not find member ref: {0:X8}", token)); - return memberRef; - } - - static void RestoreConstrainedPrefix(MethodDef method) { - if (method == null || method.Body == null) - return; - - var instrs = method.Body.Instructions; - for (int i = 0; i < instrs.Count; i++) { - var instr = instrs[i]; - if (instr.OpCode.Code != Code.Callvirt) - continue; - - var calledMethod = instr.Operand as IMethod; - if (calledMethod == null) - continue; - var sig = calledMethod.MethodSig; - if (sig == null || !sig.HasThis) - continue; - var thisType = MethodStack.GetLoadedType(method, instrs, i, sig.Params.Count) as ByRefSig; - if (thisType == null) - continue; - if (HasPrefix(instrs, i, Code.Constrained)) - continue; - instrs.Insert(i, OpCodes.Constrained.ToInstruction(thisType.Next.ToTypeDefOrRef())); - i++; - } - } - - static bool HasPrefix(IList instrs, int index, Code prefix) { - index--; - for (; index >= 0; index--) { - var instr = instrs[index]; - if (instr.OpCode.OpCodeType != OpCodeType.Prefix) - break; - if (instr.OpCode.Code == prefix) - return true; - } - return false; - } } } diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs index 625d5638..9c572780 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) { + static Instruction arithmetic_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch (reader.ReadByte()) { case 0: return OpCodes.Add.ToInstruction(); case 1: return OpCodes.Add_Ovf.ToInstruction(); @@ -91,25 +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) { - return new Instruction { - OpCode = OpCodes.Newarr, - Operand = new TokenOperand(reader.ReadInt32()), - }; + static Instruction newarr_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Newarr, resolver.ResolveToken(reader.ReadUInt32())); } - static Instruction box_read(BinaryReader reader) { + static Instruction box_read(BinaryReader reader, IInstructionOperandResolver resolver) { 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 = new TokenOperand(reader.ReadInt32()); + instr.Operand = resolver.ResolveToken(reader.ReadUInt32()); return instr; } - static Instruction call_read(BinaryReader reader) { + static Instruction call_read(BinaryReader reader, IInstructionOperandResolver resolver) { var instr = new Instruction(); switch (reader.ReadByte()) { case 0: instr.OpCode = OpCodes.Newobj; break; @@ -117,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 = new TokenOperand(reader.ReadInt32()); + instr.Operand = resolver.ResolveToken(reader.ReadUInt32()); return instr; } - static Instruction cast_read(BinaryReader reader) { + static Instruction cast_read(BinaryReader reader, IInstructionOperandResolver resolver) { 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 = new TokenOperand(reader.ReadInt32()); + instr.Operand = resolver.ResolveToken(reader.ReadUInt32()); return instr; } - static Instruction compare_read(BinaryReader reader) { + static Instruction compare_read(BinaryReader reader, IInstructionOperandResolver resolver) { int type = reader.ReadByte(); Instruction instr = new Instruction(); switch (type) { @@ -205,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) { + static Instruction convert_read(BinaryReader reader, IInstructionOperandResolver resolver) { byte type = reader.ReadByte(); bool second = reader.ReadBoolean(); bool third = reader.ReadBoolean(); @@ -215,7 +212,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { if (type != info.Type || info.Second != second || info.Third != third) continue; - instr = new Instruction { OpCode = info.OpCode }; + instr = new Instruction(info.OpCode); break; } if (instr == null) @@ -224,7 +221,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return instr; } - static Instruction dup_read(BinaryReader reader) { + static Instruction dup_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch (reader.ReadByte()) { case 0: return OpCodes.Dup.ToInstruction(); case 1: return OpCodes.Pop.ToInstruction(); @@ -262,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) { + static Instruction ldelem_read(BinaryReader reader, IInstructionOperandResolver resolver) { Instruction instr = null; bool first = reader.ReadBoolean(); bool second = reader.ReadBoolean(); @@ -274,9 +271,9 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { continue; if (second) - instr = new Instruction { OpCode = info.OpCode }; + instr = new Instruction(info.OpCode); else - instr = new Instruction { OpCode = info.OpCode, Operand = new TokenOperand(value) }; + instr = new Instruction(info.OpCode, resolver.ResolveToken((uint)value)); break; } if (instr == null) @@ -289,29 +286,26 @@ 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) { + static Instruction endfinally_read(BinaryReader reader, IInstructionOperandResolver resolver) { return OpCodes.Endfinally.ToInstruction(); } - static Instruction ldfld_read(BinaryReader reader) { - var instr = new Instruction(); - switch (reader.ReadByte()) { - case 0: instr.Operand = new LoadFieldOperand(reader.ReadInt32()); break; - case 1: instr.Operand = new LoadFieldAddressOperand(reader.ReadInt32()); break; - case 2: instr.Operand = new StoreFieldOperand(reader.ReadInt32()); break; + static Instruction ldfld_read(BinaryReader reader, IInstructionOperandResolver resolver) { + byte b = reader.ReadByte(); + var field = resolver.ResolveToken(reader.ReadUInt32()) 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)); + case 2: return new Instruction(null, new FieldInstructionOperand(OpCodes.Stsfld, OpCodes.Stfld, field)); default: throw new ApplicationException("Invalid opcode"); } - return instr; } - static Instruction initobj_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Initobj, - Operand = new TokenOperand(reader.ReadInt32()), - }; + static Instruction initobj_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Initobj, resolver.ResolveToken(reader.ReadUInt32())); } - static Instruction ldloc_read(BinaryReader reader) { + static Instruction ldloc_read(BinaryReader reader, IInstructionOperandResolver resolver) { bool isLdarg = reader.ReadBoolean(); ushort index = reader.ReadUInt16(); @@ -328,7 +322,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return instr; } - static Instruction ldloca_read(BinaryReader reader) { + static Instruction ldloca_read(BinaryReader reader, IInstructionOperandResolver resolver) { Instruction instr = new Instruction(); if (reader.ReadBoolean()) { instr.OpCode = OpCodes.Ldarga; @@ -342,25 +336,19 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return instr; } - static Instruction ldelema_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Ldelema, - Operand = null, - }; + static Instruction ldelema_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Ldelema, null); } - static Instruction ldlen_read(BinaryReader reader) { + static Instruction ldlen_read(BinaryReader reader, IInstructionOperandResolver resolver) { return OpCodes.Ldlen.ToInstruction(); } - static Instruction ldobj_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Ldobj, - Operand = null, - }; + static Instruction ldobj_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Ldobj, null); } - static Instruction ldstr_read(BinaryReader reader) { + static Instruction ldstr_read(BinaryReader reader, IInstructionOperandResolver resolver) { return OpCodes.Ldstr.ToInstruction(reader.ReadString()); } @@ -368,11 +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) { - return new Instruction { - OpCode = OpCodes.Ldtoken, - Operand = new TokenOperand(reader.ReadInt32()), - }; + static Instruction ldtoken_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Ldtoken, resolver.ResolveToken(reader.ReadUInt32())); } static bool leave_check(UnknownHandlerInfo info) { @@ -381,15 +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) { + static Instruction leave_read(BinaryReader reader, IInstructionOperandResolver resolver) { int displacement = reader.ReadInt32(); - return new Instruction { - OpCode = OpCodes.Leave, - Operand = new TargetDisplOperand(displacement), - }; + return new Instruction(OpCodes.Leave, new TargetDisplOperand(displacement)); } - static Instruction ldc_read(BinaryReader reader) { + static Instruction ldc_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch ((ElementType)reader.ReadByte()) { case ElementType.I4: return Instruction.CreateLdcI4(reader.ReadInt32()); case ElementType.I8: return OpCodes.Ldc_I8.ToInstruction(reader.ReadInt64()); @@ -400,29 +382,24 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { } } - static Instruction ldftn_read(BinaryReader reader) { + static Instruction ldftn_read(BinaryReader reader, IInstructionOperandResolver resolver) { byte code = reader.ReadByte(); - int token = reader.ReadInt32(); + uint token = reader.ReadUInt32(); - Instruction instr; switch (code) { case 0: - instr = new Instruction { OpCode = OpCodes.Ldftn, Operand = new TokenOperand(token) }; - break; + return new Instruction(OpCodes.Ldftn, resolver.ResolveToken(token)); case 1: reader.ReadInt32(); // token of newobj .ctor - instr = new Instruction { OpCode = OpCodes.Ldvirtftn, Operand = new TokenOperand(token) }; - break; + return new Instruction(OpCodes.Ldvirtftn, resolver.ResolveToken(token)); default: throw new ApplicationException("Invalid opcode"); } - - return instr; } - static Instruction logical_read(BinaryReader reader) { + static Instruction logical_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch (reader.ReadByte()) { case 0: return OpCodes.And.ToInstruction(); case 1: return OpCodes.Or.ToInstruction(); @@ -448,7 +425,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return false; } - static Instruction nop_read(BinaryReader reader) { + static Instruction nop_read(BinaryReader reader, IInstructionOperandResolver resolver) { return OpCodes.Nop.ToInstruction(); } @@ -456,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) { + static Instruction ret_read(BinaryReader reader, IInstructionOperandResolver resolver) { reader.ReadInt32(); // token of current method return OpCodes.Ret.ToInstruction(); } @@ -465,11 +442,11 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return info.ExecuteMethod.Body.Variables.Count == 0; } - static Instruction rethrow_read(BinaryReader reader) { + static Instruction rethrow_read(BinaryReader reader, IInstructionOperandResolver resolver) { return OpCodes.Rethrow.ToInstruction(); } - static Instruction stloc_read(BinaryReader reader) { + static Instruction stloc_read(BinaryReader reader, IInstructionOperandResolver resolver) { bool isStarg = reader.ReadBoolean(); ushort index = reader.ReadUInt16(); @@ -487,33 +464,27 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { return instr; } - static Instruction stobj_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Stobj, - Operand = null, - }; + static Instruction stobj_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Stobj, null); } - static Instruction switch_read(BinaryReader reader) { + static Instruction switch_read(BinaryReader reader, IInstructionOperandResolver resolver) { int numTargets = reader.ReadInt32(); int[] targetDispls = new int[numTargets]; for (int i = 0; i < targetDispls.Length; i++) targetDispls[i] = reader.ReadInt32(); - return new Instruction { - OpCode = OpCodes.Switch, - Operand = new SwitchTargetDisplOperand(targetDispls), - }; + return new Instruction(OpCodes.Switch, new SwitchTargetDisplOperand(targetDispls)); } static bool throw_check(UnknownHandlerInfo info) { return !DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags)"); } - static Instruction throw_read(BinaryReader reader) { + static Instruction throw_read(BinaryReader reader, IInstructionOperandResolver resolver) { return OpCodes.Throw.ToInstruction(); } - static Instruction neg_read(BinaryReader reader) { + static Instruction neg_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch (reader.ReadByte()) { case 0: return OpCodes.Neg.ToInstruction(); case 1: return OpCodes.Not.ToInstruction(); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs index 3dc9fc80..aa8419a3 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs @@ -19,7 +19,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { static partial class OpCodeHandlers { - public static readonly OpCodeHandler[][] opcodeHandlers = new OpCodeHandler[][] { + public static readonly OpCodeHandler[][] Handlers = new OpCodeHandler[][] { new OpCodeHandler[] { new OpCodeHandler { Name = "arithmetic", diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs index 7ec49e86..b06ca9c8 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs @@ -214,7 +214,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { opCodeHandlers = new List(); var detected = new List(); - foreach (var handlersList in OpCodeHandlers.opcodeHandlers) { + foreach (var handlersList in OpCodeHandlers.Handlers) { opCodeHandlers.Clear(); foreach (var handlerType in handlerTypes) { diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM1_v2.bin b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM1_v2.bin new file mode 100644 index 0000000000000000000000000000000000000000..156f0777f02c11e2312c196ba61d21e2391f1a7a GIT binary patch literal 9520 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 literal 0 HcmV?d00001 diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM2_v2.bin b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM2_v2.bin new file mode 100644 index 0000000000000000000000000000000000000000..81bd5ae61b634bf895c08f7254eacd511e1bd157 GIT binary patch literal 9520 zcmds+2{cvR`^S$=nG>SvqFa)SYYv%*WKKjWxu#S|DiytrhRBpLBtvu!Zzx14M7^e~ zDAk+NK#6#fk_PfSkJVo5tk&(n{_D4vwO+ro*5llDK4(As+28$~y`OWiU>IhF8yqaS zEx^s&x1TpI@9oXw$%FF+uU4@khPd6aV43H;0(Bj1pFdc$)b|1KdAK32Q8mqyD|X&)N4vV{cNRHv8#hfcV0gZ?S1k8P*{e4hRV)Zy`+_1lnd zI0G-Lr*R&4PUqfMNZVW_7MKD3f`4Fc)zecK*GbzdNr$`HA6QpUWGAXidJlT$UDTB~ z`ZyF-J_`PzU-qAEnfc}T$(Z>?ti1S6kLM~Y?!yAgD@+|;{sDeDahw0Mt+7or<2hzF z$+a-oqn(dh0Wop?WU*OmS9gwl;p{$tsGCn(pq9wAb7Y=(gfHm7(r)OaU7UI!Z!!}r z(#dr3@?NW|NtMV~1Gmj$$=3gNorJvKZJW02HCK>-`E*-lG-Eb(g1JFDnX84>e%csWo-YK63?ke81*K6&& zziV(db%JLZ=``2!X-lbT5$mR}!L`M-DWepxi?gW{tZPW8xt34iHG*63O*B2qXcc17 zSd_7PHgy7xkWO`EFF~idmQRj;G8Ts^JfwzK{sJGkZ`;mhKJm?Aol0$7^ZiWV z&<=7P&gr&aJl~SfmD)$y?8^GENgCpYwr~c{tnU8*)brWtgOXjt!GW32wNA&W>~;iy z#Co1t-HmE?W_34W%<67I-1lsc$GEp{`M10)2DCwPjSVKWU^d6I5FZab3!uO2{GL9U zO!Mn3`u8{RUv_Um^Ml4KCwVD5(pSAS|AbYZYMzA-a4*~ zVQyg_z&EowKx}knxa+W(ZVS0j-u)}DeiQhFcQG^%w=IQTBQ!UE=Jz&YnHp5Oh@%;w zwD_DBC1Eo+-2+Fd&f7cPpg$C=#G$VWi{}=VQVYwI8FX6h}d|uj$HJ;@>|5p8las5%A8Pe@3+_E+4kvUZ) z9mfzhh#BS&eHTcex)jy!9N2i4m9?XUvrQH_7|MThD?^D_`+vmWigYz3d-|q8Ta@GJ zca+@+q_m@pzaH{WSTzuhqnU%~-}Uo)HU&Dn7Pp($vx|EhLg5Ax+)(b#weKM|xr)Xd zl|dfi~782gFx zgG@g?A`^}`!xrH-S9N+U{mC=m-H;N`-Ne;AQQkY7I*Af=nrro8nnk>zS1gB9nP`mm z@Bo+cZ0aON&}ptcchl^j1AfG?>uMz>_zhQho6V+9iwQc-wR~DNpp_ylpCTS}=z5yp z-Xz7@)M*Jpr^fk%?sgeXg#;i6W*1=o5qFDTe2Cpu$FPG^RvWg+Dx6XBzSB_bqxRJ_1s@TW##T8Ok{FouHz})8~X>1MfR*- z^3~UMk=o(82ak{!J;AnDcagG3}Zywt@I1Y#DBd z(}%L(Hv1{WIizffe|P@$81Ur;?tY?CN29et_-ni5Q>sbIcHku3(0+d~Drz!RKjrUa z+vJ`fB*T?(9ejhHpc`ladq14v0ETLT3CrCI`}}Q-&i^AZqJM6@jL6bgh?mNSoox74 z!_N3PaQ`s0h4Flkm{RlMea+vjOzC4Q^wNgw=n%($pturuv|28YI}mP|Eyc0dPITt~ z2;d*(2Qxq5|FU0;p(VGmbwyrxt*6|+ShydJ1^7$;0L8oXXZA0Kq~G6((|bvbxqYhU zppK$)lCz$#oKIg>E@F7InL`5qi^l%DItkS}|4p3)al!%pB0tRPA<3WjzfB^OV|nme>S1`F3}|ivJ|6zU`fZw1Rn0`ykogcspylQXk!c; z`38I?fzPWdzN71~cNyt5b=-Te(M*V|2>jF-eGkn(;`#WS*e1Txi8g32g&W)m@$qIm zJxS9DCO=S4<*4a*RfYM0@u7J@GxZA5e_;E2K_3yN-O=ttq-@z&F2GRS*$JfWnw6Jchw-g>t<^N7g`IYY^cB6)1mI|xS@g}^&U6t~{o|ER$tP2}|XQw6EOWpP7&?{3f3pPVO7 z8s>VjmaHal`&Z3=_gp2W+Ki5VA1$U!Y3N$eL8u>m`CgZK8%;mtf zKEUO1L+fL6OkTHK*Um@ZN`%>3>>ddI+V4k7L_bR>Rnpsfg}G=~Mke@0>uzOz)_~bW zRq{Uhc;mDY$~9;U<3V%r_g-JU%RCIff~eGt@M`@x#I{L_Z7L&d2C?6(mJN6p-|U38 z@C>7M#yzgi<~fc1xZ9E1RQ|AXDPr5-X~ArcsJKt0g`A!I{UqVulI?9*tQEAsb>aw9 zzzyD!A-Bp>x~-*}=t6-Zt52nca6U#_pqO4@`{iE^)xYarlP}vcYP|tC7~)5j8KE)h zcOHcuC-H=FN-YLP)I)Pa#ll|IzWpnx!mQP2uT?=@Xx(D=P5}%K;@Fw(v1mS+?XfV= z=-nic&J{g%#Hm(HkE}acZXgPN(R)dym(0e@14lW1@*f`#`17R#V$eSvinzfw(5az3 zBw{ovZckmThm_8b{tWyQri2^fMg{GU&8CV+2Q}KFiiBLCyug$R{6=Ag!%l&tN(H5T zQeQ-9j=)t2e6xN0STR&aW8xiW)tMeLW8=)XK9tj-b8fFlzSEf_6{jo8I`)ivlp-ymANY;@ z@Y=|d9v+=q`I1CxG0dk`0@wL@5BQ-+TTp`k{!G&AF>gpy5t$jME`f7#OFVP%Q*Aqj zoy_E=`a>#WdITPdVfNUUPA88G*juA*&J5uC1g@Z;Clqlt*{im_b;FM`KadbJAaJVO zH3i-JK`pJ_)%jO_0~3K868Pq!aVu9fR#lUhPR7Xe>6yLS2pI#@P&&4Jd? z(Yv6BhIE88jPL*2OHKCN-BsN8>h>my*f4R!mOJ?Em@#h1w{C|)&)q_HN6JD)Ej4>= z$*;cSAMh+DY2FeoXia0ge!Q>gg-Bw43&T|*%ae6J_?zV|)Ip8xyE zu00VWEhJ;nnfWEQ|J^#9Agk25Fjl_8LyD1KTj>j*oiP*Kpe>A7-8aiET~o>;SzKVB zRgCOS{1UbSH^i~ln_^Q#)-mqU8aMd<(QX26O5o~ArjeD6O?3Ttt8#t1cFe3FW;jQh zqzZjmJ|#+-T+zVf5r<^0~6F#~#WJtulyNh#2$%hb3-E%d!to Uuh}WiTb^L3_btHqKmzdp07N>HDF6Tf literal 0 HcmV?d00001 diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM3_v2.bin b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM3_v2.bin new file mode 100644 index 0000000000000000000000000000000000000000..a86daab3f42c88aeacb7e76b96e4f02550db89d4 GIT binary patch literal 9580 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 D(I=s5 literal 0 HcmV?d00001 diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs new file mode 100644 index 00000000..9ae1debc --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs @@ -0,0 +1,335 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class CompositeHandlerDetector { + readonly List handlers; + + public CompositeHandlerDetector(IList handlers) { + this.handlers = new List(handlers.Count); + OpCodeHandler nop = null; + foreach (var handler in handlers) { + if (nop == null && handler.OpCodeHandlerInfo.TypeCode == HandlerTypeCode.Nop) + nop = handler; + else + this.handlers.Add(handler); + } + if (nop != null) + this.handlers.Add(nop); + } + + struct MatchState { + public HandlerState OtherState; + public HandlerState CompositeState; + + public MatchState(HandlerState OtherState, HandlerState CompositeState) { + this.OtherState = OtherState; + this.CompositeState = CompositeState; + } + } + + struct HandlerState { + public readonly HandlerMethod HandlerMethod; + public readonly IList Blocks; + public readonly int BlockIndex; + public int InstructionIndex; + + public HandlerState(HandlerMethod handlerMethod, int blockIndex, int instructionIndex) { + this.HandlerMethod = handlerMethod; + this.Blocks = handlerMethod.Blocks.MethodBlocks.GetAllBlocks(); + this.BlockIndex = blockIndex; + this.InstructionIndex = instructionIndex; + } + + public HandlerState(HandlerMethod handlerMethod, IList blocks, int blockIndex, int instructionIndex) { + this.HandlerMethod = handlerMethod; + this.Blocks = blocks; + this.BlockIndex = blockIndex; + this.InstructionIndex = instructionIndex; + } + + public HandlerState Clone() { + return new HandlerState(HandlerMethod, Blocks, BlockIndex, InstructionIndex); + } + } + + struct FindHandlerState { + public HandlerState CompositeState; + public readonly Dictionary VisitedCompositeBlocks; + public bool Done; + + public FindHandlerState(HandlerState compositeState) { + this.CompositeState = compositeState; + this.VisitedCompositeBlocks = new Dictionary(); + this.Done = false; + } + + public FindHandlerState(HandlerState compositeState, Dictionary visitedCompositeBlocks, bool done) { + this.CompositeState = compositeState; + this.VisitedCompositeBlocks = new Dictionary(visitedCompositeBlocks); + this.Done = done; + } + + public FindHandlerState Clone() { + return new FindHandlerState(CompositeState.Clone(), VisitedCompositeBlocks, Done); + } + } + + public bool FindHandlers(CompositeOpCodeHandler composite) { + composite.OpCodeHandlerInfos.Clear(); + var compositeExecState = new FindHandlerState(new HandlerState(composite.ExecMethod, 0, 0)); + while (!compositeExecState.Done) { + var handler = FindHandlerMethod(ref compositeExecState); + if (handler == null) + return false; + + composite.OpCodeHandlerInfos.Add(handler.OpCodeHandlerInfo); + } + return composite.OpCodeHandlerInfos.Count != 0; + } + + OpCodeHandler FindHandlerMethod(ref FindHandlerState findExecState) { + foreach (var handler in handlers) { + FindHandlerState findExecStateNew = findExecState.Clone(); + if (!Matches(handler.ExecMethod, ref findExecStateNew)) + continue; + + findExecState = findExecStateNew; + return handler; + } + return null; + } + + Stack stack = new Stack(); + bool Matches(HandlerMethod handler, ref FindHandlerState findState) { + HandlerState? nextState = null; + stack.Clear(); + stack.Push(new MatchState(new HandlerState(handler, 0, 0), findState.CompositeState)); + while (stack.Count > 0) { + var matchState = stack.Pop(); + + if (matchState.CompositeState.InstructionIndex == 0) { + if (findState.VisitedCompositeBlocks.ContainsKey(matchState.CompositeState.BlockIndex)) + continue; + findState.VisitedCompositeBlocks[matchState.CompositeState.BlockIndex] = true; + } + else { + if (!findState.VisitedCompositeBlocks.ContainsKey(matchState.CompositeState.BlockIndex)) + throw new ApplicationException("Block hasn't been visited"); + } + + if (!Compare(ref matchState.OtherState, ref matchState.CompositeState)) + return false; + + var hblock = matchState.OtherState.Blocks[matchState.OtherState.BlockIndex]; + var hinstrs = hblock.Instructions; + int hi = matchState.OtherState.InstructionIndex; + var cblock = matchState.CompositeState.Blocks[matchState.CompositeState.BlockIndex]; + var cinstrs = cblock.Instructions; + int ci = matchState.CompositeState.InstructionIndex; + if (hi < hinstrs.Count) + return false; + + if (ci < cinstrs.Count) { + if (hblock.CountTargets() != 0) + return false; + if (hblock.LastInstr.OpCode.Code == Code.Ret) { + if (nextState != null) + return false; + nextState = matchState.CompositeState; + } + } + else { + if (cblock.CountTargets() != hblock.CountTargets()) + return false; + if (cblock.FallThrough != null || hblock.FallThrough != null) { + if (cblock.FallThrough == null || hblock.FallThrough == null) + return false; + + var hs = CreateHandlerState(handler, matchState.OtherState.Blocks, hblock.FallThrough); + var cs = CreateHandlerState(findState.CompositeState.HandlerMethod, findState.CompositeState.Blocks, cblock.FallThrough); + stack.Push(new MatchState(hs, cs)); + } + if (cblock.Targets != null || hblock.Targets != null) { + if (cblock.Targets == null || hblock.Targets == null || + cblock.Targets.Count != hblock.Targets.Count) + return false; + + for (int i = 0; i < cblock.Targets.Count; i++) { + var hs = CreateHandlerState(handler, matchState.OtherState.Blocks, hblock.Targets[i]); + var cs = CreateHandlerState(findState.CompositeState.HandlerMethod, findState.CompositeState.Blocks, cblock.Targets[i]); + stack.Push(new MatchState(hs, cs)); + } + } + } + } + + if (nextState == null) { + findState.Done = true; + return true; + } + else { + if (findState.CompositeState.BlockIndex == nextState.Value.BlockIndex && + findState.CompositeState.InstructionIndex == nextState.Value.InstructionIndex) + return false; + findState.CompositeState = nextState.Value; + return true; + } + } + + static HandlerState CreateHandlerState(HandlerMethod handler, IList blocks, Block target) { + return new HandlerState(handler, blocks.IndexOf(target), 0); + } + + static bool Compare(ref HandlerState handler, ref HandlerState composite) { + var hinstrs = handler.Blocks[handler.BlockIndex].Instructions; + int hi = handler.InstructionIndex; + var cinstrs = composite.Blocks[composite.BlockIndex].Instructions; + int ci = composite.InstructionIndex; + + while (true) { + if (hi >= hinstrs.Count && ci >= cinstrs.Count) + break; + if (hi >= hinstrs.Count || ci >= cinstrs.Count) + return false; + + var hinstr = hinstrs[hi++]; + var cinstr = cinstrs[ci++]; + if (hinstr.OpCode.Code == Code.Nop || + cinstr.OpCode.Code == Code.Nop) { + if (hinstr.OpCode.Code != Code.Nop) + hi--; + if (cinstr.OpCode.Code != Code.Nop) + ci--; + continue; + } + + if (hi == hinstrs.Count && hinstr.OpCode.Code == Code.Ret) { + if (cinstr.OpCode.Code != Code.Br && cinstr.OpCode.Code != Code.Ret) + ci--; + break; + } + + if (hinstr.OpCode.Code != cinstr.OpCode.Code) + return false; + + if (hinstr.OpCode.Code == Code.Ldfld && + hi + 1 < hinstrs.Count && ci + 1 < cinstrs.Count) { + var hfield = hinstr.Operand as FieldDef; + var cfield = cinstr.Operand as FieldDef; + if (hfield != null && cfield != null && + !hfield.IsStatic && !cfield.IsStatic && + hfield.DeclaringType == handler.HandlerMethod.Method.DeclaringType && + cfield.DeclaringType == composite.HandlerMethod.Method.DeclaringType && + SignatureEqualityComparer.Instance.Equals(hfield.Signature, cfield.Signature)) { + cinstr = cinstrs[ci++]; + hinstr = hinstrs[hi++]; + if (cinstr.OpCode.Code != Code.Ldc_I4 || + hinstr.OpCode.Code != Code.Ldc_I4) + return false; + continue; + } + } + + if (!CompareOperand(hinstr.OpCode.OperandType, cinstr.Operand, hinstr.Operand)) + return false; + } + + handler.InstructionIndex = hi; + composite.InstructionIndex = ci; + return true; + } + + static bool CompareOperand(OperandType opType, object a, object b) { + switch (opType) { + case OperandType.ShortInlineI: + return (a is byte && b is byte && (byte)a == (byte)b) || + (a is sbyte && b is sbyte && (sbyte)a == (sbyte)b); + + case OperandType.InlineI: + return a is int && b is int && (int)a == (int)b; + + case OperandType.InlineI8: + return a is long && b is long && (long)a == (long)b; + + case OperandType.ShortInlineR: + return a is float && b is float && (float)a == (float)b; + + case OperandType.InlineR: + return a is double && b is double && (double)a == (double)b; + + case OperandType.InlineField: + return FieldEqualityComparer.CompareDeclaringTypes.Equals(a as IField, b as IField); + + case OperandType.InlineMethod: + return MethodEqualityComparer.CompareDeclaringTypes.Equals(a as IMethod, b as IMethod); + + case OperandType.InlineSig: + return SignatureEqualityComparer.Instance.Equals(a as MethodSig, b as MethodSig); + + case OperandType.InlineString: + return string.Equals(a as string, b as string); + + case OperandType.InlineSwitch: + var al = a as IList; + var bl = b as IList; + return al != null && bl != null && al.Count == bl.Count; + + case OperandType.InlineTok: + var fa = a as IField; + var fb = b as IField; + if (fa != null && fb != null) + return FieldEqualityComparer.CompareDeclaringTypes.Equals(fa, fb); + var ma = a as IMethod; + var mb = b as IMethod; + if (ma != null && mb != null) + return MethodEqualityComparer.CompareDeclaringTypes.Equals(ma, mb); + return TypeEqualityComparer.Instance.Equals(a as ITypeDefOrRef, b as ITypeDefOrRef); + + case OperandType.InlineType: + return TypeEqualityComparer.Instance.Equals(a as ITypeDefOrRef, b as ITypeDefOrRef); + + case OperandType.InlineVar: + case OperandType.ShortInlineVar: + var la = a as Local; + var lb = b as Local; + if (la != null && lb != null) + return true; + var pa = a as Parameter; + var pb = b as Parameter; + return pa != null && pb != null && pa.Index == pb.Index; + + case OperandType.InlineBrTarget: + case OperandType.ShortInlineBrTarget: + case OperandType.InlineNone: + case OperandType.InlinePhi: + return true; + + default: + return false; + } + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeOpCodeHandler.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeOpCodeHandler.cs new file mode 100644 index 00000000..2508d020 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeOpCodeHandler.cs @@ -0,0 +1,67 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System.Collections.Generic; +using System.Text; +using dnlib.DotNet; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class HandlerMethod { + public MethodDef Method { get; private set; } + public Blocks Blocks { get; private set; } + + public HandlerMethod(MethodDef method) { + this.Method = method; + this.Blocks = new Blocks(method); + } + } + + class PrimitiveHandlerMethod : HandlerMethod { + public MethodSigInfo Sig { get; set; } + + public PrimitiveHandlerMethod(MethodDef method) + : base(method) { + } + } + + class CompositeOpCodeHandler { + public TypeDef HandlerType { get; private set; } + public HandlerMethod ExecMethod { get; private set; } + public List OpCodeHandlerInfos { get; private set; } + + public CompositeOpCodeHandler(TypeDef handlerType, HandlerMethod execMethod) { + this.HandlerType = handlerType; + this.ExecMethod = execMethod; + this.OpCodeHandlerInfos = new List(); + } + + public override string ToString() { + if (OpCodeHandlerInfos.Count == 0) + return ""; + var sb = new StringBuilder(); + foreach (var handler in OpCodeHandlerInfos) { + if (sb.Length != 0) + sb.Append(", "); + sb.Append(handler.Name); + } + return sb.ToString(); + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/Csvm.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/Csvm.cs new file mode 100644 index 00000000..9b6f0c10 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/Csvm.cs @@ -0,0 +1,164 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using dnlib.DotNet; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class Csvm { + IDeobfuscatorContext deobfuscatorContext; + ModuleDefMD module; + EmbeddedResource resource; + AssemblyRef vmAssemblyRef; + + public bool Detected { + get { return resource != null && vmAssemblyRef != null; } + } + + public EmbeddedResource Resource { + get { return Detected ? resource : null; } + } + + public Csvm(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module) { + this.deobfuscatorContext = deobfuscatorContext; + this.module = module; + } + + public Csvm(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, Csvm oldOne) { + this.deobfuscatorContext = deobfuscatorContext; + this.module = module; + if (oldOne.resource != null) + this.resource = (EmbeddedResource)module.Resources[oldOne.module.Resources.IndexOf(oldOne.resource)]; + if (oldOne.vmAssemblyRef != null) + this.vmAssemblyRef = module.ResolveAssemblyRef(oldOne.vmAssemblyRef.Rid); + } + + public void Find() { + resource = FindCsvmResource(); + vmAssemblyRef = FindVmAssemblyRef(); + } + + AssemblyRef FindVmAssemblyRef() { + foreach (var memberRef in module.GetMemberRefs()) { + var sig = memberRef.MethodSig; + if (sig == null) + continue; + if (sig.RetType.GetElementType() != ElementType.Object) + continue; + if (sig.Params.Count != 2) + continue; + if (memberRef.Name != "RunMethod") + continue; + if (memberRef.FullName == "System.Object VMRuntime.Libraries.CSVMRuntime::RunMethod(System.String,System.Object[])") + return memberRef.DeclaringType.Scope as AssemblyRef; + } + return null; + } + + EmbeddedResource FindCsvmResource() { + return DotNetUtils.GetResource(module, "_CSVM") as EmbeddedResource; + } + + public bool Restore() { + if (!Detected) + return true; + + int oldIndent = Logger.Instance.IndentLevel; + try { + Restore2(); + return true; + } + catch { + return false; + } + finally { + Logger.Instance.IndentLevel = oldIndent; + } + } + + void Restore2() { + Logger.n("Restoring CSVM methods"); + Logger.Instance.Indent(); + + var opcodeDetector = GetVmOpCodeHandlerDetector(); + var csvmMethods = new CsvmDataReader(resource.Data).Read(); + + var converter = new CsvmToCilMethodConverter(deobfuscatorContext, module, opcodeDetector); + var methodPrinter = new MethodPrinter(); + foreach (var csvmMethod in csvmMethods) { + var cilMethod = module.ResolveToken(csvmMethod.Token) as MethodDef; + if (cilMethod == null) + throw new ApplicationException(string.Format("Could not find method {0:X8}", csvmMethod.Token)); + converter.Convert(cilMethod, csvmMethod); + Logger.v("Restored method {0:X8}", cilMethod.MDToken.ToInt32()); + PrintMethod(methodPrinter, cilMethod); + } + Logger.Instance.DeIndent(); + Logger.n("Restored {0} CSVM methods", csvmMethods.Count); + } + + static void PrintMethod(MethodPrinter methodPrinter, MethodDef method) { + const LoggerEvent dumpLogLevel = LoggerEvent.Verbose; + if (Logger.Instance.IgnoresEvent(dumpLogLevel)) + return; + + Logger.Instance.Indent(); + + Logger.v("Locals:"); + Logger.Instance.Indent(); + for (int i = 0; i < method.Body.Variables.Count; i++) + Logger.v("#{0}: {1}", i, method.Body.Variables[i].Type); + Logger.Instance.DeIndent(); + + Logger.v("Code:"); + Logger.Instance.Indent(); + methodPrinter.Print(dumpLogLevel, method.Body.Instructions, method.Body.ExceptionHandlers); + Logger.Instance.DeIndent(); + + Logger.Instance.DeIndent(); + } + + VmOpCodeHandlerDetector GetVmOpCodeHandlerDetector() { + var vmFilename = vmAssemblyRef.Name + ".dll"; + var vmModulePath = Path.Combine(Path.GetDirectoryName(module.Location), vmFilename); + Logger.v("CSVM filename: {0}", vmFilename); + + var dataKey = "cs cached VmOpCodeHandlerDetector v2"; + var dict = (Dictionary)deobfuscatorContext.GetData(dataKey); + if (dict == null) + deobfuscatorContext.SetData(dataKey, dict = new Dictionary(StringComparer.OrdinalIgnoreCase)); + VmOpCodeHandlerDetector detector; + if (dict.TryGetValue(vmModulePath, out detector)) + return detector; + dict[vmModulePath] = detector = new VmOpCodeHandlerDetector(ModuleDefMD.Load(vmModulePath)); + + detector.FindHandlers(); + Logger.v("CSVM opcodes: {0}", detector.Handlers.Count); + Logger.Instance.Indent(); + for (int i = 0; i < detector.Handlers.Count; i++) + Logger.v("{0:X4}: {1}", i, detector.Handlers[i]); + Logger.Instance.DeIndent(); + + return detector; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs new file mode 100644 index 00000000..815cc53d --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs @@ -0,0 +1,779 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class CsvmInfo { + ModuleDef module; + + public TypeDef VmHandlerBaseType; + + public MethodDef LogicalOpShrUn; + public MethodDef LogicalOpShl; + public MethodDef LogicalOpShr; + public MethodDef LogicalOpAnd; + public MethodDef LogicalOpXor; + public MethodDef LogicalOpOr; + + public MethodDef CompareLt; + public MethodDef CompareLte; + public MethodDef CompareGt; + public MethodDef CompareGte; + public MethodDef CompareEq; + public MethodDef CompareEqz; + + public MethodDef ArithmeticSubOvfUn; + public MethodDef ArithmeticMulOvfUn; + public MethodDef ArithmeticRemUn; + public MethodDef ArithmeticRem; + public MethodDef ArithmeticDivUn; + public MethodDef ArithmeticDiv; + public MethodDef ArithmeticMul; + public MethodDef ArithmeticMulOvf; + public MethodDef ArithmeticSub; + public MethodDef ArithmeticSubOvf; + public MethodDef ArithmeticAddOvfUn; + public MethodDef ArithmeticAddOvf; + public MethodDef ArithmeticAdd; + + public MethodDef UnaryNot; + public MethodDef UnaryNeg; + + public MethodDef ArgsGet; + public MethodDef ArgsSet; + public MethodDef LocalsGet; + public MethodDef LocalsSet; + + public CsvmInfo(ModuleDef module) { + this.module = module; + } + + public bool Initialize() { + return FindVmHandlerBase() && + FindLocalOpsMethods() && + FindComparerMethods() && + FindArithmeticMethods() && + FindUnaryOpsMethods() && + FindArgsLocals(); + } + + public bool FindVmHandlerBase() { + foreach (var type in module.Types) { + if (!type.IsPublic || !type.IsAbstract) + continue; + if (type.HasFields || type.HasProperties || type.HasEvents) + continue; + if (type.BaseType == null || type.BaseType.FullName != "System.Object") + continue; + if (CountVirtual(type) != 2) + continue; + + VmHandlerBaseType = type; + return true; + } + + return false; + } + + public bool FindLocalOpsMethods() { + foreach (var type in module.Types) { + if (type.BaseType == null || type.BaseType.FullName != "System.Object") + continue; + if (type.Methods.Count != 6 && type.Methods.Count != 7) + continue; + LogicalOpShrUn = FindLogicalOpMethodShrUn(type); + if (LogicalOpShrUn == null) + continue; + LogicalOpShl = FindLogicalOpMethodShl(type); + LogicalOpShr = FindLogicalOpMethodShr(type); + LogicalOpAnd = FindLogicalOpMethodAnd(type); + LogicalOpXor = FindLogicalOpMethodXor(type); + LogicalOpOr = FindLogicalOpMethodOr(type); + if (LogicalOpShrUn != null && LogicalOpShl != null && + LogicalOpShr != null && LogicalOpAnd != null && + LogicalOpXor != null && LogicalOpOr != null) + return true; + } + + return false; + } + + MethodDef FindLogicalOpMethodShrUn(TypeDef type) { + return FindLogicalOpMethod(type, ElementType.U4, ElementType.I4, ElementType.U4, Code.Shr_Un); + } + + MethodDef FindLogicalOpMethodShl(TypeDef type) { + return FindLogicalOpMethod(type, ElementType.I4, ElementType.I4, ElementType.I4, Code.Shl); + } + + MethodDef FindLogicalOpMethodShr(TypeDef type) { + return FindLogicalOpMethod(type, ElementType.I4, ElementType.I4, ElementType.I4, Code.Shr); + } + + MethodDef FindLogicalOpMethod(TypeDef type, ElementType e1, ElementType e2, ElementType e3, Code code) { + foreach (var method in type.Methods) { + if (!CheckLogicalMethodSig(method)) + continue; + if (method.Body == null) + continue; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 7; i++) { + var ldarg0 = instrs[i]; + if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0) + continue; + if (!CheckUnboxAny(instrs[i + 1], e1)) + continue; + var ldarg1 = instrs[i + 2]; + if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1) + continue; + if (!CheckUnboxAny(instrs[i + 3], e2)) + continue; + var ldci4 = instrs[i + 4]; + if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 0x1F) + continue; + if (instrs[i + 5].OpCode.Code != Code.And) + continue; + if (instrs[i + 6].OpCode.Code != code) + continue; + if (!CheckBox(instrs[i + 7], e3)) + continue; + + return method; + } + } + + return null; + } + + MethodDef FindLogicalOpMethodAnd(TypeDef type) { + return FindLogicalOpMethod(type, Code.And); + } + + MethodDef FindLogicalOpMethodXor(TypeDef type) { + return FindLogicalOpMethod(type, Code.Xor); + } + + MethodDef FindLogicalOpMethodOr(TypeDef type) { + return FindLogicalOpMethod(type, Code.Or); + } + + MethodDef FindLogicalOpMethod(TypeDef type, Code code) { + foreach (var method in type.Methods) { + if (!CheckLogicalMethodSig(method)) + continue; + if (method.Body == null) + continue; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 5; i++) { + var ldarg0 = instrs[i]; + if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0) + continue; + if (!CheckUnboxAny(instrs[i + 1], ElementType.I4)) + continue; + var ldarg1 = instrs[i + 2]; + if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1) + continue; + if (!CheckUnboxAny(instrs[i + 3], ElementType.I4)) + continue; + if (instrs[i + 4].OpCode.Code != code) + continue; + if (!CheckBox(instrs[i + 5], ElementType.I4)) + continue; + + return method; + } + } + + return null; + } + + static bool CheckLogicalMethodSig(MethodDef method) { + return method != null && + method.IsStatic && + method.MethodSig.GetParamCount() == 2 && + method.MethodSig.RetType.GetElementType() == ElementType.Object && + method.MethodSig.Params[0].GetElementType() == ElementType.Object && + method.MethodSig.Params[1].GetElementType() == ElementType.Object; + } + + public bool FindComparerMethods() { + foreach (var type in module.Types) { + if (type.BaseType == null || type.BaseType.FullName != "System.Object") + continue; + if (type.Methods.Count != 9) + continue; + CompareLt = FindCompareLt(type); + if (CompareLt == null) + continue; + CompareLte = FindCompareLte(type); + CompareGt = FindCompareGt(type); + CompareGte = FindCompareGte(type); + CompareEq = FindCompareEq(type); + CompareEqz = FindCompareEqz(type); + if (CompareLt != null && CompareLte != null && + CompareGt != null && CompareGte != null && + CompareEq != null && CompareEqz != null) + return true; + } + + return false; + } + + MethodDef FindCompareLt(TypeDef type) { + return FindCompareMethod(type, Code.Clt, false); + } + + MethodDef FindCompareLte(TypeDef type) { + return FindCompareMethod(type, Code.Cgt, true); + } + + MethodDef FindCompareGt(TypeDef type) { + return FindCompareMethod(type, Code.Cgt, false); + } + + MethodDef FindCompareGte(TypeDef type) { + return FindCompareMethod(type, Code.Clt, true); + } + + MethodDef FindCompareMethod(TypeDef type, Code code, bool invert) { + foreach (var method in type.Methods) { + if (!CheckCompareMethodSig(method)) + continue; + if (method.Body == null) + continue; + var instrs = method.Body.Instructions; + int end = instrs.Count - 6; + if (invert) + end -= 2; + for (int i = 0; i < end; i++) { + int index = i; + var ldarg0 = instrs[index++]; + if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0) + continue; + if (!CheckUnboxAny(instrs[index++], ElementType.I4)) + continue; + var ldarg1 = instrs[index++]; + if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1) + continue; + if (!CheckUnboxAny(instrs[index++], ElementType.I4)) + continue; + if (instrs[index++].OpCode.Code != code) + continue; + if (invert) { + var ldci4 = instrs[index++]; + if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 0) + continue; + if (instrs[index++].OpCode.Code != Code.Ceq) + continue; + } + if (!instrs[index++].IsStloc()) + continue; + + return method; + } + } + + return null; + } + + static bool CheckCompareMethodSig(MethodDef method) { + if (method == null || !method.IsStatic) + return false; + var sig = method.MethodSig; + if (sig == null || sig.GetParamCount() != 3) + return false; + if (sig.RetType.GetElementType() != ElementType.Boolean) + return false; + if (sig.Params[0].GetElementType() != ElementType.Object) + return false; + if (sig.Params[1].GetElementType() != ElementType.Object) + return false; + var arg2 = sig.Params[2] as ValueTypeSig; + if (arg2 == null || arg2.TypeDef == null || !arg2.TypeDef.IsEnum) + return false; + + return true; + } + + MethodDef FindCompareEq(TypeDef type) { + foreach (var method in type.Methods) { + if (!CheckCompareEqMethodSig(method)) + continue; + if (method.Body == null) + continue; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 5; i++) { + var ldarg0 = instrs[i]; + if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0) + continue; + if (!CheckUnboxAny(instrs[i + 1], ElementType.I4)) + continue; + var ldarg1 = instrs[i + 2]; + if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1) + continue; + if (!CheckUnboxAny(instrs[i + 3], ElementType.I4)) + continue; + if (instrs[i + 4].OpCode.Code != Code.Ceq) + continue; + if (!instrs[i + 5].IsStloc()) + continue; + + return method; + } + } + + return null; + } + + static bool CheckCompareEqMethodSig(MethodDef method) { + return method != null && + method.IsStatic && + method.MethodSig.GetParamCount() == 2 && + method.MethodSig.RetType.GetElementType() == ElementType.Boolean && + method.MethodSig.Params[0].GetElementType() == ElementType.Object && + method.MethodSig.Params[1].GetElementType() == ElementType.Object; + } + + MethodDef FindCompareEqz(TypeDef type) { + foreach (var method in type.Methods) { + if (!CheckCompareEqzMethodSig(method)) + continue; + if (method.Body == null) + continue; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 4; i++) { + var ldarg0 = instrs[i]; + if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0) + continue; + if (!CheckUnboxAny(instrs[i + 1], ElementType.I4)) + continue; + var ldci4 = instrs[i + 2]; + if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 0) + continue; + if (instrs[i + 3].OpCode.Code != Code.Ceq) + continue; + if (!instrs[i + 4].IsStloc()) + continue; + + return method; + } + } + + return null; + } + + static bool CheckCompareEqzMethodSig(MethodDef method) { + return method != null && + method.IsStatic && + method.MethodSig.GetParamCount() == 1 && + method.MethodSig.RetType.GetElementType() == ElementType.Boolean && + method.MethodSig.Params[0].GetElementType() == ElementType.Object; + } + + public bool FindArithmeticMethods() { + foreach (var type in module.Types) { + if (type.BaseType == null || type.BaseType.FullName != "System.Object") + continue; + if (type.Methods.Count != 15) + continue; + ArithmeticSubOvfUn = FindArithmeticSubOvfUn(type); + if (ArithmeticSubOvfUn == null) + continue; + ArithmeticMulOvfUn = FindArithmeticMulOvfUn(type); + ArithmeticRemUn = FindArithmeticRemUn(type); + ArithmeticRem = FindArithmeticRem(type); + ArithmeticDivUn = FindArithmeticDivUn(type); + ArithmeticDiv = FindArithmeticDiv(type); + ArithmeticMul = FindArithmeticMul(type); + ArithmeticMulOvf = FindArithmeticMulOvf(type); + ArithmeticSub = FindArithmeticSub(type); + ArithmeticSubOvf = FindArithmeticSubOvf(type); + ArithmeticAddOvfUn = FindArithmeticAddOvfUn(type); + ArithmeticAddOvf = FindArithmeticAddOvf(type); + ArithmeticAdd = FindArithmeticAdd(type); + + if (ArithmeticSubOvfUn != null && ArithmeticMulOvfUn != null && + ArithmeticRemUn != null && ArithmeticRem != null && + ArithmeticDivUn != null && ArithmeticDiv != null && + ArithmeticMul != null && ArithmeticMulOvf != null && + ArithmeticSub != null && ArithmeticSubOvf != null && + ArithmeticAddOvfUn != null && ArithmeticAddOvf != null && + ArithmeticAdd != null) + return true; + } + + return false; + } + + MethodDef FindArithmeticSubOvfUn(TypeDef type) { + return FindArithmeticOpUn(type, Code.Sub_Ovf_Un); + } + + MethodDef FindArithmeticMulOvfUn(TypeDef type) { + return FindArithmeticOpUn(type, Code.Mul_Ovf_Un); + } + + MethodDef FindArithmeticAddOvfUn(TypeDef type) { + return FindArithmeticOpUn(type, Code.Add_Ovf_Un); + } + + MethodDef FindArithmeticOpUn(TypeDef type, Code code) { + foreach (var method in type.Methods) { + if (!CheckArithmeticUnMethodSig(method)) + continue; + if (method.Body == null) + continue; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 8; i++) { + var ldarg0 = instrs[i]; + if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0) + continue; + if (!CheckCallvirt(instrs[i + 1], "System.Int32", "()")) + continue; + if (instrs[i + 2].OpCode.Code != Code.Conv_Ovf_U4) + continue; + var ldarg1 = instrs[i + 3]; + if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1) + continue; + if (!CheckCallvirt(instrs[i + 4], "System.Int32", "()")) + continue; + if (instrs[i + 5].OpCode.Code != Code.Conv_Ovf_U4) + continue; + if (instrs[i + 6].OpCode.Code != code) + continue; + if (!CheckBox(instrs[i + 7], ElementType.U4)) + continue; + if (!instrs[i + 8].IsStloc()) + continue; + + return method; + } + } + + return null; + } + + static bool CheckArithmeticUnMethodSig(MethodDef method) { + return method != null && + method.IsStatic && + method.MethodSig.GetParamCount() == 2 && + method.MethodSig.RetType.GetElementType() == ElementType.Object && + method.MethodSig.Params[0].GetElementType() == ElementType.Class && + method.MethodSig.Params[1].GetElementType() == ElementType.Class; + } + + MethodDef FindArithmeticRemUn(TypeDef type) { + return FindArithmeticDivOrRemUn(type, Code.Rem_Un); + } + + MethodDef FindArithmeticDivUn(TypeDef type) { + return FindArithmeticDivOrRemUn(type, Code.Div_Un); + } + + MethodDef FindArithmeticDivOrRemUn(TypeDef type, Code code) { + foreach (var method in type.Methods) { + if (!CheckArithmeticUnMethodSig(method)) + continue; + if (method.Body == null) + continue; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 7; i++) { + var ldarg0 = instrs[i]; + if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0) + continue; + if (!CheckCallvirt(instrs[i + 1], "System.Int32", "()")) + continue; + var ldarg1 = instrs[i + 2]; + if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1) + continue; + if (!CheckCallvirt(instrs[i + 3], "System.Int32", "()")) + continue; + if (instrs[i + 4].OpCode.Code != code) + continue; + if (!CheckBox(instrs[i + 5], ElementType.U4)) + continue; + if (!instrs[i + 6].IsStloc()) + continue; + + return method; + } + } + + return null; + } + + MethodDef FindArithmeticRem(TypeDef type) { + return FindArithmeticOther(type, Code.Rem); + } + + MethodDef FindArithmeticDiv(TypeDef type) { + return FindArithmeticOther(type, Code.Div); + } + + MethodDef FindArithmeticMul(TypeDef type) { + return FindArithmeticOther(type, Code.Mul); + } + + MethodDef FindArithmeticMulOvf(TypeDef type) { + return FindArithmeticOther(type, Code.Mul_Ovf); + } + + MethodDef FindArithmeticSub(TypeDef type) { + return FindArithmeticOther(type, Code.Sub); + } + + MethodDef FindArithmeticSubOvf(TypeDef type) { + return FindArithmeticOther(type, Code.Sub_Ovf); + } + + MethodDef FindArithmeticAdd(TypeDef type) { + return FindArithmeticOther(type, Code.Add); + } + + MethodDef FindArithmeticAddOvf(TypeDef type) { + return FindArithmeticOther(type, Code.Add_Ovf); + } + + MethodDef FindArithmeticOther(TypeDef type, Code code) { + foreach (var method in type.Methods) { + if (!CheckArithmeticOtherMethodSig(method)) + continue; + if (method.Body == null) + continue; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 6; i++) { + var ldarg0 = instrs[i]; + if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0) + continue; + if (!CheckUnboxAny(instrs[i + 1], ElementType.I4)) + continue; + var ldarg1 = instrs[i + 2]; + if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1) + continue; + if (!CheckUnboxAny(instrs[i + 3], ElementType.I4)) + continue; + if (instrs[i + 4].OpCode.Code != code) + continue; + if (!CheckBox(instrs[i + 5], ElementType.I4)) + continue; + + return method; + } + } + + return null; + } + + static bool CheckArithmeticOtherMethodSig(MethodDef method) { + return method != null && + method.IsStatic && + method.MethodSig.GetParamCount() == 2 && + method.MethodSig.RetType.GetElementType() == ElementType.Object && + method.MethodSig.Params[0].GetElementType() == ElementType.Object && + method.MethodSig.Params[1].GetElementType() == ElementType.Object; + } + + public bool FindUnaryOpsMethods() { + UnaryNot = FindUnaryOpMethod(Code.Not); + UnaryNeg = FindUnaryOpMethod(Code.Neg); + return UnaryNot != null && UnaryNeg != null; + } + + MethodDef FindUnaryOpMethod(Code code) { + foreach (var type in module.Types) { + if (type.BaseType != VmHandlerBaseType) + continue; + if (type.Methods.Count != 4) + continue; + foreach (var method in type.Methods) { + if (!method.HasBody || !method.IsStatic) + continue; + if (!DotNetUtils.IsMethod(method, "System.Object", "(System.Object)")) + continue; + if (CountThrows(method) != 1) + continue; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 4; i++) { + var ldarg = instrs[i]; + if (!ldarg.IsLdarg() || ldarg.GetParameterIndex() != 0) + continue; + if (!CheckUnboxAny(instrs[i + 1], ElementType.I4)) + continue; + if (instrs[i + 2].OpCode.Code != code) + continue; + if (!CheckBox(instrs[i + 3], ElementType.I4)) + continue; + if (!instrs[i + 4].IsStloc()) + continue; + + return method; + } + } + } + return null; + } + + static int CountThrows(MethodDef method) { + if (method == null || method.Body == null) + return 0; + int count = 0; + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code == Code.Throw) + count++; + } + return count; + } + + public bool FindArgsLocals() { + var vmState = FindVmState(); + if (vmState == null) + return false; + + var ctor = vmState.FindMethod(".ctor"); + return FindArgsLocals(ctor, 1, out ArgsGet, out ArgsSet) && + FindArgsLocals(ctor, 2, out LocalsGet, out LocalsSet); + } + + TypeDef FindVmState() { + if (VmHandlerBaseType == null) + return null; + foreach (var method in VmHandlerBaseType.Methods) { + if (method.IsStatic || !method.IsAbstract) + continue; + if (method.Parameters.Count != 2) + continue; + var arg1 = method.Parameters[1].Type.TryGetTypeDef(); + if (arg1 == null) + continue; + + return arg1; + } + return null; + } + + static bool FindArgsLocals(MethodDef ctor, int arg, out MethodDef getter, out MethodDef setter) { + getter = null; + setter = null; + if (ctor == null || !ctor.HasBody) + return false; + + setter = FindSetter(ctor, arg); + if (setter == null) + return false; + + var propField = GetPropField(setter); + if (propField == null) + return false; + + getter = FindGetter(ctor.DeclaringType, propField); + return getter != null; + } + + static MethodDef FindSetter(MethodDef ctor, int arg) { + if (ctor == null || !ctor.HasBody) + return null; + + var instrs = ctor.Body.Instructions; + for (int i = 0; i < instrs.Count - 1; i++) { + var ldarg = instrs[i]; + if (!ldarg.IsLdarg() || ldarg.GetParameterIndex() != arg) + continue; + var call = instrs[i + 1]; + if (call.OpCode.Code != Code.Call) + continue; + var method = call.Operand as MethodDef; + if (method == null) + continue; + if (method.DeclaringType != ctor.DeclaringType) + continue; + + return method; + } + + return null; + } + + static FieldDef GetPropField(MethodDef method) { + if (method == null || !method.HasBody) + return null; + + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code != Code.Stfld) + continue; + var field = instr.Operand as FieldDef; + if (field == null || field.DeclaringType != method.DeclaringType) + continue; + + return field; + } + + return null; + } + + static MethodDef FindGetter(TypeDef type, FieldDef propField) { + foreach (var method in type.Methods) { + if (method.IsStatic || !method.HasBody) + continue; + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code != Code.Ldfld) + continue; + if (instr.Operand != propField) + continue; + + return method; + } + } + + return null; + } + + static bool CheckCallvirt(Instruction instr, string returnType, string parameters) { + if (instr.OpCode.Code != Code.Callvirt) + return false; + return DotNetUtils.IsMethod(instr.Operand as IMethod, returnType, parameters); + } + + bool CheckUnboxAny(Instruction instr, ElementType expectedType) { + if (instr == null || instr.OpCode.Code != Code.Unbox_Any) + return false; + var typeSig = module.CorLibTypes.GetCorLibTypeSig(instr.Operand as ITypeDefOrRef); + return typeSig.GetElementType() == expectedType; + } + + bool CheckBox(Instruction instr, ElementType expectedType) { + if (instr == null || instr.OpCode.Code != Code.Box) + return false; + var typeSig = module.CorLibTypes.GetCorLibTypeSig(instr.Operand as ITypeDefOrRef); + return typeSig.GetElementType() == expectedType; + } + + static int CountVirtual(TypeDef type) { + int count = 0; + foreach (var method in type.Methods) { + if (method.IsVirtual) + count++; + } + return count; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs new file mode 100644 index 00000000..6153040b --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18052 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class CsvmResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CsvmResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("de4dot.code.deobfuscators.Agile_NET.vm.v2.CsvmResources", typeof(CsvmResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] CSVM1_v2 { + get { + object obj = ResourceManager.GetObject("CSVM1_v2", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] CSVM2_v2 { + get { + object obj = ResourceManager.GetObject("CSVM2_v2", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] CSVM3_v2 { + get { + object obj = ResourceManager.GetObject("CSVM3_v2", 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 new file mode 100644 index 00000000..254aef83 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + CSVM1_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CSVM2_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CSVM3_v2.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 new file mode 100644 index 00000000..3cc2f2f6 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmToCilMethodConverter.cs @@ -0,0 +1,64 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class CsvmToCilMethodConverter : CsvmToCilMethodConverterBase { + VmOpCodeHandlerDetector opCodeDetector; + + public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector) + : base(deobfuscatorContext, module) { + this.opCodeDetector = opCodeDetector; + } + + 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); + + int numVmInstrs = reader.ReadInt32(); + var vmInstrs = new ushort[numVmInstrs]; + for (int i = 0; i < numVmInstrs; i++) + vmInstrs[i] = reader.ReadUInt16(); + + uint offset = 0; + for (int vmInstrIndex = 0; vmInstrIndex < numVmInstrs; vmInstrIndex++) { + var composite = opCodeDetector.Handlers[vmInstrs[vmInstrIndex]]; + var handlerInfos = composite.OpCodeHandlerInfos; + if (handlerInfos.Count == 0) + handlerInfos = new List() { new OpCodeHandlerInfo(HandlerTypeCode.Nop, null) }; + for (int hi = 0; hi < handlerInfos.Count; hi++) { + var instr = handlerInfoReader.Read(handlerInfos[hi].TypeCode, reader); + instr.Offset = offset; + offset += (uint)GetInstructionSize(instr); + SetCilToVmIndex(instr, vmInstrIndex); + if (hi == 0) + SetVmIndexToCil(instr, vmInstrIndex); + instrs.Add(instr); + } + } + return instrs; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/HandlerTypeCode.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/HandlerTypeCode.cs new file mode 100644 index 00000000..d679c54d --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/HandlerTypeCode.cs @@ -0,0 +1,102 @@ +/* + Copyright (C) 2011-2013 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 . +*/ + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + // These constants are hard coded. Don't change the values (i.e., only append if more are needed) + enum HandlerTypeCode { + Add, + Add_Ovf, + Add_Ovf_Un, + And, + Beq, + Bge, + Bge_Un, + Bgt, + Bgt_Un, + Ble, + Ble_Un, + Blt, + Blt_Un, + Bne_Un, + Box, + Br, + Brfalse, + Brtrue, + Call, + Callvirt, + Castclass, + Ceq, + Cgt, + Cgt_Un, + Clt, + Clt_Un, + Conv, + Div, + Div_Un, + Dup, + Endfinally, + Initobj, + Isinst, + Ldarg, + Ldarga, + Ldc, + Ldelem, + Ldelema, + Ldfld_Ldsfld, + Ldflda_Ldsflda, + Ldftn, + Ldlen, + Ldloc, + Ldloca, + Ldobj, + Ldstr, + Ldtoken, + Ldvirtftn, + Leave, + Mul, + Mul_Ovf, + Mul_Ovf_Un, + Neg, + Newarr, + Newobj, + Nop, + Not, + Or, + Pop, + Rem, + Rem_Un, + Ret, + Rethrow, + Shl, + Shr, + Shr_Un, + Starg, + Stelem, + Stfld_Stsfld, + Stloc, + Stobj, + Sub, + Sub_Ovf, + Sub_Ovf_Un, + Switch, + Throw, + Unbox_Any, + Xor, + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodFinder.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodFinder.cs new file mode 100644 index 00000000..ca206830 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodFinder.cs @@ -0,0 +1,109 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System.Collections.Generic; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class MethodFinder { + readonly IList handlerInfos; + readonly PrimitiveHandlerMethod handlerMethod; + + class SigState { + public readonly MethodSigInfo SigInfo; + + public SigState(PrimitiveHandlerMethod handlerMethod) { + this.SigInfo = handlerMethod.Sig; + } + } + + public MethodFinder(IList handlerInfos, PrimitiveHandlerMethod handlerMethod) { + this.handlerInfos = handlerInfos; + this.handlerMethod = handlerMethod; + } + + public OpCodeHandler FindHandler() { + var handler = FindHandler(new SigState(handlerMethod)); + if (handler == null) + return null; + + return new OpCodeHandler(handler, handlerMethod.Method.DeclaringType, handlerMethod); + } + + OpCodeHandlerInfo FindHandler(SigState execSigState) { + foreach (var handler in handlerInfos) { + if (Matches(handler.ExecSig, execSigState)) + return handler; + } + return null; + } + + struct MatchInfo { + public int HandlerIndex; + public int SigIndex; + + public MatchInfo(int handlerIndex, int sigIndex) { + this.HandlerIndex = handlerIndex; + this.SigIndex = sigIndex; + } + } + + Dictionary sigIndexToHandlerIndex = new Dictionary(); + Dictionary handlerIndexToSigIndex = new Dictionary(); + Stack stack = new Stack(); + bool Matches(MethodSigInfo handlerSig, SigState sigState) { + stack.Clear(); + sigIndexToHandlerIndex.Clear(); + handlerIndexToSigIndex.Clear(); + var handlerInfos = handlerSig.BlockInfos; + var sigInfos = sigState.SigInfo.BlockInfos; + + stack.Push(new MatchInfo(0, 0)); + while (stack.Count > 0) { + var info = stack.Pop(); + + int handlerIndex, sigIndex; + bool hasVisitedHandler = handlerIndexToSigIndex.TryGetValue(info.HandlerIndex, out sigIndex); + bool hasVisitedSig = sigIndexToHandlerIndex.TryGetValue(info.SigIndex, out handlerIndex); + if (hasVisitedHandler != hasVisitedSig) + return false; + if (hasVisitedHandler) { + if (handlerIndex != info.HandlerIndex || sigIndex != info.SigIndex) + return false; + continue; + } + handlerIndexToSigIndex[info.HandlerIndex] = info.SigIndex; + sigIndexToHandlerIndex[info.SigIndex] = info.HandlerIndex; + + var handlerBlock = handlerInfos[info.HandlerIndex]; + var sigBlock = sigInfos[info.SigIndex]; + + if (!handlerBlock.Equals(sigBlock)) + return false; + + for (int i = 0; i < handlerBlock.Targets.Count; i++) { + int handlerTargetIndex = handlerBlock.Targets[i]; + int sigTargetIndex = sigBlock.Targets[i]; + stack.Push(new MatchInfo(handlerTargetIndex, sigTargetIndex)); + } + } + + return true; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodSigInfoCreator.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodSigInfoCreator.cs new file mode 100644 index 00000000..c311e0a9 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodSigInfoCreator.cs @@ -0,0 +1,737 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using dnlib.DotNet.MD; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class MethodSigInfo { + readonly List blockInfos; + + public List BlockInfos { + get { return blockInfos; } + } + + public MethodSigInfo() { + this.blockInfos = new List(); + } + + public MethodSigInfo(IEnumerable blockInfos) { + this.blockInfos = new List(blockInfos); + } + } + + class BlockInfo : IEquatable { + readonly List targets; + + public byte[] Hash { get; set; } + public List Targets { + get { return targets; } + } + + public BlockInfo() { + this.targets = new List(); + } + + public BlockInfo(byte[] hash, IEnumerable targets) { + this.Hash = hash; + this.targets = new List(targets); + } + + public override string ToString() { + if (Hash == null) + return ""; + return BitConverter.ToString(Hash).Replace("-", string.Empty); + } + + public bool Equals(BlockInfo other) { + return Equals(Hash, other.Hash) && + Targets.Count == other.Targets.Count; + } + + bool Equals(byte[] a, byte[] b) { + if (a == b) + return true; + if (a == null || b == null) + return false; + if (a.Length != b.Length) + return false; + for (int i = 0; i < a.Length; i++) { + if (a[i] != b[i]) + return false; + } + return true; + } + } + + class MethodSigInfoCreator { + MethodSigInfo methodSigInfo; + Blocks blocks; + IList allBlocks; + Dictionary blockToInfo; + Dictionary methodToId = new Dictionary(); + + public void AddId(object key, int id) { + if (key != null) + methodToId[key] = id; + } + + int GetId(object key) { + if (key == null) + return int.MinValue; + + int id; + if (methodToId.TryGetValue(key, out id)) + return id; + return int.MinValue + 1; + } + + public MethodSigInfo Create(Blocks blocks) { + methodSigInfo = new MethodSigInfo(); + + this.blocks = blocks; + allBlocks = blocks.MethodBlocks.GetAllBlocks(); + + blockToInfo = new Dictionary(); + foreach (var block in allBlocks) { + var blockInfo = new BlockInfo(); + blockToInfo[block] = blockInfo; + methodSigInfo.BlockInfos.Add(blockInfo); + } + + foreach (var block in allBlocks) { + var blockInfo = blockToInfo[block]; + Update(blockInfo, block); + if (block.FallThrough != null) + blockInfo.Targets.Add(allBlocks.IndexOf(block.FallThrough)); + if (block.Targets != null) { + foreach (var target in block.Targets) + blockInfo.Targets.Add(allBlocks.IndexOf(target)); + } + } + + return methodSigInfo; + } + + void Update(BlockInfo blockInfo, Block block) { + using (var hasher = MD5.Create()) { + bool emptyHash; + using (var outStream = new NullStream()) { + using (var csStream = new CryptoStream(outStream, hasher, CryptoStreamMode.Write)) { + var writer = new BinaryWriter(csStream); + Update(writer, blockInfo, block); + } + emptyHash = outStream.Length == 0; + } + if (!emptyHash) + blockInfo.Hash = hasher.Hash; + } + } + + void Update(BinaryWriter writer, BlockInfo blockInfo, Block block) { + var instrs = block.Instructions; + for (int i = 0; i < instrs.Count; i++) { + var instr = instrs[i]; + switch (instr.OpCode.Code) { + case Code.Beq_S: + case Code.Bge_S: + case Code.Bgt_S: + case Code.Ble_S: + case Code.Blt_S: + case Code.Bne_Un_S: + case Code.Bge_Un_S: + case Code.Bgt_Un_S: + case Code.Ble_Un_S: + case Code.Blt_Un_S: + case Code.Brfalse_S: + case Code.Brtrue_S: + case Code.Leave_S: + case Code.Beq: + case Code.Bge: + case Code.Bgt: + case Code.Ble: + case Code.Blt: + case Code.Bne_Un: + case Code.Bge_Un: + case Code.Bgt_Un: + case Code.Ble_Un: + case Code.Blt_Un: + case Code.Brfalse: + case Code.Brtrue: + case Code.Leave: + writer.Write((ushort)SimplifyBranch(instr.OpCode.Code)); + break; + + case Code.Switch: + writer.Write((ushort)instr.OpCode.Code); + writer.Write(blockInfo.Targets.Count); + break; + + case Code.Br_S: + case Code.Br: + break; + + case Code.Ret: + break; + + case Code.Ldc_I4_M1: + case Code.Ldc_I4_0: + case Code.Ldc_I4_1: + case Code.Ldc_I4_2: + case Code.Ldc_I4_3: + case Code.Ldc_I4_4: + case Code.Ldc_I4_5: + case Code.Ldc_I4_6: + case Code.Ldc_I4_7: + case Code.Ldc_I4_8: + case Code.Ldc_I4: + case Code.Ldc_I4_S: + writer.Write((ushort)Code.Ldc_I4); + writer.Write(instr.GetLdcI4Value()); + break; + + case Code.Ldc_I8: + writer.Write((ushort)instr.OpCode.Code); + writer.Write((long)instr.Operand); + break; + + case Code.Ldc_R4: + writer.Write((ushort)instr.OpCode.Code); + writer.Write((float)instr.Operand); + break; + + case Code.Ldc_R8: + writer.Write((ushort)instr.OpCode.Code); + writer.Write((double)instr.Operand); + break; + + case Code.Ldfld: + var typeField = instr.Operand as FieldDef; + bool isField = IsTypeField(typeField); + writer.Write((ushort)instr.OpCode.Code); + writer.Write(isField); + if (isField) { + if (i + 1 < instrs.Count && instrs[i + 1].IsLdcI4()) + i++; + writer.Write(GetFieldId(typeField)); + } + else + Write(writer, instr.Operand); + break; + + case Code.Call: + case Code.Callvirt: + case Code.Newobj: + case Code.Jmp: + case Code.Ldftn: + case Code.Ldvirtftn: + case Code.Ldtoken: + case Code.Stfld: + case Code.Ldsfld: + case Code.Stsfld: + case Code.Ldflda: + case Code.Ldsflda: + case Code.Cpobj: + case Code.Ldobj: + case Code.Castclass: + case Code.Isinst: + case Code.Unbox: + case Code.Stobj: + case Code.Box: + case Code.Newarr: + case Code.Ldelema: + case Code.Ldelem: + case Code.Stelem: + case Code.Unbox_Any: + case Code.Refanyval: + case Code.Mkrefany: + case Code.Initobj: + case Code.Constrained: + case Code.Sizeof: + writer.Write((ushort)instr.OpCode.Code); + Write(writer, instr.Operand); + break; + + case Code.Ldstr: + writer.Write((ushort)instr.OpCode.Code); + break; + + case Code.Ldarg: + case Code.Ldarg_S: + case Code.Ldarg_0: + case Code.Ldarg_1: + case Code.Ldarg_2: + case Code.Ldarg_3: + writer.Write((ushort)Code.Ldarg); + writer.Write(instr.Instruction.GetParameterIndex()); + break; + + case Code.Ldarga: + case Code.Ldarga_S: + writer.Write((ushort)Code.Ldarga); + writer.Write(instr.Instruction.GetParameterIndex()); + break; + + case Code.Starg: + case Code.Starg_S: + writer.Write((ushort)Code.Starg); + writer.Write(instr.Instruction.GetParameterIndex()); + break; + + case Code.Ldloc: + case Code.Ldloc_S: + case Code.Ldloc_0: + case Code.Ldloc_1: + case Code.Ldloc_2: + case Code.Ldloc_3: + writer.Write((ushort)Code.Ldloc); + break; + + case Code.Ldloca: + case Code.Ldloca_S: + writer.Write((ushort)Code.Ldloca); + break; + + case Code.Stloc: + case Code.Stloc_S: + case Code.Stloc_0: + case Code.Stloc_1: + case Code.Stloc_2: + case Code.Stloc_3: + writer.Write((ushort)Code.Stloc); + break; + + case Code.Ldnull: + case Code.Throw: + case Code.Rethrow: + case Code.Ldlen: + case Code.Ckfinite: + case Code.Arglist: + case Code.Localloc: + case Code.Volatile: + case Code.Tailcall: + case Code.Cpblk: + case Code.Initblk: + case Code.Refanytype: + case Code.Readonly: + case Code.Break: + case Code.Endfinally: + case Code.Endfilter: + writer.Write((ushort)instr.OpCode.Code); + break; + + case Code.Calli: + writer.Write((ushort)instr.OpCode.Code); + Write(writer, instr.Operand); + break; + + case Code.Unaligned: + writer.Write((ushort)instr.OpCode.Code); + writer.Write((byte)instr.Operand); + break; + + default: + break; + } + } + } + + void Write(BinaryWriter writer, object op) { + var fd = op as FieldDef; + if (fd != null) { + Write(writer, fd); + return; + } + + var mr = op as MemberRef; + if (mr != null) { + Write(writer, mr); + return; + } + + var md = op as MethodDef; + if (md != null) { + Write(writer, md); + return; + } + + var ms = op as MethodSpec; + if (ms != null) { + Write(writer, ms); + return; + } + + var td = op as TypeDef; + if (td != null) { + Write(writer, td); + return; + } + + var tr = op as TypeRef; + if (tr != null) { + Write(writer, tr); + return; + } + + var ts = op as TypeSpec; + if (ts != null) { + Write(writer, ts); + return; + } + + var fsig = op as FieldSig; + if (fsig != null) { + Write(writer, fsig); + return; + } + + var msig = op as MethodSig; + if (msig != null) { + Write(writer, msig); + return; + } + + var gsig = op as GenericInstMethodSig; + if (gsig != null) { + Write(writer, gsig); + return; + } + + var asmRef = op as AssemblyRef; + if (asmRef != null) { + Write(writer, asmRef); + return; + } + + writer.Write((byte)ObjectType.Unknown); + } + + enum ObjectType : byte { + // 00..3F = Table.XXX values. + Unknown = 0x40, + TypeSig = 0x41, + FieldSig = 0x42, + MethodSig = 0x43, + GenericInstMethodSig = 0x44, + } + + void Write(BinaryWriter writer, TypeSig sig) { + Write(writer, sig, 0); + } + + void Write(BinaryWriter writer, TypeSig sig, int level) { + if (level++ > 20) + return; + + writer.Write((byte)ObjectType.TypeSig); + var etype = sig.GetElementType(); + writer.Write((byte)etype); + switch (etype) { + case ElementType.Ptr: + case ElementType.ByRef: + case ElementType.SZArray: + case ElementType.Pinned: + Write(writer, sig.Next, level); + break; + + case ElementType.Array: + var arySig = (ArraySig)sig; + writer.Write(arySig.Rank); + writer.Write(arySig.Sizes.Count); + writer.Write(arySig.LowerBounds.Count); + Write(writer, sig.Next, level); + break; + + case ElementType.CModReqd: + case ElementType.CModOpt: + Write(writer, ((ModifierSig)sig).Modifier); + Write(writer, sig.Next, level); + break; + + case ElementType.ValueArray: + writer.Write(((ValueArraySig)sig).Size); + Write(writer, sig.Next, level); + break; + + case ElementType.Module: + writer.Write(((ModuleSig)sig).Index); + Write(writer, sig.Next, level); + break; + + case ElementType.GenericInst: + var gis = (GenericInstSig)sig; + Write(writer, gis.GenericType, level); + foreach (var ga in gis.GenericArguments) + Write(writer, ga, level); + Write(writer, sig.Next, level); + break; + + case ElementType.FnPtr: + Write(writer, ((FnPtrSig)sig).Signature); + break; + + case ElementType.Var: + case ElementType.MVar: + writer.Write(((GenericSig)sig).Number); + break; + + case ElementType.ValueType: + case ElementType.Class: + Write(writer, ((TypeDefOrRefSig)sig).TypeDefOrRef); + break; + + case ElementType.End: + case ElementType.Void: + case ElementType.Boolean: + case ElementType.Char: + case ElementType.I1: + case ElementType.U1: + case ElementType.I2: + case ElementType.U2: + case ElementType.I4: + case ElementType.U4: + case ElementType.I8: + case ElementType.U8: + case ElementType.R4: + case ElementType.R8: + case ElementType.String: + case ElementType.TypedByRef: + case ElementType.I: + case ElementType.U: + case ElementType.R: + case ElementType.Object: + case ElementType.Internal: + case ElementType.Sentinel: + default: + break; + } + } + + void Write(BinaryWriter writer, FieldSig sig) { + writer.Write((byte)ObjectType.FieldSig); + writer.Write((byte)(sig == null ? 0 : sig.GetCallingConvention())); + Write(writer, sig.GetFieldType()); + } + + void Write(BinaryWriter writer, MethodSig sig) { + writer.Write((byte)ObjectType.MethodSig); + writer.Write((byte)(sig == null ? 0 : sig.GetCallingConvention())); + Write(writer, sig.GetRetType()); + foreach (var p in sig.GetParams()) + Write(writer, p); + writer.Write(sig.GetParamCount()); + bool hasParamsAfterSentinel = sig.GetParamsAfterSentinel() != null; + writer.Write(hasParamsAfterSentinel); + if (hasParamsAfterSentinel) { + foreach (var p in sig.GetParamsAfterSentinel()) + Write(writer, p); + } + } + + void Write(BinaryWriter writer, GenericInstMethodSig sig) { + writer.Write((byte)ObjectType.GenericInstMethodSig); + writer.Write((byte)(sig == null ? 0 : sig.GetCallingConvention())); + foreach (var ga in sig.GetGenericArguments()) + Write(writer, ga); + } + + void Write(BinaryWriter writer, FieldDef fd) { + writer.Write((byte)Table.Field); + Write(writer, fd.DeclaringType); + var attrMask = FieldAttributes.Static | FieldAttributes.InitOnly | + FieldAttributes.Literal | FieldAttributes.SpecialName | + FieldAttributes.PinvokeImpl | FieldAttributes.RTSpecialName; + writer.Write((ushort)(fd == null ? 0 : fd.Attributes & attrMask)); + Write(writer, fd == null ? null : fd.Signature); + } + + void Write(BinaryWriter writer, MemberRef mr) { + writer.Write((byte)Table.MemberRef); + var parent = mr == null ? null : mr.Class; + Write(writer, parent); + bool canWriteName = IsFromNonObfuscatedAssembly(parent); + writer.Write(canWriteName); + if (canWriteName) + writer.Write(mr.Name); + Write(writer, mr == null ? null : mr.Signature); + } + + void Write(BinaryWriter writer, MethodDef md) { + writer.Write((byte)Table.Method); + Write(writer, md.DeclaringType); + var attrMask1 = MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask | + MethodImplAttributes.ForwardRef | MethodImplAttributes.PreserveSig | + MethodImplAttributes.InternalCall; + writer.Write((ushort)(md == null ? 0 : md.ImplAttributes & attrMask1)); + var attrMask2 = MethodAttributes.Static | MethodAttributes.Virtual | + MethodAttributes.HideBySig | MethodAttributes.VtableLayoutMask | + MethodAttributes.CheckAccessOnOverride | MethodAttributes.Abstract | + MethodAttributes.SpecialName | MethodAttributes.PinvokeImpl | + MethodAttributes.UnmanagedExport | MethodAttributes.RTSpecialName; + writer.Write((ushort)(md == null ? 0 : md.Attributes & attrMask2)); + Write(writer, md == null ? null : md.Signature); + writer.Write(md == null ? 0 : md.ParamDefs.Count); + writer.Write(md == null ? 0 : md.GenericParameters.Count); + writer.Write(md == null ? false : md.HasImplMap); + writer.Write(GetId(md)); + } + + void Write(BinaryWriter writer, MethodSpec ms) { + writer.Write((byte)Table.MethodSpec); + Write(writer, ms == null ? null : ms.Method); + Write(writer, ms == null ? null : ms.Instantiation); + } + + void Write(BinaryWriter writer, TypeDef td) { + writer.Write((byte)Table.TypeDef); + Write(writer, td == null ? null : td.BaseType); + var attrMask = TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask | + TypeAttributes.Abstract | TypeAttributes.SpecialName | + TypeAttributes.Import | TypeAttributes.WindowsRuntime | + TypeAttributes.StringFormatMask | TypeAttributes.RTSpecialName; + writer.Write((uint)(td == null ? 0 : td.Attributes & attrMask)); + Write(writer, td == null ? null : td.BaseType); + writer.Write(td == null ? 0 : td.GenericParameters.Count); + writer.Write(td == null ? 0 : td.Interfaces.Count); + if (td != null) { + foreach (var iface in td.Interfaces) + Write(writer, iface); + } + writer.Write(GetId(td)); + } + + void Write(BinaryWriter writer, TypeRef tr) { + writer.Write((byte)Table.TypeRef); + Write(writer, tr == null ? null : tr.ResolutionScope); + bool canWriteName = IsFromNonObfuscatedAssembly(tr); + writer.Write(canWriteName); + if (canWriteName) { + writer.Write(tr.Namespace); + writer.Write(tr.Name); + } + } + + void Write(BinaryWriter writer, TypeSpec ts) { + writer.Write((byte)Table.TypeSpec); + Write(writer, ts == null ? null : ts.TypeSig); + } + + void Write(BinaryWriter writer, AssemblyRef asmRef) { + writer.Write((byte)Table.AssemblyRef); + + bool canWriteAsm = IsNonObfuscatedAssembly(asmRef); + writer.Write(canWriteAsm); + if (canWriteAsm) { + bool hasPk = !PublicKeyBase.IsNullOrEmpty2(asmRef.PublicKeyOrToken); + writer.Write(hasPk); + if (hasPk) + writer.Write(PublicKeyBase.ToPublicKeyToken(asmRef.PublicKeyOrToken).Data); + writer.Write(asmRef.Name); + writer.Write(asmRef.Culture); + } + } + + static bool IsFromNonObfuscatedAssembly(IMemberRefParent mrp) { + return IsFromNonObfuscatedAssembly(mrp as TypeRef); + } + + static bool IsFromNonObfuscatedAssembly(TypeRef tr) { + if (tr == null) + return false; + + for (int i = 0; i < 100; i++) { + var asmRef = tr.ResolutionScope as AssemblyRef; + if (asmRef != null) + return IsNonObfuscatedAssembly(asmRef); + + var tr2 = tr.ResolutionScope as TypeRef; + if (tr2 != null) { + tr = tr2; + continue; + } + + break; + } + + return false; + } + + static bool IsNonObfuscatedAssembly(AssemblyRef asmRef) { + if (asmRef == null) + return false; + // The only external asm refs it uses... + if (asmRef.Name != "mscorlib" && asmRef.Name != "System") + return false; + + return true; + } + + bool IsTypeField(FieldDef fd) { + return fd != null && fd.DeclaringType == blocks.Method.DeclaringType; + } + + int GetFieldId(FieldDef fd) { + if (fd == null) + return int.MinValue; + var fieldType = fd.FieldSig.GetFieldType(); + if (fieldType == null) + return int.MinValue + 1; + + int result = 0; + for (int i = 0; i < 100; i++) { + result += (int)fieldType.ElementType; + if (fieldType.Next == null) + break; + result += 0x100; + fieldType = fieldType.Next; + } + + var td = fieldType.TryGetTypeDef(); + if (td != null && td.IsEnum) + return result + 0x10000000; + return result; + } + + static Code SimplifyBranch(Code code) { + switch (code) { + case Code.Beq_S: return Code.Beq; + case Code.Bge_S: return Code.Bge; + case Code.Bgt_S: return Code.Bgt; + case Code.Ble_S: return Code.Ble; + case Code.Blt_S: return Code.Blt; + case Code.Bne_Un_S: return Code.Bne_Un; + case Code.Bge_Un_S: return Code.Bge_Un; + case Code.Bgt_Un_S: return Code.Bgt_Un; + case Code.Ble_Un_S: return Code.Ble_Un; + case Code.Blt_Un_S: return Code.Blt_Un; + case Code.Br_S: return Code.Br; + case Code.Brfalse_S: return Code.Brfalse; + case Code.Brtrue_S: return Code.Brtrue; + case Code.Leave_S: return Code.Leave; + default: return code; + } + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandler.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandler.cs new file mode 100644 index 00000000..606d1f68 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandler.cs @@ -0,0 +1,38 @@ +/* + Copyright (C) 2011-2013 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.vm.v2 { + class OpCodeHandler { + public OpCodeHandlerInfo OpCodeHandlerInfo { get; private set; } + public TypeDef HandlerType { get; private set; } + public HandlerMethod ExecMethod { get; private set; } + + public OpCodeHandler(OpCodeHandlerInfo opCodeHandlerInfo, TypeDef handlerType, HandlerMethod execMethod) { + this.OpCodeHandlerInfo = opCodeHandlerInfo; + this.HandlerType = handlerType; + this.ExecMethod = execMethod; + } + + public override string ToString() { + return OpCodeHandlerInfo.Name; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfo.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfo.cs new file mode 100644 index 00000000..d7d3d48d --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfo.cs @@ -0,0 +1,123 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class OpCodeHandlerInfo { + public HandlerTypeCode TypeCode { get; private set; } + public string Name { get; private set; } + public MethodSigInfo ExecSig { get; private set; } + + public OpCodeHandlerInfo(HandlerTypeCode typeCode, MethodSigInfo execSig) { + this.TypeCode = typeCode; + this.Name = GetHandlerName(typeCode); + this.ExecSig = execSig; + } + + public override string ToString() { + return Name; + } + + static string GetHandlerName(HandlerTypeCode code) { + switch (code) { + case HandlerTypeCode.Add: return "add"; + case HandlerTypeCode.Add_Ovf: return "add.ovf"; + case HandlerTypeCode.Add_Ovf_Un: return "add.ovf.un"; + case HandlerTypeCode.And: return "and"; + case HandlerTypeCode.Beq: return "beq"; + case HandlerTypeCode.Bge: return "bge"; + case HandlerTypeCode.Bge_Un: return "bge.un"; + case HandlerTypeCode.Bgt: return "bgt"; + case HandlerTypeCode.Bgt_Un: return "bgt.un"; + case HandlerTypeCode.Ble: return "ble"; + case HandlerTypeCode.Ble_Un: return "ble.un"; + case HandlerTypeCode.Blt: return "blt"; + case HandlerTypeCode.Blt_Un: return "blt.un"; + case HandlerTypeCode.Bne_Un: return "bne.un"; + case HandlerTypeCode.Box: return "box"; + case HandlerTypeCode.Br: return "br"; + case HandlerTypeCode.Brfalse: return "brfalse"; + case HandlerTypeCode.Brtrue: return "brtrue"; + case HandlerTypeCode.Call: return "call"; + case HandlerTypeCode.Callvirt: return "callvirt"; + case HandlerTypeCode.Castclass: return "castclass"; + case HandlerTypeCode.Ceq: return "ceq"; + case HandlerTypeCode.Cgt: return "cgt"; + case HandlerTypeCode.Cgt_Un: return "cgt.un"; + case HandlerTypeCode.Clt: return "clt"; + case HandlerTypeCode.Clt_Un: return "clt.un"; + case HandlerTypeCode.Conv: return "conv"; + case HandlerTypeCode.Div: return "div"; + case HandlerTypeCode.Div_Un: return "div.un"; + case HandlerTypeCode.Dup: return "dup"; + case HandlerTypeCode.Endfinally: return "endfinally"; + case HandlerTypeCode.Initobj: return "initobj"; + case HandlerTypeCode.Isinst: return "isinst"; + case HandlerTypeCode.Ldarg: return "ldarg"; + case HandlerTypeCode.Ldarga: return "ldarga"; + case HandlerTypeCode.Ldc: return "ldc"; + case HandlerTypeCode.Ldelem: return "ldelem"; + case HandlerTypeCode.Ldelema: return "ldelema"; + case HandlerTypeCode.Ldfld_Ldsfld: return "ldfld/ldsfld"; + case HandlerTypeCode.Ldflda_Ldsflda:return "ldflda/ldsflda"; + case HandlerTypeCode.Ldftn: return "ldftn"; + case HandlerTypeCode.Ldlen: return "ldlen"; + case HandlerTypeCode.Ldloc: return "ldloc"; + case HandlerTypeCode.Ldloca: return "ldloca"; + case HandlerTypeCode.Ldobj: return "ldobj"; + case HandlerTypeCode.Ldstr: return "ldstr"; + case HandlerTypeCode.Ldtoken: return "ldtoken"; + case HandlerTypeCode.Ldvirtftn: return "ldvirtftn"; + case HandlerTypeCode.Leave: return "leave"; + case HandlerTypeCode.Mul: return "mul"; + case HandlerTypeCode.Mul_Ovf: return "mul.ovf"; + case HandlerTypeCode.Mul_Ovf_Un: return "mul.ovf.un"; + case HandlerTypeCode.Neg: return "neg"; + case HandlerTypeCode.Newarr: return "newarr"; + case HandlerTypeCode.Newobj: return "newobj"; + case HandlerTypeCode.Nop: return "nop"; + case HandlerTypeCode.Not: return "not"; + case HandlerTypeCode.Or: return "or"; + case HandlerTypeCode.Pop: return "pop"; + case HandlerTypeCode.Rem: return "rem"; + case HandlerTypeCode.Rem_Un: return "rem.un"; + case HandlerTypeCode.Ret: return "ret"; + case HandlerTypeCode.Rethrow: return "rethrow"; + case HandlerTypeCode.Shl: return "shl"; + case HandlerTypeCode.Shr: return "shr"; + case HandlerTypeCode.Shr_Un: return "shr.un"; + case HandlerTypeCode.Starg: return "starg"; + case HandlerTypeCode.Stelem: return "stelem"; + case HandlerTypeCode.Stfld_Stsfld: return "stfld/stsfld"; + case HandlerTypeCode.Stloc: return "stloc"; + case HandlerTypeCode.Stobj: return "stobj"; + case HandlerTypeCode.Sub: return "sub"; + case HandlerTypeCode.Sub_Ovf: return "sub.ovf"; + case HandlerTypeCode.Sub_Ovf_Un: return "sub.ovf.un"; + case HandlerTypeCode.Switch: return "switch"; + case HandlerTypeCode.Throw: return "throw"; + case HandlerTypeCode.Unbox_Any: return "unbox.any"; + case HandlerTypeCode.Xor: return "xor"; + default: throw new ApplicationException("Invalid handler type code"); + } + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs new file mode 100644 index 00000000..96721152 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs @@ -0,0 +1,528 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using dnlib.DotNet.Emit; +using dnlib.DotNet; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class OpCodeHandlerInfoReader { + IInstructionOperandResolver resolver; + Dictionary> readHandlers; + + public OpCodeHandlerInfoReader(IInstructionOperandResolver resolver) { + this.resolver = resolver; + this.readHandlers = new Dictionary> { + { HandlerTypeCode.Add, Handler_Add }, + { HandlerTypeCode.Add_Ovf, Handler_Add_Ovf }, + { HandlerTypeCode.Add_Ovf_Un, Handler_Add_Ovf_Un }, + { HandlerTypeCode.And, Handler_And }, + { HandlerTypeCode.Beq, Handler_Beq }, + { HandlerTypeCode.Bge, Handler_Bge }, + { HandlerTypeCode.Bge_Un, Handler_Bge_Un }, + { HandlerTypeCode.Bgt, Handler_Bgt }, + { HandlerTypeCode.Bgt_Un, Handler_Bgt_Un }, + { HandlerTypeCode.Ble, Handler_Ble }, + { HandlerTypeCode.Ble_Un, Handler_Ble_Un }, + { HandlerTypeCode.Blt, Handler_Blt }, + { HandlerTypeCode.Blt_Un, Handler_Blt_Un }, + { HandlerTypeCode.Bne_Un, Handler_Bne_Un }, + { HandlerTypeCode.Box, Handler_Box }, + { HandlerTypeCode.Br, Handler_Br }, + { HandlerTypeCode.Brfalse, Handler_Brfalse }, + { HandlerTypeCode.Brtrue, Handler_Brtrue }, + { HandlerTypeCode.Call, Handler_Call }, + { HandlerTypeCode.Callvirt, Handler_Callvirt }, + { HandlerTypeCode.Castclass, Handler_Castclass }, + { HandlerTypeCode.Ceq, Handler_Ceq }, + { HandlerTypeCode.Cgt, Handler_Cgt }, + { HandlerTypeCode.Cgt_Un, Handler_Cgt_Un }, + { HandlerTypeCode.Clt, Handler_Clt }, + { HandlerTypeCode.Clt_Un, Handler_Clt_Un }, + { HandlerTypeCode.Conv, Handler_Conv }, + { HandlerTypeCode.Div, Handler_Div }, + { HandlerTypeCode.Div_Un, Handler_Div_Un }, + { HandlerTypeCode.Dup, Handler_Dup }, + { HandlerTypeCode.Endfinally, Handler_Endfinally }, + { HandlerTypeCode.Initobj, Handler_Initobj }, + { HandlerTypeCode.Isinst, Handler_Isinst }, + { HandlerTypeCode.Ldarg, Handler_Ldarg }, + { HandlerTypeCode.Ldarga, Handler_Ldarga }, + { HandlerTypeCode.Ldc, Handler_Ldc }, + { HandlerTypeCode.Ldelem, Handler_Ldelem }, + { HandlerTypeCode.Ldelema, Handler_Ldelema }, + { HandlerTypeCode.Ldfld_Ldsfld, Handler_Ldfld_Ldsfld }, + { HandlerTypeCode.Ldflda_Ldsflda, Handler_Ldflda_Ldsflda }, + { HandlerTypeCode.Ldftn, Handler_Ldftn }, + { HandlerTypeCode.Ldlen, Handler_Ldlen }, + { HandlerTypeCode.Ldloc, Handler_Ldloc }, + { HandlerTypeCode.Ldloca, Handler_Ldloca }, + { HandlerTypeCode.Ldobj, Handler_Ldobj }, + { HandlerTypeCode.Ldstr, Handler_Ldstr }, + { HandlerTypeCode.Ldtoken, Handler_Ldtoken }, + { HandlerTypeCode.Ldvirtftn, Handler_Ldvirtftn }, + { HandlerTypeCode.Leave, Handler_Leave }, + { HandlerTypeCode.Mul, Handler_Mul }, + { HandlerTypeCode.Mul_Ovf, Handler_Mul_Ovf }, + { HandlerTypeCode.Mul_Ovf_Un, Handler_Mul_Ovf_Un }, + { HandlerTypeCode.Neg, Handler_Neg }, + { HandlerTypeCode.Newarr, Handler_Newarr }, + { HandlerTypeCode.Newobj, Handler_Newobj }, + { HandlerTypeCode.Nop, Handler_Nop }, + { HandlerTypeCode.Not, Handler_Not }, + { HandlerTypeCode.Or, Handler_Or }, + { HandlerTypeCode.Pop, Handler_Pop }, + { HandlerTypeCode.Rem, Handler_Rem }, + { HandlerTypeCode.Rem_Un, Handler_Rem_Un }, + { HandlerTypeCode.Ret, Handler_Ret }, + { HandlerTypeCode.Rethrow, Handler_Rethrow }, + { HandlerTypeCode.Shl, Handler_Shl }, + { HandlerTypeCode.Shr, Handler_Shr }, + { HandlerTypeCode.Shr_Un, Handler_Shr_Un }, + { HandlerTypeCode.Starg, Handler_Starg }, + { HandlerTypeCode.Stelem, Handler_Stelem }, + { HandlerTypeCode.Stfld_Stsfld, Handler_Stfld_Stsfld }, + { HandlerTypeCode.Stloc, Handler_Stloc }, + { HandlerTypeCode.Stobj, Handler_Stobj }, + { HandlerTypeCode.Sub, Handler_Sub }, + { HandlerTypeCode.Sub_Ovf, Handler_Sub_Ovf }, + { HandlerTypeCode.Sub_Ovf_Un, Handler_Sub_Ovf_Un }, + { HandlerTypeCode.Switch, Handler_Switch }, + { HandlerTypeCode.Throw, Handler_Throw }, + { HandlerTypeCode.Unbox_Any, Handler_Unbox_Any }, + { HandlerTypeCode.Xor, Handler_Xor }, + }; + } + + public Instruction Read(HandlerTypeCode typeCode, BinaryReader reader) { + Func readHandler; + if (!readHandlers.TryGetValue(typeCode, out readHandler)) + throw new ApplicationException("Invalid handler type"); + return readHandler(reader); + } + + Instruction Handler_Add(BinaryReader reader) { + return OpCodes.Add.ToInstruction(); + } + + Instruction Handler_Add_Ovf(BinaryReader reader) { + return OpCodes.Add_Ovf.ToInstruction(); + } + + Instruction Handler_Add_Ovf_Un(BinaryReader reader) { + return OpCodes.Add_Ovf_Un.ToInstruction(); + } + + Instruction Handler_And(BinaryReader reader) { + return OpCodes.And.ToInstruction(); + } + + Instruction Handler_Beq(BinaryReader reader) { + return new Instruction(OpCodes.Beq, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Bge(BinaryReader reader) { + return new Instruction(OpCodes.Bge, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Bge_Un(BinaryReader reader) { + return new Instruction(OpCodes.Bge_Un, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Bgt(BinaryReader reader) { + return new Instruction(OpCodes.Bgt, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Bgt_Un(BinaryReader reader) { + return new Instruction(OpCodes.Bgt_Un, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Ble(BinaryReader reader) { + return new Instruction(OpCodes.Ble, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Ble_Un(BinaryReader reader) { + return new Instruction(OpCodes.Ble_Un, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Blt(BinaryReader reader) { + return new Instruction(OpCodes.Blt, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Blt_Un(BinaryReader reader) { + return new Instruction(OpCodes.Blt_Un, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Bne_Un(BinaryReader reader) { + return new Instruction(OpCodes.Bne_Un, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Box(BinaryReader reader) { + var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + return OpCodes.Box.ToInstruction(type); + } + + Instruction Handler_Br(BinaryReader reader) { + return new Instruction(OpCodes.Br, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Brfalse(BinaryReader reader) { + return new Instruction(OpCodes.Brfalse, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Brtrue(BinaryReader reader) { + return new Instruction(OpCodes.Brtrue, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Call(BinaryReader reader) { + var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + return OpCodes.Call.ToInstruction(method); + } + + Instruction Handler_Callvirt(BinaryReader reader) { + var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + return OpCodes.Callvirt.ToInstruction(method); + } + + Instruction Handler_Castclass(BinaryReader reader) { + var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + return OpCodes.Castclass.ToInstruction(type); + } + + Instruction Handler_Ceq(BinaryReader reader) { + return OpCodes.Ceq.ToInstruction(); + } + + Instruction Handler_Cgt(BinaryReader reader) { + return OpCodes.Cgt.ToInstruction(); + } + + Instruction Handler_Cgt_Un(BinaryReader reader) { + return OpCodes.Cgt_Un.ToInstruction(); + } + + Instruction Handler_Clt(BinaryReader reader) { + return OpCodes.Clt.ToInstruction(); + } + + Instruction Handler_Clt_Un(BinaryReader reader) { + return OpCodes.Clt_Un.ToInstruction(); + } + + class ConvInfo { + public byte Type { get; private set; } + public bool Second { get; private set; } + public bool Third { get; private set; } + public OpCode OpCode { get; private set; } + public ConvInfo(byte type, bool second, bool third, OpCode opCode) { + this.Type = type; + this.Second = second; + this.Third = third; + this.OpCode = opCode; + } + } + readonly static List instructionInfos1 = new List { + new ConvInfo(0, false, false, OpCodes.Conv_I1), + new ConvInfo(1, false, false, OpCodes.Conv_I2), + new ConvInfo(2, false, false, OpCodes.Conv_I4), + new ConvInfo(3, false, false, OpCodes.Conv_I8), + new ConvInfo(4, false, false, OpCodes.Conv_R4), + new ConvInfo(5, false, false, OpCodes.Conv_R8), + new ConvInfo(6, false, false, OpCodes.Conv_U1), + new ConvInfo(7, false, false, OpCodes.Conv_U2), + new ConvInfo(8, false, false, OpCodes.Conv_U4), + new ConvInfo(9, false, false, OpCodes.Conv_U8), + new ConvInfo(10, false, false, OpCodes.Conv_I), + new ConvInfo(11, false, false, OpCodes.Conv_U), + + new ConvInfo(0, true, false, OpCodes.Conv_Ovf_I1), + new ConvInfo(1, true, false, OpCodes.Conv_Ovf_I2), + new ConvInfo(2, true, false, OpCodes.Conv_Ovf_I4), + new ConvInfo(3, true, false, OpCodes.Conv_Ovf_I8), + new ConvInfo(6, true, false, OpCodes.Conv_Ovf_U1), + new ConvInfo(7, true, false, OpCodes.Conv_Ovf_U2), + new ConvInfo(8, true, false, OpCodes.Conv_Ovf_U4), + new ConvInfo(9, true, false, OpCodes.Conv_Ovf_U8), + new ConvInfo(10, true, false, OpCodes.Conv_Ovf_I), + new ConvInfo(11, true, false, OpCodes.Conv_Ovf_U), + + new ConvInfo(0, true, true, OpCodes.Conv_Ovf_I1_Un), + new ConvInfo(1, true, true, OpCodes.Conv_Ovf_I2_Un), + new ConvInfo(2, true, true, OpCodes.Conv_Ovf_I4_Un), + new ConvInfo(3, true, true, OpCodes.Conv_Ovf_I8_Un), + new ConvInfo(6, true, true, OpCodes.Conv_Ovf_U1_Un), + new ConvInfo(7, true, true, OpCodes.Conv_Ovf_U2_Un), + new ConvInfo(8, true, true, OpCodes.Conv_Ovf_U4_Un), + new ConvInfo(9, true, true, OpCodes.Conv_Ovf_U8_Un), + new ConvInfo(10, true, true, OpCodes.Conv_Ovf_I_Un), + new ConvInfo(11, true, true, OpCodes.Conv_Ovf_U_Un), + new ConvInfo(12, true, true, OpCodes.Conv_R_Un), + }; + Instruction Handler_Conv(BinaryReader reader) { + byte type = reader.ReadByte(); + bool second = reader.ReadBoolean(); + bool third = reader.ReadBoolean(); + + Instruction instr = null; + foreach (var info in instructionInfos1) { + if (type != info.Type || info.Second != second || info.Third != third) + continue; + + instr = new Instruction { OpCode = info.OpCode }; + break; + } + if (instr == null) + throw new ApplicationException("Invalid opcode"); + + return instr; + } + + Instruction Handler_Div(BinaryReader reader) { + return OpCodes.Div.ToInstruction(); + } + + Instruction Handler_Div_Un(BinaryReader reader) { + return OpCodes.Div_Un.ToInstruction(); + } + + Instruction Handler_Dup(BinaryReader reader) { + return OpCodes.Dup.ToInstruction(); + } + + Instruction Handler_Endfinally(BinaryReader reader) { + return OpCodes.Endfinally.ToInstruction(); + } + + Instruction Handler_Initobj(BinaryReader reader) { + var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + return OpCodes.Initobj.ToInstruction(type); + } + + Instruction Handler_Isinst(BinaryReader reader) { + var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + return OpCodes.Isinst.ToInstruction(type); + } + + Instruction Handler_Ldarg(BinaryReader reader) { + return new Instruction(OpCodes.Ldarg, new ArgOperand(reader.ReadUInt16())); + } + + Instruction Handler_Ldarga(BinaryReader reader) { + return new Instruction(OpCodes.Ldarga, new ArgOperand(reader.ReadUInt16())); + } + + Instruction Handler_Ldc(BinaryReader reader) { + switch ((ElementType)reader.ReadByte()) { + case ElementType.I4: return Instruction.CreateLdcI4(reader.ReadInt32()); + case ElementType.I8: return OpCodes.Ldc_I8.ToInstruction(reader.ReadInt64()); + case ElementType.R4: return OpCodes.Ldc_R4.ToInstruction(reader.ReadSingle()); + case ElementType.R8: return OpCodes.Ldc_R8.ToInstruction(reader.ReadDouble()); + case ElementType.Object: return OpCodes.Ldnull.ToInstruction(); + default: throw new ApplicationException("Invalid instruction"); + } + } + + Instruction Handler_Ldelem(BinaryReader reader) { + return new Instruction(OpCodes.Ldelem, null); + } + + Instruction Handler_Ldelema(BinaryReader reader) { + return new Instruction(OpCodes.Ldelema, null); + } + + Instruction Handler_Ldfld_Ldsfld(BinaryReader reader) { + var field = resolver.ResolveToken(reader.ReadUInt32()) 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; + return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsflda, OpCodes.Ldflda, field)); + } + + Instruction Handler_Ldftn(BinaryReader reader) { + var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + return OpCodes.Ldftn.ToInstruction(method); + } + + Instruction Handler_Ldlen(BinaryReader reader) { + return OpCodes.Ldlen.ToInstruction(); + } + + Instruction Handler_Ldloc(BinaryReader reader) { + return new Instruction(OpCodes.Ldloc, new LocalOperand(reader.ReadUInt16())); + } + + Instruction Handler_Ldloca(BinaryReader reader) { + return new Instruction(OpCodes.Ldloca, new LocalOperand(reader.ReadUInt16())); + } + + Instruction Handler_Ldobj(BinaryReader reader) { + return new Instruction(OpCodes.Ldobj, null); + } + + Instruction Handler_Ldstr(BinaryReader reader) { + return OpCodes.Ldstr.ToInstruction(reader.ReadString()); + } + + Instruction Handler_Ldtoken(BinaryReader reader) { + var member = resolver.ResolveToken(reader.ReadUInt32()) as ITokenOperand; + return OpCodes.Ldtoken.ToInstruction(member); + } + + Instruction Handler_Ldvirtftn(BinaryReader reader) { + var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + reader.ReadUInt32(); + return OpCodes.Ldvirtftn.ToInstruction(method); + } + + Instruction Handler_Leave(BinaryReader reader) { + return new Instruction(OpCodes.Leave, new TargetDisplOperand(reader.ReadInt32())); + } + + Instruction Handler_Mul(BinaryReader reader) { + return OpCodes.Mul.ToInstruction(); + } + + Instruction Handler_Mul_Ovf(BinaryReader reader) { + return OpCodes.Mul_Ovf.ToInstruction(); + } + + Instruction Handler_Mul_Ovf_Un(BinaryReader reader) { + return OpCodes.Mul_Ovf_Un.ToInstruction(); + } + + Instruction Handler_Neg(BinaryReader reader) { + return OpCodes.Neg.ToInstruction(); + } + + Instruction Handler_Newarr(BinaryReader reader) { + var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + return OpCodes.Newarr.ToInstruction(type); + } + + Instruction Handler_Newobj(BinaryReader reader) { + var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + return OpCodes.Newobj.ToInstruction(method); + } + + Instruction Handler_Nop(BinaryReader reader) { + return OpCodes.Nop.ToInstruction(); + } + + Instruction Handler_Not(BinaryReader reader) { + return OpCodes.Not.ToInstruction(); + } + + Instruction Handler_Or(BinaryReader reader) { + return OpCodes.Or.ToInstruction(); + } + + Instruction Handler_Pop(BinaryReader reader) { + return OpCodes.Pop.ToInstruction(); + } + + Instruction Handler_Rem(BinaryReader reader) { + return OpCodes.Rem.ToInstruction(); + } + + Instruction Handler_Rem_Un(BinaryReader reader) { + return OpCodes.Rem_Un.ToInstruction(); + } + + Instruction Handler_Ret(BinaryReader reader) { + var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod; + return OpCodes.Ret.ToInstruction(); + } + + Instruction Handler_Rethrow(BinaryReader reader) { + return OpCodes.Rethrow.ToInstruction(); + } + + Instruction Handler_Shl(BinaryReader reader) { + return OpCodes.Shl.ToInstruction(); + } + + Instruction Handler_Shr(BinaryReader reader) { + return OpCodes.Shr.ToInstruction(); + } + + Instruction Handler_Shr_Un(BinaryReader reader) { + return OpCodes.Shr_Un.ToInstruction(); + } + + Instruction Handler_Starg(BinaryReader reader) { + return new Instruction(OpCodes.Starg, new ArgOperand(reader.ReadUInt16())); + } + + Instruction Handler_Stelem(BinaryReader reader) { + return new Instruction(OpCodes.Stelem, null); + } + + Instruction Handler_Stfld_Stsfld(BinaryReader reader) { + var field = resolver.ResolveToken(reader.ReadUInt32()) as IField; + return new Instruction(null, new FieldInstructionOperand(OpCodes.Stsfld, OpCodes.Stfld, field)); + } + + Instruction Handler_Stloc(BinaryReader reader) { + ushort loc = reader.ReadUInt16(); + var etype = (ElementType)reader.ReadInt32(); + return new Instruction(OpCodes.Stloc, new LocalOperand(loc)); + } + + Instruction Handler_Stobj(BinaryReader reader) { + return new Instruction(OpCodes.Stobj, null); + } + + Instruction Handler_Sub(BinaryReader reader) { + return OpCodes.Sub.ToInstruction(); + } + + Instruction Handler_Sub_Ovf(BinaryReader reader) { + return OpCodes.Sub_Ovf.ToInstruction(); + } + + Instruction Handler_Sub_Ovf_Un(BinaryReader reader) { + return OpCodes.Sub_Ovf_Un.ToInstruction(); + } + + Instruction Handler_Switch(BinaryReader reader) { + int size = reader.ReadInt32(); + var offsets = new int[size]; + for (int i = 0; i < size; i++) + offsets[i] = reader.ReadInt32(); + return new Instruction(OpCodes.Switch, new SwitchTargetDisplOperand(offsets)); + } + + Instruction Handler_Throw(BinaryReader reader) { + return OpCodes.Throw.ToInstruction(); + } + + Instruction Handler_Unbox_Any(BinaryReader reader) { + var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef; + return OpCodes.Unbox_Any.ToInstruction(type); + } + + Instruction Handler_Xor(BinaryReader reader) { + return OpCodes.Xor.ToInstruction(); + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs new file mode 100644 index 00000000..13165b97 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs @@ -0,0 +1,98 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using System.IO; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + static class OpCodeHandlerInfos { + enum OpCodeHandlersFileVersion : int { + V1 = 1, + } + + public static void Write(BinaryWriter writer, IList handlerInfos) { + WriteV1(writer, handlerInfos); + } + + public static void WriteV1(BinaryWriter writer, IList handlerInfos) { + writer.Write((int)OpCodeHandlersFileVersion.V1); + writer.Write(handlerInfos.Count); + foreach (var handler in handlerInfos) { + writer.Write((int)handler.TypeCode); + var infos = handler.ExecSig.BlockInfos; + writer.Write(infos.Count); + foreach (var info in infos) { + if (info.Hash == null) + writer.Write(0); + else { + writer.Write(info.Hash.Length); + writer.Write(info.Hash); + } + writer.Write(info.Targets.Count); + foreach (var target in info.Targets) + writer.Write(target); + } + } + } + + public static List Read(BinaryReader reader) { + switch ((OpCodeHandlersFileVersion)reader.ReadInt32()) { + case OpCodeHandlersFileVersion.V1: return ReadV1(reader); + default: throw new ApplicationException("Invalid file version"); + } + } + + static List ReadV1(BinaryReader reader) { + int numHandlers = reader.ReadInt32(); + var list = new List(numHandlers); + for (int i = 0; i < numHandlers; i++) { + var typeCode = (HandlerTypeCode)reader.ReadInt32(); + int numInfos = reader.ReadInt32(); + var sigInfo = new MethodSigInfo(); + for (int j = 0; j < numInfos; j++) { + var info = new BlockInfo(); + + info.Hash = reader.ReadBytes(reader.ReadInt32()); + if (info.Hash.Length == 0) + info.Hash = null; + + int numTargets = reader.ReadInt32(); + for (int k = 0; k < numTargets; k++) + info.Targets.Add(reader.ReadInt32()); + + sigInfo.BlockInfos.Add(info); + } + + list.Add(new OpCodeHandlerInfo(typeCode, sigInfo)); + } + return list; + } + + public static readonly IList[] HandlerInfos = new IList[] { + ReadOpCodeHandlerInfos(CsvmResources.CSVM1_v2), + ReadOpCodeHandlerInfos(CsvmResources.CSVM2_v2), + ReadOpCodeHandlerInfos(CsvmResources.CSVM3_v2), + }; + + static IList ReadOpCodeHandlerInfos(byte[] data) { + return OpCodeHandlerInfos.Read(new BinaryReader(new MemoryStream(data))); + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCode.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCode.cs new file mode 100644 index 00000000..bb1fdd85 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCode.cs @@ -0,0 +1,44 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System.Collections.Generic; +using System.Text; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class VmOpCode { + public List OpCodeHandlerInfos { get; private set; } + + public VmOpCode(List opCodeHandlerInfos) { + this.OpCodeHandlerInfos = new List(opCodeHandlerInfos.Count); + this.OpCodeHandlerInfos.AddRange(opCodeHandlerInfos); + } + + public override string ToString() { + if (OpCodeHandlerInfos.Count == 0) + return ""; + var sb = new StringBuilder(); + foreach (var handler in OpCodeHandlerInfos) { + if (sb.Length != 0) + sb.Append(", "); + sb.Append(handler.Name); + } + return sb.ToString(); + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs new file mode 100644 index 00000000..ea234c72 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs @@ -0,0 +1,297 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using de4dot.blocks; +using de4dot.blocks.cflow; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class MyDeobfuscator { + CliSecureRtType cliSecureRtType; + StringDecrypter stringDecrypter; + StaticStringInliner staticStringInliner = new StaticStringInliner(); + + public MyDeobfuscator(ModuleDefMD module) { + cliSecureRtType = new CliSecureRtType(module); + cliSecureRtType.Find(null); + stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterMethod); + stringDecrypter.Find(); + cliSecureRtType.FindStringDecrypterMethod(); + stringDecrypter.Method = cliSecureRtType.StringDecrypterMethod; + staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0])); + } + + void RestoreMethod(Blocks blocks) { + IList allInstructions; + IList allExceptionHandlers; + blocks.GetCode(out allInstructions, out allExceptionHandlers); + DotNetUtils.RestoreBody(blocks.Method, allInstructions, allExceptionHandlers); + } + + public void DecryptStrings(MethodDef method) { + var blocks = new Blocks(method); + DecryptStrings(blocks); + RestoreMethod(blocks); + } + + public void DecryptStrings(Blocks blocks) { + staticStringInliner.Decrypt(blocks); + } + + public void Deobfuscate(MethodDef method) { + DecryptStrings(method); + } + } + + class VmOpCodeHandlerDetector { + const int NUM_HANDLERS = 78; + ModuleDefMD module; + List vmOpCodes; + MyDeobfuscator deobfuscator; + + public IList Handlers { + get { return vmOpCodes; } + } + + public VmOpCodeHandlerDetector(ModuleDefMD module) { + this.module = module; + } + + public void FindHandlers() { + if (vmOpCodes != null) + return; + + deobfuscator = new MyDeobfuscator(module); + + var csvmInfo = new CsvmInfo(module); + csvmInfo.Initialize(); + var vmHandlerTypes = FindVmHandlerTypes(); + if (vmHandlerTypes == null) + throw new ApplicationException("Could not find CSVM opcode handler types"); + + var composites = CreateCompositeOpCodeHandlers(csvmInfo, vmHandlerTypes); + foreach (var handlerInfos in OpCodeHandlerInfos.HandlerInfos) { + var otherHandlers = CreateOtherHandlers(csvmInfo, handlerInfos); + + if (!DetectCompositeHandlers(composites, otherHandlers)) + continue; + + vmOpCodes = CreateVmOpCodes(composites); + break; + } + if (vmOpCodes == null) + throw new ApplicationException("Could not find any/all CSVM handlers"); + } + + static List CreateVmOpCodes(IList composites) { + var list = new List(composites.Count); + foreach (var composite in composites) + list.Add(new VmOpCode(composite.OpCodeHandlerInfos)); + return list; + } + + bool DetectCompositeHandlers(IEnumerable composites, List otherHandlers) { + var detector = new CompositeHandlerDetector(otherHandlers); + foreach (var composite in composites) { + if (!detector.FindHandlers(composite)) + return false; + } + return true; + } + + static MethodDef SimplifyInstructions(MethodDef method) { + if (method.Body == null) + return method; + method.Body.SimplifyMacros(method.Parameters); + return method; + } + + List CreateCompositeOpCodeHandlers(CsvmInfo csvmInfo, List handlers) { + var list = new List(handlers.Count); + + foreach (var handler in handlers) { + var execHandler = new HandlerMethod(GetExecMethod(handler)); + list.Add(new CompositeOpCodeHandler(handler, execHandler)); + } + + return list; + } + + MethodDef GetExecMethod(TypeDef type) { + MethodDef readMethod, execMethod; + GetReadAndExecMethods(type, out readMethod, out execMethod); + deobfuscator.Deobfuscate(execMethod); + SimplifyInstructions(execMethod); + return execMethod; + } + + static MethodSigInfoCreator CreateMethodSigInfoCreator(CsvmInfo csvmInfo) { + var creator = new MethodSigInfoCreator(); + + creator.AddId(csvmInfo.LogicalOpShrUn, 1); + creator.AddId(csvmInfo.LogicalOpShl, 2); + creator.AddId(csvmInfo.LogicalOpShr, 3); + creator.AddId(csvmInfo.LogicalOpAnd, 4); + creator.AddId(csvmInfo.LogicalOpXor, 5); + creator.AddId(csvmInfo.LogicalOpOr, 6); + + creator.AddId(csvmInfo.CompareLt, 7); + creator.AddId(csvmInfo.CompareLte, 8); + creator.AddId(csvmInfo.CompareGt, 9); + creator.AddId(csvmInfo.CompareGte, 10); + creator.AddId(csvmInfo.CompareEq, 11); + creator.AddId(csvmInfo.CompareEqz, 12); + + creator.AddId(csvmInfo.ArithmeticSubOvfUn, 13); + creator.AddId(csvmInfo.ArithmeticMulOvfUn, 14); + creator.AddId(csvmInfo.ArithmeticRemUn, 15); + creator.AddId(csvmInfo.ArithmeticRem, 16); + creator.AddId(csvmInfo.ArithmeticDivUn, 17); + creator.AddId(csvmInfo.ArithmeticDiv, 18); + creator.AddId(csvmInfo.ArithmeticMul, 19); + creator.AddId(csvmInfo.ArithmeticMulOvf, 20); + creator.AddId(csvmInfo.ArithmeticSub, 21); + creator.AddId(csvmInfo.ArithmeticSubOvf, 22); + creator.AddId(csvmInfo.ArithmeticAddOvfUn, 23); + creator.AddId(csvmInfo.ArithmeticAddOvf, 24); + creator.AddId(csvmInfo.ArithmeticAdd, 25); + + creator.AddId(csvmInfo.UnaryNot, 26); + creator.AddId(csvmInfo.UnaryNeg, 27); + + creator.AddId(csvmInfo.ArgsGet, 28); + creator.AddId(csvmInfo.ArgsSet, 29); + creator.AddId(csvmInfo.LocalsGet, 30); + creator.AddId(csvmInfo.LocalsSet, 31); + + return creator; + } + + static void GetReadAndExecMethods(TypeDef handler, out MethodDef readMethod, out MethodDef execMethod) { + readMethod = execMethod = null; + foreach (var method in handler.Methods) { + if (!method.IsVirtual) + continue; + if (DotNetUtils.IsMethod(method, "System.Void", "(System.IO.BinaryReader)")) { + if (readMethod != null) + throw new ApplicationException("Found another read method"); + readMethod = method; + } + else if (!DotNetUtils.HasReturnValue(method) && method.MethodSig.GetParamCount() == 1) { + if (execMethod != null) + throw new ApplicationException("Found another execute method"); + execMethod = method; + } + } + + if (readMethod == null) + throw new ApplicationException("Could not find read method"); + if (execMethod == null) + throw new ApplicationException("Could not find execute method"); + } + + IEnumerable GetVmHandlerTypes(TypeDef baseType) { + foreach (var type in module.Types) { + if (type.BaseType == baseType) + yield return type; + } + } + + List FindBasicVmHandlerTypes(CsvmInfo csvmInfo) { + var list = new List(); + if (csvmInfo.VmHandlerBaseType == null) + return list; + foreach (var type in module.Types) { + if (list.Count == NUM_HANDLERS) + break; + if (type.BaseType == csvmInfo.VmHandlerBaseType) + list.Add(type); + } + return list; + } + + List FindVmHandlerTypes() { + var requiredFields = new string[] { + null, + "System.Collections.Generic.Dictionary`2", + "System.UInt16", + }; + var cflowDeobfuscator = new CflowDeobfuscator(); + foreach (var type in module.Types) { + var cctor = type.FindStaticConstructor(); + if (cctor == null) + continue; + requiredFields[0] = type.FullName; + if (!new FieldTypes(type).Exactly(requiredFields)) + continue; + + cflowDeobfuscator.Deobfuscate(cctor); + var handlers = FindVmHandlerTypes(cctor); + if (handlers.Count < NUM_HANDLERS) + continue; + + return handlers; + } + + return null; + } + + static List FindVmHandlerTypes(MethodDef method) { + var list = new List(); + + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code != Code.Ldtoken) + continue; + var type = instr.Operand as TypeDef; + if (type == null) + continue; + + list.Add(type); + } + + return list; + } + + List CreateOtherHandlers(CsvmInfo csvmInfo, IList handlerInfos) { + var list = new List(NUM_HANDLERS); + + foreach (var type in GetVmHandlerTypes(csvmInfo.VmHandlerBaseType)) { + if (list.Count == NUM_HANDLERS) + break; + + var execHandler = new PrimitiveHandlerMethod(GetExecMethod(type)); + execHandler.Sig = CreateMethodSigInfoCreator(csvmInfo).Create(execHandler.Blocks); + + var finder = new MethodFinder(handlerInfos, execHandler); + var handler = finder.FindHandler(); + if (handler == null) + continue; + + list.Add(handler); + } + + list.Sort((a, b) => a.OpCodeHandlerInfo.Name.ToUpperInvariant().CompareTo(b.OpCodeHandlerInfo.Name.ToUpperInvariant())); + + return list; + } + } +} diff --git a/de4dot.code/deobfuscators/NullStream.cs b/de4dot.code/deobfuscators/NullStream.cs new file mode 100644 index 00000000..a88b0728 --- /dev/null +++ b/de4dot.code/deobfuscators/NullStream.cs @@ -0,0 +1,87 @@ +/* + Copyright (C) 2011-2013 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.IO; + +namespace de4dot.code.deobfuscators { + class NullStream : Stream { + long offset = 0; + long length = 0; + + public override bool CanRead { + get { return false; } + } + + public override bool CanSeek { + get { return true; } + } + + public override bool CanWrite { + get { return true; } + } + + public override void Flush() { + } + + public override long Length { + get { return length; } + } + + public override long Position { + get { return offset; } + set { offset = value; } + } + + public override int Read(byte[] buffer, int offset, int count) { + throw new NotImplementedException(); + } + + public override long Seek(long offset, SeekOrigin origin) { + switch (origin) { + case SeekOrigin.Begin: + this.offset = offset; + break; + + case SeekOrigin.Current: + this.offset += offset; + break; + + case SeekOrigin.End: + this.offset = length + offset; + break; + + default: + throw new NotSupportedException(); + } + + return this.offset; + } + + public override void SetLength(long value) { + this.length = value; + } + + public override void Write(byte[] buffer, int offset, int count) { + this.offset += count; + if (this.offset > this.length) + this.length = this.offset; + } + } +} diff --git a/de4dot.mdecrypt/DynamicMethodsDecrypter.cs b/de4dot.mdecrypt/DynamicMethodsDecrypter.cs index 3aa2c949..f25c8697 100644 --- a/de4dot.mdecrypt/DynamicMethodsDecrypter.cs +++ b/de4dot.mdecrypt/DynamicMethodsDecrypter.cs @@ -20,12 +20,21 @@ using System; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Reflection; +using System.Security; using dnlib.DotNet; using dnlib.DotNet.MD; +using dnlib.PE; using de4dot.blocks; +namespace System.Runtime.ExceptionServices { + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + class HandleProcessCorruptedStateExceptionsAttribute : Attribute { + } +} + namespace de4dot.mdecrypt { public class DynamicMethodsDecrypter { static DynamicMethodsDecrypter instance; @@ -132,6 +141,9 @@ namespace de4dot.mdecrypt { [DllImport("kernel32")] static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect); + [DllImport("kernel32")] + static extern bool GetModuleHandleEx(uint dwFlags, IntPtr lpModuleName, out IntPtr phModule); + delegate IntPtr GetJit(); delegate int CompileMethod(IntPtr jitter, IntPtr comp, IntPtr info, uint flags, IntPtr nativeEntry, IntPtr nativeSizeOfCode, out bool handled); delegate int ReturnMethodToken(); @@ -364,13 +376,15 @@ namespace de4dot.mdecrypt { // We're not decrypting methods var info2 = (CORINFO_METHOD_INFO*)info; - if (info2->scope != moduleToDecryptScope) { + if (info2->scope != moduleToDecryptScope || + decryptMethodsInfo.moduleCctorBytes == null || + moduleCctorCodeRva == 0) { handled = false; return 0; } uint codeRva = (uint)((byte*)info2->ILCode - (byte*)hInstModule); - if (decryptMethodsInfo.moduleCctorBytes != null && moduleCctorCodeRva != 0 && moduleCctorCodeRva == codeRva) { + if (moduleCctorCodeRva == codeRva) { fixed (byte* newIlCodeBytes = &decryptMethodsInfo.moduleCctorBytes[0]) { WriteCompileMethod(origCompileMethod); info2->ILCode = new IntPtr(newIlCodeBytes); @@ -553,30 +567,78 @@ namespace de4dot.mdecrypt { if (hasInstalledCompileMethod2) return; - if (!PatchDword(*(IntPtr*)jitterVtbl, 0x30000, origCompileMethod, ourCompileMethodInfo.ptrInDll)) + if (!PatchCM(*(IntPtr*)jitterVtbl, origCompileMethod, ourCompileMethodInfo.ptrInDll)) throw new ApplicationException("Couldn't patch compileMethod"); hasInstalledCompileMethod2 = true; return; } - unsafe bool PatchDword(IntPtr addr, int size, IntPtr origValue, IntPtr newValue) { - addr = new IntPtr(addr.ToInt64() & ~0xFFF); - var endAddr = new IntPtr(addr.ToInt64() + size); - for (; addr.ToPointer() < endAddr.ToPointer(); addr = new IntPtr(addr.ToInt64() + 0x1000)) { - try { - for (int i = 0; i < 0x1000; i += IntPtr.Size) { - var addr2 = (IntPtr*)((byte*)addr + i); - if (*addr2 == origValue) { - *addr2 = newValue; - return true; + static IntPtr GetModuleHandle(IntPtr addr) { + IntPtr hModule; + if (!GetModuleHandleEx(4, addr, out hModule)) + throw new ApplicationException("GetModuleHandleEx() failed"); + return hModule; + } + + static unsafe bool PatchCM(IntPtr addr, IntPtr origValue, IntPtr newValue) { + var baseAddr = GetModuleHandle(addr); + IntPtr patchAddr; + using (var peImage = new PEImage(baseAddr)) + patchAddr = FindCMAddress(peImage, baseAddr, origValue); + if (patchAddr == IntPtr.Zero) + return false; + + *(IntPtr*)patchAddr = newValue; + return true; + } + + [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0 + static unsafe IntPtr FindCMAddress(PEImage peImage, IntPtr baseAddr, IntPtr origValue) { + const int offset1_CLR2 = 0x78; + const int offset1_CLR4 = 0x74; + int offset1 = Environment.Version.Major == 2 ? offset1_CLR2 : offset1_CLR4; + + const int offset2_CLR2 = 0x10; + const int offset2_CLR4 = 0x28; + int offset2 = Environment.Version.Major == 2 ? offset2_CLR2 : offset2_CLR4; + + foreach (var section in peImage.ImageSectionHeaders) { + const uint RW = 0x80000000 | 0x40000000; + if ((section.Characteristics & RW) != RW) + continue; + + byte* p = (byte*)baseAddr + (uint)section.VirtualAddress + ((section.VirtualSize + IntPtr.Size - 1) & ~(IntPtr.Size - 1)) - IntPtr.Size; + for (; p >= (byte*)baseAddr; p -= IntPtr.Size) { + try { + byte* p2 = (byte*)*(IntPtr**)p; + if ((ulong)p2 >= 0x10000) { + p2 += offset1; + if (*(IntPtr*)p2 == origValue) + return new IntPtr(p2); } } - } - catch { + catch { + } + try { + byte* p2 = (byte*)*(IntPtr**)p; + if ((ulong)p2 >= 0x10000) { + p2 += offset2; + if (*(IntPtr*)p2 == origValue) + return new IntPtr(p2); + } + } + catch { + } + try { + if (*(IntPtr*)p == origValue) + return new IntPtr(p); + } + catch { + } } } - return false; + return IntPtr.Zero; } } }