diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index c76a0746..42c01c7a 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -67,16 +67,36 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ CsvmResources.resx
+
+
+
+
+
+
+
+
+
+
+
+
@@ -240,6 +260,7 @@
+
@@ -358,6 +379,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 4caf59ad..e0546f3f 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.Csvm csvm;
+ vm.v1.Csvm csvmV1;
+ vm.v2.Csvm csvmV2;
internal class Options : OptionsBase {
public bool DecryptMethods { get; set; }
@@ -166,7 +167,7 @@ namespace de4dot.code.deobfuscators.Agile_NET {
ToInt32(stringDecrypter.Detected) +
ToInt32(proxyCallFixer.Detected) +
ToInt32(resourceDecrypter.Detected) +
- ToInt32(csvm.Detected);
+ ToInt32(csvmV1.Detected || csvmV2.Detected);
if (sum > 0)
val += 100 + 10 * (sum - 1);
if (cliSecureAttributes.Count != 0)
@@ -185,8 +186,10 @@ namespace de4dot.code.deobfuscators.Agile_NET {
resourceDecrypter.Find();
proxyCallFixer = new ProxyCallFixer(module);
proxyCallFixer.FindDelegateCreator();
- csvm = new vm.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 +230,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.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 +274,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/CilOperandInstructionRestorer.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs
index 313243c0..4df28e4c 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs
@@ -23,9 +23,7 @@ using de4dot.blocks;
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 {
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 {
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/CsvmToCilMethodConverter.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs
similarity index 75%
rename from de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverter.cs
rename to de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs
index b468ef9e..00757dab 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverter.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs
@@ -25,22 +25,33 @@ using dnlib.DotNet.Emit;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Agile_NET.vm {
- class CsvmToCilMethodConverter {
- IDeobfuscatorContext deobfuscatorContext;
- ModuleDefMD module;
- VmOpCodeHandlerDetector opCodeDetector;
- CilOperandInstructionRestorer operandRestorer = new CilOperandInstructionRestorer();
+ 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 CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector) {
+ public CsvmToCilMethodConverterBase(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module) {
this.deobfuscatorContext = deobfuscatorContext;
this.module = module;
- this.opCodeDetector = opCodeDetector;
+ }
+
+ 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, newInstructions);
+ var newExceptions = ReadExceptions(cilMethod, csvmMethod);
FixInstructionOperands(newInstructions);
FixLocals(newInstructions, cilMethod.Body.Variables);
@@ -59,7 +70,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
if (op == null)
continue;
- UpdateLocalInstruction(instr, locals[op.local], op.local);
+ UpdateLocalInstruction(instr, locals[op.Local], op.Local);
}
}
@@ -134,7 +145,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
if (op == null)
continue;
- UpdateArgInstruction(instr, method.Parameters[op.arg], op.arg);
+ UpdateArgInstruction(instr, method.Parameters[op.Arg], op.Arg);
}
}
@@ -197,28 +208,16 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
}
}
- 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);
- instr.Offset = offset;
- offset += (uint)GetInstructionSize(instr);
- instrs.Add(instr);
- }
- return instrs;
- }
+ protected abstract List ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod);
- static int GetInstructionSize(Instruction instr) {
+ 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;
+ return instr.OpCode.Size + (op.TargetDisplacements.Length + 1) * 4;
}
List ReadLocals(MethodDef cilMethod, CsvmMethodData csvmMethod) {
@@ -289,7 +288,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
}
}
- List ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod, List cilInstructions) {
+ List ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod) {
var reader = new BinaryReader(new MemoryStream(csvmMethod.Exceptions));
var ehs = new List();
@@ -302,14 +301,14 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
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());
+ 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(cilInstructions, reader.ReadInt32());
+ eh.FilterStart = GetInstruction(reader.ReadInt32());
ehs.Add(eh);
}
@@ -317,22 +316,20 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
return ehs;
}
- static Instruction GetInstruction(IList instrs, int index) {
- return instrs[index];
+ Instruction GetInstruction(int vmIndex) {
+ return vmIndexToCil[vmIndex];
}
- static Instruction GetInstructionEnd(IList instrs, int index) {
- index++;
- if (index == instrs.Count)
- return null;
- return instrs[index];
+ Instruction GetInstructionEnd(int vmIndex) {
+ vmIndex++;
+ Instruction instr;
+ vmIndexToCil.TryGetValue(vmIndex, out instr);
+ return instr;
}
- 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];
+ Instruction GetInstruction(Instruction source, int displ) {
+ int vmIndex = cilToVmIndex[source];
+ return vmIndexToCil[vmIndex + displ];
}
void FixInstructionOperands(IList instrs) {
@@ -344,41 +341,33 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
}
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);
+ return GetInstruction(instr, ((TargetDisplOperand)vmOperand).Displacement);
if (vmOperand is SwitchTargetDisplOperand) {
- var targetDispls = ((SwitchTargetDisplOperand)vmOperand).targetDisplacements;
+ 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]);
+ targets[i] = GetInstruction(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);
+ 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, int token, OpCode staticInstr, OpCode instanceInstr) {
- var fieldRef = module.ResolveToken(token) as IField;
+ 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.", token);
+ Logger.w("Could not resolve field {0:X8}. Assuming it's not static.", fieldRef == null ? 0 : fieldRef.MDToken.Raw);
isStatic = false;
}
else
@@ -387,13 +376,6 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
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;
diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/VmOperands.cs b/de4dot.code/deobfuscators/Agile_NET/vm/VmOperand.cs
similarity index 54%
rename from de4dot.code/deobfuscators/Agile_NET/vm/VmOperands.cs
rename to de4dot.code/deobfuscators/Agile_NET/vm/VmOperand.cs
index bfb143a8..03cde6c4 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/VmOperands.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/VmOperand.cs
@@ -17,66 +17,50 @@
along with de4dot. If not, see .
*/
+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/Csvm.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs
similarity index 97%
rename from de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs
rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs
index e3030940..91339051 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs
@@ -23,7 +23,7 @@ using System.IO;
using dnlib.DotNet;
using de4dot.blocks;
-namespace de4dot.code.deobfuscators.Agile_NET.vm {
+namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
class Csvm {
IDeobfuscatorContext deobfuscatorContext;
ModuleDefMD module;
@@ -140,7 +140,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
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
new file mode 100644
index 00000000..93516c18
--- /dev/null
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs
@@ -0,0 +1,52 @@
+/*
+ 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.v1 {
+ 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();
+ uint offset = 0;
+ while (reader.BaseStream.Position < reader.BaseStream.Length) {
+ int vmOpCode = reader.ReadUInt16();
+ 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;
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/FieldsInfo.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/FieldsInfo.cs
similarity index 97%
rename from de4dot.code/deobfuscators/Agile_NET/vm/FieldsInfo.cs
rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/FieldsInfo.cs
index a6945092..875d349d 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/FieldsInfo.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/FieldsInfo.cs
@@ -21,7 +21,7 @@ using System;
using System.Collections.Generic;
using dnlib.DotNet;
-namespace de4dot.code.deobfuscators.Agile_NET.vm {
+namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
class FieldsInfo {
public static readonly object EnumType = new object();
Dictionary fieldTypes = new Dictionary(StringComparer.Ordinal);
diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandler.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs
similarity index 78%
rename from de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandler.cs
rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs
index 1a911df6..9c572780 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandler.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs
@@ -24,12 +24,12 @@ using de4dot.blocks;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
-namespace de4dot.code.deobfuscators.Agile_NET.vm {
+namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
partial class OpCodeHandler {
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 {
}
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
!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 {
}
}
- 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 {
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 {
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 {
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 {
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/OpCodeHandlers.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs
similarity index 99%
rename from de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandlers.cs
rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs
index 6b9dd252..aa8419a3 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandlers.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs
@@ -17,9 +17,9 @@
along with de4dot. If not, see .
*/
-namespace de4dot.code.deobfuscators.Agile_NET.vm {
+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/UnknownHandlerInfo.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/UnknownHandlerInfo.cs
similarity index 98%
rename from de4dot.code/deobfuscators/Agile_NET/vm/UnknownHandlerInfo.cs
rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/UnknownHandlerInfo.cs
index 05557708..b2a08276 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/UnknownHandlerInfo.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/UnknownHandlerInfo.cs
@@ -23,7 +23,7 @@ using dnlib.DotNet;
using dnlib.DotNet.Emit;
using de4dot.blocks;
-namespace de4dot.code.deobfuscators.Agile_NET.vm {
+namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
class UnknownHandlerInfo {
TypeDef type;
CsvmInfo csvmInfo;
diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/VmOpCodeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs
similarity index 98%
rename from de4dot.code/deobfuscators/Agile_NET/vm/VmOpCodeHandlerDetector.cs
rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs
index 5be219cc..b06ca9c8 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/VmOpCodeHandlerDetector.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs
@@ -24,7 +24,7 @@ using dnlib.DotNet.Emit;
using de4dot.blocks;
using de4dot.blocks.cflow;
-namespace de4dot.code.deobfuscators.Agile_NET.vm {
+namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
class OpCodeHandlerSigInfo {
public object[] RequiredFieldTypes { get; set; }
public string[] ExecuteMethodLocals { get; set; }
@@ -214,7 +214,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm {
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 00000000..156f0777
Binary files /dev/null and b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM1_v2.bin differ
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 00000000..81bd5ae6
Binary files /dev/null and b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM2_v2.bin differ
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 00000000..a86daab3
Binary files /dev/null and b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM3_v2.bin differ
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