Support latest Agile.NET

This commit is contained in:
de4dot 2013-11-08 11:05:17 +01:00
parent f9dde3317c
commit b65e9a59df
34 changed files with 4497 additions and 567 deletions

View File

@ -66,17 +66,37 @@
<Compile Include="deobfuscators\Agile_NET\ResourceDecrypter.cs" />
<Compile Include="deobfuscators\Agile_NET\StackFrameHelper.cs" />
<Compile Include="deobfuscators\Agile_NET\StringDecrypter.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\CilOperandInstructionRestorer.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\CilOperandInstructionRestorer.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\CsvmDataReader.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\CsvmMethodData.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\CsvmToCilMethodConverterBase.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\Csvm.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\CsvmDataReader.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\CsvmMethodData.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\CsvmToCilMethodConverter.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\FieldsInfo.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\OpCodeHandler.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\OpCodeHandlers.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\UnknownHandlerInfo.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\VmOpCodeHandlerDetector.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v1\VmOperands.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\CompositeHandlerDetector.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\CompositeOpCodeHandler.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\Csvm.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\CsvmInfo.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\CsvmResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>CsvmResources.resx</DependentUpon>
</Compile>
<Compile Include="deobfuscators\Agile_NET\vm\v2\CsvmToCilMethodConverter.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\HandlerTypeCode.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\MethodFinder.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\MethodSigInfoCreator.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\OpCodeHandler.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\OpCodeHandlerInfo.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\OpCodeHandlerInfoReader.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\OpCodeHandlerInfos.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\VmOpCode.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\v2\VmOpCodeHandlerDetector.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\VmOperand.cs" />
<Compile Include="deobfuscators\ArrayFinder.cs" />
<Compile Include="deobfuscators\Babel_NET\AssemblyResolver.cs" />
<Compile Include="deobfuscators\Babel_NET\BabelInflater.cs" />
@ -233,6 +253,7 @@
<Compile Include="deobfuscators\MethodStack.cs" />
<Compile Include="deobfuscators\MPRESS\Deobfuscator.cs" />
<Compile Include="deobfuscators\MPRESS\Lzmat.cs" />
<Compile Include="deobfuscators\NullStream.cs" />
<Compile Include="deobfuscators\Operations.cs" />
<Compile Include="deobfuscators\ProxyCallFixerBase.cs" />
<Compile Include="deobfuscators\QuickLZ.cs" />
@ -351,6 +372,12 @@
<Name>dnlib</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="deobfuscators\Agile_NET\vm\v2\CsvmResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>CsvmResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>mkdir "..\$(OutDir)..\LICENSES"

View File

@ -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;

View File

@ -85,7 +85,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
ResourceDecrypter resourceDecrypter;
StackFrameHelper stackFrameHelper;
vm.v1.Csvm csvm;
vm.v1.Csvm csvmV1;
vm.v2.Csvm csvmV2;
internal class Options : OptionsBase {
public bool DecryptMethods { get; set; }
@ -166,7 +167,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
ToInt32(stringDecrypter.Detected) +
ToInt32(proxyCallFixer.Detected) +
ToInt32(resourceDecrypter.Detected) +
ToInt32(csvm.Detected);
ToInt32(csvmV1.Detected) +
ToInt32(csvmV2.Detected);
if (sum > 0)
val += 100 + 10 * (sum - 1);
if (cliSecureAttributes.Count != 0)
@ -185,8 +187,10 @@ namespace de4dot.code.deobfuscators.Agile_NET {
resourceDecrypter.Find();
proxyCallFixer = new ProxyCallFixer(module);
proxyCallFixer.FindDelegateCreator();
csvm = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module);
csvm.Find();
csvmV1 = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module);
csvmV1.Find();
csvmV2 = new vm.v2.Csvm(DeobfuscatedFile.DeobfuscatorContext, module);
csvmV2.Find();
}
void FindCliSecureAttribute() {
@ -227,7 +231,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
newOne.stringDecrypter = new StringDecrypter(module, stringDecrypter);
newOne.resourceDecrypter = new ResourceDecrypter(module, resourceDecrypter);
newOne.proxyCallFixer = new ProxyCallFixer(module, proxyCallFixer);
newOne.csvm = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module, csvm);
newOne.csvmV1 = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module, csvmV1);
newOne.csvmV2 = new vm.v2.Csvm(DeobfuscatedFile.DeobfuscatorContext, module, csvmV2);
return newOne;
}
@ -270,9 +275,11 @@ namespace de4dot.code.deobfuscators.Agile_NET {
FindPossibleNamesToRemove(cliSecureRtType.LoadMethod);
}
if (options.RestoreVmCode) {
if (csvm.Restore())
AddResourceToBeRemoved(csvm.Resource, "CSVM data resource");
if (options.RestoreVmCode && (csvmV1.Detected || csvmV2.Detected)) {
if (csvmV1.Detected && csvmV1.Restore())
AddResourceToBeRemoved(csvmV1.Resource, "CSVM data resource");
else if (csvmV2.Detected && csvmV2.Restore())
AddResourceToBeRemoved(csvmV2.Resource, "CSVM data resource");
else {
Logger.e("Couldn't restore VM methods. Use --dont-rename or it will not run");
PreserveTokensAndTypes();

View File

@ -21,11 +21,9 @@ using dnlib.DotNet;
using dnlib.DotNet.Emit;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
namespace de4dot.code.deobfuscators.Agile_NET.vm {
// Tries to restore the operands of the following CIL instructions:
// ldelema
// ldobj
// stobj
// ldelema, ldelem.*, stelem.*, ldobj, stobj
class CilOperandInstructionRestorer {
MethodDef method;
@ -42,10 +40,66 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
if (instr.Operand != null)
continue;
TypeSig operandType = null;
TypeSig operandType = null, operandTypeTmp;
OpCode newOpCode = null;
SZArraySig arrayType;
switch (instr.OpCode.Code) {
case Code.Ldelem:
arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig;
if (arrayType == null)
break;
operandTypeTmp = arrayType.Next;
if (operandTypeTmp == null)
newOpCode = OpCodes.Ldelem_Ref;
else {
switch (operandTypeTmp.ElementType) {
case ElementType.I: newOpCode = OpCodes.Ldelem_I; break;
case ElementType.I1: newOpCode = OpCodes.Ldelem_I1; break;
case ElementType.I2: newOpCode = OpCodes.Ldelem_I2; break;
case ElementType.I4: newOpCode = OpCodes.Ldelem_I4; break;
case ElementType.I8: newOpCode = OpCodes.Ldelem_I8; break;
case ElementType.U: newOpCode = OpCodes.Ldelem_I; break;
case ElementType.U1: newOpCode = OpCodes.Ldelem_U1; break;
case ElementType.U2: newOpCode = OpCodes.Ldelem_U2; break;
case ElementType.U4: newOpCode = OpCodes.Ldelem_U4; break;
case ElementType.U8: newOpCode = OpCodes.Ldelem_I8; break;
case ElementType.R4: newOpCode = OpCodes.Ldelem_R4; break;
case ElementType.R8: newOpCode = OpCodes.Ldelem_R8; break;
default: newOpCode = OpCodes.Ldelem_Ref; break;
//TODO: Ldelem
}
}
break;
case Code.Stelem:
arrayType = MethodStack.GetLoadedType(method, instrs, i, 2) as SZArraySig;
if (arrayType == null)
break;
operandTypeTmp = arrayType.Next;
if (operandTypeTmp == null)
newOpCode = OpCodes.Stelem_Ref;
else {
switch (operandTypeTmp.ElementType) {
case ElementType.U:
case ElementType.I: newOpCode = OpCodes.Stelem_I; break;
case ElementType.U1:
case ElementType.I1: newOpCode = OpCodes.Stelem_I1; break;
case ElementType.U2:
case ElementType.I2: newOpCode = OpCodes.Stelem_I2; break;
case ElementType.U4:
case ElementType.I4: newOpCode = OpCodes.Stelem_I4; break;
case ElementType.U8:
case ElementType.I8: newOpCode = OpCodes.Stelem_I8; break;
case ElementType.R4: newOpCode = OpCodes.Stelem_R4; break;
case ElementType.R8: newOpCode = OpCodes.Stelem_R8; break;
default: newOpCode = OpCodes.Stelem_Ref; break;
//TODO: Stelem
}
}
break;
case Code.Ldelema:
var arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig;
arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig;
if (arrayType == null)
break;
operandType = arrayType.Next;
@ -64,12 +118,14 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
default:
continue;
}
if (!IsValidType(operandType)) {
if (newOpCode == null && !IsValidType(operandType)) {
atLeastOneFailed = true;
continue;
}
instr.Operand = operandType.ToTypeDefOrRef();
if (newOpCode != null)
instr.OpCode = newOpCode;
}
return !atLeastOneFailed;

View File

@ -24,7 +24,7 @@ using dnlib.IO;
using dnlib.DotNet;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
namespace de4dot.code.deobfuscators.Agile_NET.vm {
class CsvmDataReader {
IBinaryReader reader;

View File

@ -19,7 +19,7 @@
using System;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
namespace de4dot.code.deobfuscators.Agile_NET.vm {
class CsvmMethodData {
public Guid Guid { get; set; }
public int Token { get; set; }

View File

@ -0,0 +1,417 @@
/*
Copyright (C) 2011-2013 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Agile_NET.vm {
abstract class CsvmToCilMethodConverterBase {
readonly IDeobfuscatorContext deobfuscatorContext;
readonly protected ModuleDefMD module;
readonly CilOperandInstructionRestorer operandRestorer = new CilOperandInstructionRestorer();
readonly Dictionary<Instruction, int> cilToVmIndex = new Dictionary<Instruction, int>();
readonly Dictionary<int, Instruction> vmIndexToCil = new Dictionary<int, Instruction>();
public CsvmToCilMethodConverterBase(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module) {
this.deobfuscatorContext = deobfuscatorContext;
this.module = module;
}
protected void SetCilToVmIndex(Instruction instr, int vmIndex) {
cilToVmIndex[instr] = vmIndex;
}
protected void SetVmIndexToCil(Instruction instr, int vmIndex) {
vmIndexToCil[vmIndex] = instr;
}
public void Convert(MethodDef cilMethod, CsvmMethodData csvmMethod) {
cilToVmIndex.Clear();
vmIndexToCil.Clear();
var newInstructions = ReadInstructions(cilMethod, csvmMethod);
var newLocals = ReadLocals(cilMethod, csvmMethod);
var newExceptions = ReadExceptions(cilMethod, csvmMethod);
FixInstructionOperands(newInstructions);
FixLocals(newInstructions, cilMethod.Body.Variables);
FixArgs(newInstructions, cilMethod);
DotNetUtils.RestoreBody(cilMethod, newInstructions, newExceptions);
if (!operandRestorer.Restore(cilMethod))
Logger.w("Failed to restore one or more instruction operands in CSVM method {0:X8}", cilMethod.MDToken.ToInt32());
RestoreConstrainedPrefix(cilMethod);
}
void FixLocals(IList<Instruction> instrs, IList<Local> locals) {
foreach (var instr in instrs) {
var op = instr.Operand as LocalOperand;
if (op == null)
continue;
UpdateLocalInstruction(instr, locals[op.Local], op.Local);
}
}
static void UpdateLocalInstruction(Instruction instr, Local local, int index) {
object operand = null;
OpCode opcode;
switch (instr.OpCode.Code) {
case Code.Ldloc_S:
case Code.Ldloc:
if (index == 0)
opcode = OpCodes.Ldloc_0;
else if (index == 1)
opcode = OpCodes.Ldloc_1;
else if (index == 2)
opcode = OpCodes.Ldloc_2;
else if (index == 3)
opcode = OpCodes.Ldloc_3;
else if (byte.MinValue <= index && index <= byte.MaxValue) {
opcode = OpCodes.Ldloc_S;
operand = local;
}
else {
opcode = OpCodes.Ldloc;
operand = local;
}
break;
case Code.Stloc:
case Code.Stloc_S:
if (index == 0)
opcode = OpCodes.Stloc_0;
else if (index == 1)
opcode = OpCodes.Stloc_1;
else if (index == 2)
opcode = OpCodes.Stloc_2;
else if (index == 3)
opcode = OpCodes.Stloc_3;
else if (byte.MinValue <= index && index <= byte.MaxValue) {
opcode = OpCodes.Stloc_S;
operand = local;
}
else {
opcode = OpCodes.Stloc;
operand = local;
}
break;
case Code.Ldloca:
case Code.Ldloca_S:
if (byte.MinValue <= index && index <= byte.MaxValue) {
opcode = OpCodes.Ldloca_S;
operand = local;
}
else {
opcode = OpCodes.Ldloca;
operand = local;
}
break;
default:
throw new ApplicationException("Invalid opcode");
}
instr.OpCode = opcode;
instr.Operand = operand;
}
void FixArgs(IList<Instruction> instrs, MethodDef method) {
foreach (var instr in instrs) {
var op = instr.Operand as ArgOperand;
if (op == null)
continue;
UpdateArgInstruction(instr, method.Parameters[op.Arg], op.Arg);
}
}
static void UpdateArgInstruction(Instruction instr, Parameter arg, int index) {
switch (instr.OpCode.Code) {
case Code.Ldarg:
case Code.Ldarg_S:
if (index == 0) {
instr.OpCode = OpCodes.Ldarg_0;
instr.Operand = null;
}
else if (index == 1) {
instr.OpCode = OpCodes.Ldarg_1;
instr.Operand = null;
}
else if (index == 2) {
instr.OpCode = OpCodes.Ldarg_2;
instr.Operand = null;
}
else if (index == 3) {
instr.OpCode = OpCodes.Ldarg_3;
instr.Operand = null;
}
else if (byte.MinValue <= index && index <= byte.MaxValue) {
instr.OpCode = OpCodes.Ldarg_S;
instr.Operand = arg;
}
else {
instr.OpCode = OpCodes.Ldarg;
instr.Operand = arg;
}
break;
case Code.Starg:
case Code.Starg_S:
if (byte.MinValue <= index && index <= byte.MaxValue) {
instr.OpCode = OpCodes.Starg_S;
instr.Operand = arg;
}
else {
instr.OpCode = OpCodes.Starg;
instr.Operand = arg;
}
break;
case Code.Ldarga:
case Code.Ldarga_S:
if (byte.MinValue <= index && index <= byte.MaxValue) {
instr.OpCode = OpCodes.Ldarga_S;
instr.Operand = arg;
}
else {
instr.OpCode = OpCodes.Ldarga;
instr.Operand = arg;
}
break;
default:
throw new ApplicationException("Invalid opcode");
}
}
protected abstract List<Instruction> ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod);
protected static int GetInstructionSize(Instruction instr) {
var opcode = instr.OpCode;
if (opcode == null)
return 5; // Load store/field
var op = instr.Operand as SwitchTargetDisplOperand;
if (op == null)
return instr.GetSize();
return instr.OpCode.Size + (op.TargetDisplacements.Length + 1) * 4;
}
List<Local> ReadLocals(MethodDef cilMethod, CsvmMethodData csvmMethod) {
var locals = new List<Local>();
var reader = new BinaryReader(new MemoryStream(csvmMethod.Locals));
if (csvmMethod.Locals.Length == 0)
return locals;
// v6.0.0.5 sometimes duplicates the last two locals so only check for a negative value.
int numLocals = reader.ReadInt32();
if (numLocals < 0)
throw new ApplicationException("Invalid number of locals");
for (int i = 0; i < numLocals; i++)
locals.Add(new Local(ReadTypeRef(reader)));
return locals;
}
TypeSig ReadTypeRef(BinaryReader reader) {
var etype = (ElementType)reader.ReadInt32();
switch (etype) {
case ElementType.Void: return module.CorLibTypes.Void;
case ElementType.Boolean: return module.CorLibTypes.Boolean;
case ElementType.Char: return module.CorLibTypes.Char;
case ElementType.I1: return module.CorLibTypes.SByte;
case ElementType.U1: return module.CorLibTypes.Byte;
case ElementType.I2: return module.CorLibTypes.Int16;
case ElementType.U2: return module.CorLibTypes.UInt16;
case ElementType.I4: return module.CorLibTypes.Int32;
case ElementType.U4: return module.CorLibTypes.UInt32;
case ElementType.I8: return module.CorLibTypes.Int64;
case ElementType.U8: return module.CorLibTypes.UInt64;
case ElementType.R4: return module.CorLibTypes.Single;
case ElementType.R8: return module.CorLibTypes.Double;
case ElementType.String: return module.CorLibTypes.String;
case ElementType.TypedByRef: return module.CorLibTypes.TypedReference;
case ElementType.I: return module.CorLibTypes.IntPtr;
case ElementType.U: return module.CorLibTypes.UIntPtr;
case ElementType.Object: return module.CorLibTypes.Object;
case ElementType.ValueType:
case ElementType.Var:
case ElementType.MVar:
return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig();
case ElementType.GenericInst:
etype = (ElementType)reader.ReadInt32();
if (etype == ElementType.ValueType)
return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig();
// ElementType.Class
return module.CorLibTypes.Object;
case ElementType.Ptr:
case ElementType.Class:
case ElementType.Array:
case ElementType.FnPtr:
case ElementType.SZArray:
case ElementType.ByRef:
case ElementType.CModReqd:
case ElementType.CModOpt:
case ElementType.Internal:
case ElementType.Sentinel:
case ElementType.Pinned:
default:
return module.CorLibTypes.Object;
}
}
List<ExceptionHandler> ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod) {
var reader = new BinaryReader(new MemoryStream(csvmMethod.Exceptions));
var ehs = new List<ExceptionHandler>();
if (reader.BaseStream.Length == 0)
return ehs;
int numExceptions = reader.ReadInt32();
if (numExceptions < 0)
throw new ApplicationException("Invalid number of exception handlers");
for (int i = 0; i < numExceptions; i++) {
var eh = new ExceptionHandler((ExceptionHandlerType)reader.ReadInt32());
eh.TryStart = GetInstruction(reader.ReadInt32());
eh.TryEnd = GetInstructionEnd(reader.ReadInt32());
eh.HandlerStart = GetInstruction(reader.ReadInt32());
eh.HandlerEnd = GetInstructionEnd(reader.ReadInt32());
if (eh.HandlerType == ExceptionHandlerType.Catch)
eh.CatchType = module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
else if (eh.HandlerType == ExceptionHandlerType.Filter)
eh.FilterStart = GetInstruction(reader.ReadInt32());
ehs.Add(eh);
}
return ehs;
}
Instruction GetInstruction(int vmIndex) {
return vmIndexToCil[vmIndex];
}
Instruction GetInstructionEnd(int vmIndex) {
vmIndex++;
Instruction instr;
vmIndexToCil.TryGetValue(vmIndex, out instr);
return instr;
}
Instruction GetInstruction(Instruction source, int displ) {
int vmIndex = cilToVmIndex[source];
return vmIndexToCil[vmIndex + displ];
}
void FixInstructionOperands(IList<Instruction> instrs) {
foreach (var instr in instrs) {
var op = instr.Operand as IVmOperand;
if (op != null)
instr.Operand = FixOperand(instrs, instr, op);
}
}
object FixOperand(IList<Instruction> instrs, Instruction instr, IVmOperand vmOperand) {
if (vmOperand is TargetDisplOperand)
return GetInstruction(instr, ((TargetDisplOperand)vmOperand).Displacement);
if (vmOperand is SwitchTargetDisplOperand) {
var targetDispls = ((SwitchTargetDisplOperand)vmOperand).TargetDisplacements;
Instruction[] targets = new Instruction[targetDispls.Length];
for (int i = 0; i < targets.Length; i++)
targets[i] = GetInstruction(instr, targetDispls[i]);
return targets;
}
if (vmOperand is ArgOperand || vmOperand is LocalOperand)
return vmOperand;
if (vmOperand is FieldInstructionOperand) {
var fieldInfo = (FieldInstructionOperand)vmOperand;
return FixLoadStoreFieldInstruction(instr, fieldInfo.Field, fieldInfo.StaticOpCode, fieldInfo.InstanceOpCode);
}
throw new ApplicationException(string.Format("Unknown operand type: {0}", vmOperand.GetType()));
}
IField FixLoadStoreFieldInstruction(Instruction instr, IField fieldRef, OpCode staticInstr, OpCode instanceInstr) {
var field = deobfuscatorContext.ResolveField(fieldRef);
bool isStatic;
if (field == null) {
Logger.w("Could not resolve field {0:X8}. Assuming it's not static.", fieldRef == null ? 0 : fieldRef.MDToken.Raw);
isStatic = false;
}
else
isStatic = field.IsStatic;
instr.OpCode = isStatic ? staticInstr : instanceInstr;
return fieldRef;
}
static void RestoreConstrainedPrefix(MethodDef method) {
if (method == null || method.Body == null)
return;
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode.Code != Code.Callvirt)
continue;
var calledMethod = instr.Operand as IMethod;
if (calledMethod == null)
continue;
var sig = calledMethod.MethodSig;
if (sig == null || !sig.HasThis)
continue;
var thisType = MethodStack.GetLoadedType(method, instrs, i, sig.Params.Count) as ByRefSig;
if (thisType == null)
continue;
if (HasPrefix(instrs, i, Code.Constrained))
continue;
instrs.Insert(i, OpCodes.Constrained.ToInstruction(thisType.Next.ToTypeDefOrRef()));
i++;
}
}
static bool HasPrefix(IList<Instruction> instrs, int index, Code prefix) {
index--;
for (; index >= 0; index--) {
var instr = instrs[index];
if (instr.OpCode.OpCodeType != OpCodeType.Prefix)
break;
if (instr.OpCode.Code == prefix)
return true;
}
return false;
}
}
}

View File

@ -17,66 +17,50 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
using dnlib.DotNet;
using dnlib.DotNet.Emit;
namespace de4dot.code.deobfuscators.Agile_NET.vm {
interface IVmOperand {
}
class TokenOperand : IVmOperand {
public int token; // any type of token
public TokenOperand(int token) {
this.token = token;
}
}
class TargetDisplOperand : IVmOperand {
public int displacement; // number of instructions from current instr
public readonly int Displacement; // number of VM instructions from current VM instr
public TargetDisplOperand(int displacement) {
this.displacement = displacement;
this.Displacement = displacement;
}
}
class SwitchTargetDisplOperand : IVmOperand {
public int[] targetDisplacements; // number of instructions from current instr
public readonly int[] TargetDisplacements; // number of VM instructions from current VM instr
public SwitchTargetDisplOperand(int[] targetDisplacements) {
this.targetDisplacements = targetDisplacements;
this.TargetDisplacements = targetDisplacements;
}
}
class ArgOperand : IVmOperand {
public ushort arg;
public readonly ushort Arg;
public ArgOperand(ushort arg) {
this.arg = arg;
this.Arg = arg;
}
}
class LocalOperand : IVmOperand {
public ushort local;
public readonly ushort Local;
public LocalOperand(ushort local) {
this.local = local;
this.Local = local;
}
}
// OpCode must be changed to ldfld / ldsfld
class LoadFieldOperand : IVmOperand {
public int token;
public LoadFieldOperand(int token) {
this.token = token;
}
}
class FieldInstructionOperand : IVmOperand {
public readonly OpCode StaticOpCode;
public readonly OpCode InstanceOpCode;
public readonly IField Field;
// OpCode must be changed to ldflda / ldsflda
class LoadFieldAddressOperand : IVmOperand {
public int token;
public LoadFieldAddressOperand(int token) {
this.token = token;
}
}
// OpCode must be changed to stfld / stsfld
class StoreFieldOperand : IVmOperand {
public int token;
public StoreFieldOperand(int token) {
this.token = token;
public FieldInstructionOperand(OpCode staticOpCode, OpCode instanceOpCode, IField field) {
this.StaticOpCode = staticOpCode;
this.InstanceOpCode = instanceOpCode;
this.Field = field;
}
}
}

View File

@ -140,7 +140,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
var vmModulePath = Path.Combine(Path.GetDirectoryName(module.Location), vmFilename);
Logger.v("CSVM filename: {0}", vmFilename);
var dataKey = "cs cached VmOpCodeHandlerDetector";
var dataKey = "cs cached VmOpCodeHandlerDetector v1";
var dict = (Dictionary<string, VmOpCodeHandlerDetector>)deobfuscatorContext.GetData(dataKey);
if (dict == null)
deobfuscatorContext.SetData(dataKey, dict = new Dictionary<string, VmOpCodeHandlerDetector>(StringComparer.OrdinalIgnoreCase));

View File

@ -25,411 +25,28 @@ using dnlib.DotNet.Emit;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
class CsvmToCilMethodConverter {
IDeobfuscatorContext deobfuscatorContext;
ModuleDefMD module;
class CsvmToCilMethodConverter : CsvmToCilMethodConverterBase {
VmOpCodeHandlerDetector opCodeDetector;
CilOperandInstructionRestorer operandRestorer = new CilOperandInstructionRestorer();
public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector) {
this.deobfuscatorContext = deobfuscatorContext;
this.module = module;
public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector)
: base(deobfuscatorContext, module) {
this.opCodeDetector = opCodeDetector;
}
public void Convert(MethodDef cilMethod, CsvmMethodData csvmMethod) {
var newInstructions = ReadInstructions(cilMethod, csvmMethod);
var newLocals = ReadLocals(cilMethod, csvmMethod);
var newExceptions = ReadExceptions(cilMethod, csvmMethod, newInstructions);
FixInstructionOperands(newInstructions);
FixLocals(newInstructions, cilMethod.Body.Variables);
FixArgs(newInstructions, cilMethod);
DotNetUtils.RestoreBody(cilMethod, newInstructions, newExceptions);
if (!operandRestorer.Restore(cilMethod))
Logger.w("Failed to restore one or more instruction operands in CSVM method {0:X8}", cilMethod.MDToken.ToInt32());
RestoreConstrainedPrefix(cilMethod);
}
void FixLocals(IList<Instruction> instrs, IList<Local> locals) {
foreach (var instr in instrs) {
var op = instr.Operand as LocalOperand;
if (op == null)
continue;
UpdateLocalInstruction(instr, locals[op.local], op.local);
}
}
static void UpdateLocalInstruction(Instruction instr, Local local, int index) {
object operand = null;
OpCode opcode;
switch (instr.OpCode.Code) {
case Code.Ldloc_S:
case Code.Ldloc:
if (index == 0)
opcode = OpCodes.Ldloc_0;
else if (index == 1)
opcode = OpCodes.Ldloc_1;
else if (index == 2)
opcode = OpCodes.Ldloc_2;
else if (index == 3)
opcode = OpCodes.Ldloc_3;
else if (byte.MinValue <= index && index <= byte.MaxValue) {
opcode = OpCodes.Ldloc_S;
operand = local;
}
else {
opcode = OpCodes.Ldloc;
operand = local;
}
break;
case Code.Stloc:
case Code.Stloc_S:
if (index == 0)
opcode = OpCodes.Stloc_0;
else if (index == 1)
opcode = OpCodes.Stloc_1;
else if (index == 2)
opcode = OpCodes.Stloc_2;
else if (index == 3)
opcode = OpCodes.Stloc_3;
else if (byte.MinValue <= index && index <= byte.MaxValue) {
opcode = OpCodes.Stloc_S;
operand = local;
}
else {
opcode = OpCodes.Stloc;
operand = local;
}
break;
case Code.Ldloca:
case Code.Ldloca_S:
if (byte.MinValue <= index && index <= byte.MaxValue) {
opcode = OpCodes.Ldloca_S;
operand = local;
}
else {
opcode = OpCodes.Ldloca;
operand = local;
}
break;
default:
throw new ApplicationException("Invalid opcode");
}
instr.OpCode = opcode;
instr.Operand = operand;
}
void FixArgs(IList<Instruction> instrs, MethodDef method) {
foreach (var instr in instrs) {
var op = instr.Operand as ArgOperand;
if (op == null)
continue;
UpdateArgInstruction(instr, method.Parameters[op.arg], op.arg);
}
}
static void UpdateArgInstruction(Instruction instr, Parameter arg, int index) {
switch (instr.OpCode.Code) {
case Code.Ldarg:
case Code.Ldarg_S:
if (index == 0) {
instr.OpCode = OpCodes.Ldarg_0;
instr.Operand = null;
}
else if (index == 1) {
instr.OpCode = OpCodes.Ldarg_1;
instr.Operand = null;
}
else if (index == 2) {
instr.OpCode = OpCodes.Ldarg_2;
instr.Operand = null;
}
else if (index == 3) {
instr.OpCode = OpCodes.Ldarg_3;
instr.Operand = null;
}
else if (byte.MinValue <= index && index <= byte.MaxValue) {
instr.OpCode = OpCodes.Ldarg_S;
instr.Operand = arg;
}
else {
instr.OpCode = OpCodes.Ldarg;
instr.Operand = arg;
}
break;
case Code.Starg:
case Code.Starg_S:
if (byte.MinValue <= index && index <= byte.MaxValue) {
instr.OpCode = OpCodes.Starg_S;
instr.Operand = arg;
}
else {
instr.OpCode = OpCodes.Starg;
instr.Operand = arg;
}
break;
case Code.Ldarga:
case Code.Ldarga_S:
if (byte.MinValue <= index && index <= byte.MaxValue) {
instr.OpCode = OpCodes.Ldarga_S;
instr.Operand = arg;
}
else {
instr.OpCode = OpCodes.Ldarga;
instr.Operand = arg;
}
break;
default:
throw new ApplicationException("Invalid opcode");
}
}
List<Instruction> ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) {
protected override List<Instruction> ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) {
var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions));
var instrs = new List<Instruction>();
uint offset = 0;
while (reader.BaseStream.Position < reader.BaseStream.Length) {
int vmOpCode = reader.ReadUInt16();
var instr = opCodeDetector.Handlers[vmOpCode].Read(reader);
var instr = opCodeDetector.Handlers[vmOpCode].Read(reader, module);
instr.Offset = offset;
offset += (uint)GetInstructionSize(instr);
SetCilToVmIndex(instr, instrs.Count);
SetVmIndexToCil(instr, instrs.Count);
instrs.Add(instr);
}
return instrs;
}
static int GetInstructionSize(Instruction instr) {
var opcode = instr.OpCode;
if (opcode == null)
return 5; // Load store/field
var op = instr.Operand as SwitchTargetDisplOperand;
if (op == null)
return instr.GetSize();
return instr.OpCode.Size + (op.targetDisplacements.Length + 1) * 4;
}
List<Local> ReadLocals(MethodDef cilMethod, CsvmMethodData csvmMethod) {
var locals = new List<Local>();
var reader = new BinaryReader(new MemoryStream(csvmMethod.Locals));
if (csvmMethod.Locals.Length == 0)
return locals;
// v6.0.0.5 sometimes duplicates the last two locals so only check for a negative value.
int numLocals = reader.ReadInt32();
if (numLocals < 0)
throw new ApplicationException("Invalid number of locals");
for (int i = 0; i < numLocals; i++)
locals.Add(new Local(ReadTypeRef(reader)));
return locals;
}
TypeSig ReadTypeRef(BinaryReader reader) {
var etype = (ElementType)reader.ReadInt32();
switch (etype) {
case ElementType.Void: return module.CorLibTypes.Void;
case ElementType.Boolean: return module.CorLibTypes.Boolean;
case ElementType.Char: return module.CorLibTypes.Char;
case ElementType.I1: return module.CorLibTypes.SByte;
case ElementType.U1: return module.CorLibTypes.Byte;
case ElementType.I2: return module.CorLibTypes.Int16;
case ElementType.U2: return module.CorLibTypes.UInt16;
case ElementType.I4: return module.CorLibTypes.Int32;
case ElementType.U4: return module.CorLibTypes.UInt32;
case ElementType.I8: return module.CorLibTypes.Int64;
case ElementType.U8: return module.CorLibTypes.UInt64;
case ElementType.R4: return module.CorLibTypes.Single;
case ElementType.R8: return module.CorLibTypes.Double;
case ElementType.String: return module.CorLibTypes.String;
case ElementType.TypedByRef: return module.CorLibTypes.TypedReference;
case ElementType.I: return module.CorLibTypes.IntPtr;
case ElementType.U: return module.CorLibTypes.UIntPtr;
case ElementType.Object: return module.CorLibTypes.Object;
case ElementType.ValueType:
case ElementType.Var:
case ElementType.MVar:
return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig();
case ElementType.GenericInst:
etype = (ElementType)reader.ReadInt32();
if (etype == ElementType.ValueType)
return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig();
// ElementType.Class
return module.CorLibTypes.Object;
case ElementType.Ptr:
case ElementType.Class:
case ElementType.Array:
case ElementType.FnPtr:
case ElementType.SZArray:
case ElementType.ByRef:
case ElementType.CModReqd:
case ElementType.CModOpt:
case ElementType.Internal:
case ElementType.Sentinel:
case ElementType.Pinned:
default:
return module.CorLibTypes.Object;
}
}
List<ExceptionHandler> ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod, List<Instruction> cilInstructions) {
var reader = new BinaryReader(new MemoryStream(csvmMethod.Exceptions));
var ehs = new List<ExceptionHandler>();
if (reader.BaseStream.Length == 0)
return ehs;
int numExceptions = reader.ReadInt32();
if (numExceptions < 0)
throw new ApplicationException("Invalid number of exception handlers");
for (int i = 0; i < numExceptions; i++) {
var eh = new ExceptionHandler((ExceptionHandlerType)reader.ReadInt32());
eh.TryStart = GetInstruction(cilInstructions, reader.ReadInt32());
eh.TryEnd = GetInstructionEnd(cilInstructions, reader.ReadInt32());
eh.HandlerStart = GetInstruction(cilInstructions, reader.ReadInt32());
eh.HandlerEnd = GetInstructionEnd(cilInstructions, reader.ReadInt32());
if (eh.HandlerType == ExceptionHandlerType.Catch)
eh.CatchType = module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
else if (eh.HandlerType == ExceptionHandlerType.Filter)
eh.FilterStart = GetInstruction(cilInstructions, reader.ReadInt32());
ehs.Add(eh);
}
return ehs;
}
static Instruction GetInstruction(IList<Instruction> instrs, int index) {
return instrs[index];
}
static Instruction GetInstructionEnd(IList<Instruction> instrs, int index) {
index++;
if (index == instrs.Count)
return null;
return instrs[index];
}
static Instruction GetInstruction(IList<Instruction> instrs, Instruction source, int displ) {
int sourceIndex = instrs.IndexOf(source);
if (sourceIndex < 0)
throw new ApplicationException("Could not find source instruction");
return instrs[sourceIndex + displ];
}
void FixInstructionOperands(IList<Instruction> instrs) {
foreach (var instr in instrs) {
var op = instr.Operand as IVmOperand;
if (op != null)
instr.Operand = FixOperand(instrs, instr, op);
}
}
object FixOperand(IList<Instruction> instrs, Instruction instr, IVmOperand vmOperand) {
if (vmOperand is TokenOperand)
return GetMemberRef(((TokenOperand)vmOperand).token);
if (vmOperand is TargetDisplOperand)
return GetInstruction(instrs, instr, ((TargetDisplOperand)vmOperand).displacement);
if (vmOperand is SwitchTargetDisplOperand) {
var targetDispls = ((SwitchTargetDisplOperand)vmOperand).targetDisplacements;
Instruction[] targets = new Instruction[targetDispls.Length];
for (int i = 0; i < targets.Length; i++)
targets[i] = GetInstruction(instrs, instr, targetDispls[i]);
return targets;
}
if (vmOperand is ArgOperand || vmOperand is LocalOperand)
return vmOperand;
if (vmOperand is LoadFieldOperand)
return FixLoadStoreFieldInstruction(instr, ((LoadFieldOperand)vmOperand).token, OpCodes.Ldsfld, OpCodes.Ldfld);
if (vmOperand is LoadFieldAddressOperand)
return FixLoadStoreFieldInstruction(instr, ((LoadFieldAddressOperand)vmOperand).token, OpCodes.Ldsflda, OpCodes.Ldflda);
if (vmOperand is StoreFieldOperand)
return FixLoadStoreFieldInstruction(instr, ((StoreFieldOperand)vmOperand).token, OpCodes.Stsfld, OpCodes.Stfld);
throw new ApplicationException(string.Format("Unknown operand type: {0}", vmOperand.GetType()));
}
IField FixLoadStoreFieldInstruction(Instruction instr, int token, OpCode staticInstr, OpCode instanceInstr) {
var fieldRef = module.ResolveToken(token) as IField;
var field = deobfuscatorContext.ResolveField(fieldRef);
bool isStatic;
if (field == null) {
Logger.w("Could not resolve field {0:X8}. Assuming it's not static.", token);
isStatic = false;
}
else
isStatic = field.IsStatic;
instr.OpCode = isStatic ? staticInstr : instanceInstr;
return fieldRef;
}
ITokenOperand GetMemberRef(int token) {
var memberRef = module.ResolveToken(token) as ITokenOperand;
if (memberRef == null)
throw new ApplicationException(string.Format("Could not find member ref: {0:X8}", token));
return memberRef;
}
static void RestoreConstrainedPrefix(MethodDef method) {
if (method == null || method.Body == null)
return;
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode.Code != Code.Callvirt)
continue;
var calledMethod = instr.Operand as IMethod;
if (calledMethod == null)
continue;
var sig = calledMethod.MethodSig;
if (sig == null || !sig.HasThis)
continue;
var thisType = MethodStack.GetLoadedType(method, instrs, i, sig.Params.Count) as ByRefSig;
if (thisType == null)
continue;
if (HasPrefix(instrs, i, Code.Constrained))
continue;
instrs.Insert(i, OpCodes.Constrained.ToInstruction(thisType.Next.ToTypeDefOrRef()));
i++;
}
}
static bool HasPrefix(IList<Instruction> instrs, int index, Code prefix) {
index--;
for (; index >= 0; index--) {
var instr = instrs[index];
if (instr.OpCode.OpCodeType != OpCodeType.Prefix)
break;
if (instr.OpCode.Code == prefix)
return true;
}
return false;
}
}
}

View File

@ -29,7 +29,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
public string Name { get; set; }
public OpCodeHandlerSigInfo OpCodeHandlerSigInfo { get; set; }
public Predicate<UnknownHandlerInfo> Check { get; set; }
public Func<BinaryReader, Instruction> Read { get; set; }
public Func<BinaryReader, IInstructionOperandResolver, Instruction> Read { get; set; }
public bool Detect(UnknownHandlerInfo info) {
var sigInfo = OpCodeHandlerSigInfo;
@ -68,7 +68,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
}
static partial class OpCodeHandlers {
static Instruction arithmetic_read(BinaryReader reader) {
static Instruction arithmetic_read(BinaryReader reader, IInstructionOperandResolver resolver) {
switch (reader.ReadByte()) {
case 0: return OpCodes.Add.ToInstruction();
case 1: return OpCodes.Add_Ovf.ToInstruction();
@ -91,25 +91,22 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Type System.Reflection.Module::ResolveType(System.Int32)");
}
static Instruction newarr_read(BinaryReader reader) {
return new Instruction {
OpCode = OpCodes.Newarr,
Operand = new TokenOperand(reader.ReadInt32()),
};
static Instruction newarr_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return new Instruction(OpCodes.Newarr, resolver.ResolveToken(reader.ReadUInt32()));
}
static Instruction box_read(BinaryReader reader) {
static Instruction box_read(BinaryReader reader, IInstructionOperandResolver resolver) {
var instr = new Instruction();
switch (reader.ReadByte()) {
case 0: instr.OpCode = OpCodes.Box; break;
case 1: instr.OpCode = OpCodes.Unbox_Any; break;
default: throw new ApplicationException("Invalid opcode");
}
instr.Operand = new TokenOperand(reader.ReadInt32());
instr.Operand = resolver.ResolveToken(reader.ReadUInt32());
return instr;
}
static Instruction call_read(BinaryReader reader) {
static Instruction call_read(BinaryReader reader, IInstructionOperandResolver resolver) {
var instr = new Instruction();
switch (reader.ReadByte()) {
case 0: instr.OpCode = OpCodes.Newobj; break;
@ -117,22 +114,22 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
case 2: instr.OpCode = OpCodes.Callvirt; break;
default: throw new ApplicationException("Invalid opcode");
}
instr.Operand = new TokenOperand(reader.ReadInt32());
instr.Operand = resolver.ResolveToken(reader.ReadUInt32());
return instr;
}
static Instruction cast_read(BinaryReader reader) {
static Instruction cast_read(BinaryReader reader, IInstructionOperandResolver resolver) {
var instr = new Instruction();
switch (reader.ReadByte()) {
case 0: instr.OpCode = OpCodes.Castclass; break;
case 1: instr.OpCode = OpCodes.Isinst; break;
default: throw new ApplicationException("Invalid opcode");
}
instr.Operand = new TokenOperand(reader.ReadInt32());
instr.Operand = resolver.ResolveToken(reader.ReadUInt32());
return instr;
}
static Instruction compare_read(BinaryReader reader) {
static Instruction compare_read(BinaryReader reader, IInstructionOperandResolver resolver) {
int type = reader.ReadByte();
Instruction instr = new Instruction();
switch (type) {
@ -205,7 +202,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
new InstructionInfo1 { Type = 11, Second = true, Third = true, OpCode = OpCodes.Conv_Ovf_U_Un },
new InstructionInfo1 { Type = 12, Second = true, Third = true, OpCode = OpCodes.Conv_R_Un },
};
static Instruction convert_read(BinaryReader reader) {
static Instruction convert_read(BinaryReader reader, IInstructionOperandResolver resolver) {
byte type = reader.ReadByte();
bool second = reader.ReadBoolean();
bool third = reader.ReadBoolean();
@ -215,7 +212,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
if (type != info.Type || info.Second != second || info.Third != third)
continue;
instr = new Instruction { OpCode = info.OpCode };
instr = new Instruction(info.OpCode);
break;
}
if (instr == null)
@ -224,7 +221,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return instr;
}
static Instruction dup_read(BinaryReader reader) {
static Instruction dup_read(BinaryReader reader, IInstructionOperandResolver resolver) {
switch (reader.ReadByte()) {
case 0: return OpCodes.Dup.ToInstruction();
case 1: return OpCodes.Pop.ToInstruction();
@ -262,7 +259,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
new InstructionInfo2 { First = true, Second = true, Value = 28, OpCode = OpCodes.Ldelem_Ref },
new InstructionInfo2 { First = true, Second = false, Value = 0, OpCode = OpCodes.Ldelem },
};
static Instruction ldelem_read(BinaryReader reader) {
static Instruction ldelem_read(BinaryReader reader, IInstructionOperandResolver resolver) {
Instruction instr = null;
bool first = reader.ReadBoolean();
bool second = reader.ReadBoolean();
@ -274,9 +271,9 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
continue;
if (second)
instr = new Instruction { OpCode = info.OpCode };
instr = new Instruction(info.OpCode);
else
instr = new Instruction { OpCode = info.OpCode, Operand = new TokenOperand(value) };
instr = new Instruction(info.OpCode, resolver.ResolveToken((uint)value));
break;
}
if (instr == null)
@ -289,29 +286,26 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags)");
}
static Instruction endfinally_read(BinaryReader reader) {
static Instruction endfinally_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return OpCodes.Endfinally.ToInstruction();
}
static Instruction ldfld_read(BinaryReader reader) {
var instr = new Instruction();
switch (reader.ReadByte()) {
case 0: instr.Operand = new LoadFieldOperand(reader.ReadInt32()); break;
case 1: instr.Operand = new LoadFieldAddressOperand(reader.ReadInt32()); break;
case 2: instr.Operand = new StoreFieldOperand(reader.ReadInt32()); break;
static Instruction ldfld_read(BinaryReader reader, IInstructionOperandResolver resolver) {
byte b = reader.ReadByte();
var field = resolver.ResolveToken(reader.ReadUInt32()) as IField;
switch (b) {
case 0: return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsfld, OpCodes.Ldfld, field));
case 1: return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsflda, OpCodes.Ldflda, field));
case 2: return new Instruction(null, new FieldInstructionOperand(OpCodes.Stsfld, OpCodes.Stfld, field));
default: throw new ApplicationException("Invalid opcode");
}
return instr;
}
static Instruction initobj_read(BinaryReader reader) {
return new Instruction {
OpCode = OpCodes.Initobj,
Operand = new TokenOperand(reader.ReadInt32()),
};
static Instruction initobj_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return new Instruction(OpCodes.Initobj, resolver.ResolveToken(reader.ReadUInt32()));
}
static Instruction ldloc_read(BinaryReader reader) {
static Instruction ldloc_read(BinaryReader reader, IInstructionOperandResolver resolver) {
bool isLdarg = reader.ReadBoolean();
ushort index = reader.ReadUInt16();
@ -328,7 +322,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return instr;
}
static Instruction ldloca_read(BinaryReader reader) {
static Instruction ldloca_read(BinaryReader reader, IInstructionOperandResolver resolver) {
Instruction instr = new Instruction();
if (reader.ReadBoolean()) {
instr.OpCode = OpCodes.Ldarga;
@ -342,25 +336,19 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return instr;
}
static Instruction ldelema_read(BinaryReader reader) {
return new Instruction {
OpCode = OpCodes.Ldelema,
Operand = null,
};
static Instruction ldelema_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return new Instruction(OpCodes.Ldelema, null);
}
static Instruction ldlen_read(BinaryReader reader) {
static Instruction ldlen_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return OpCodes.Ldlen.ToInstruction();
}
static Instruction ldobj_read(BinaryReader reader) {
return new Instruction {
OpCode = OpCodes.Ldobj,
Operand = null,
};
static Instruction ldobj_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return new Instruction(OpCodes.Ldobj, null);
}
static Instruction ldstr_read(BinaryReader reader) {
static Instruction ldstr_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return OpCodes.Ldstr.ToInstruction(reader.ReadString());
}
@ -368,11 +356,8 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MemberInfo System.Reflection.Module::ResolveMember(System.Int32)");
}
static Instruction ldtoken_read(BinaryReader reader) {
return new Instruction {
OpCode = OpCodes.Ldtoken,
Operand = new TokenOperand(reader.ReadInt32()),
};
static Instruction ldtoken_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return new Instruction(OpCodes.Ldtoken, resolver.ResolveToken(reader.ReadUInt32()));
}
static bool leave_check(UnknownHandlerInfo info) {
@ -381,15 +366,12 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
!DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MemberInfo System.Reflection.Module::ResolveMember(System.Int32)");
}
static Instruction leave_read(BinaryReader reader) {
static Instruction leave_read(BinaryReader reader, IInstructionOperandResolver resolver) {
int displacement = reader.ReadInt32();
return new Instruction {
OpCode = OpCodes.Leave,
Operand = new TargetDisplOperand(displacement),
};
return new Instruction(OpCodes.Leave, new TargetDisplOperand(displacement));
}
static Instruction ldc_read(BinaryReader reader) {
static Instruction ldc_read(BinaryReader reader, IInstructionOperandResolver resolver) {
switch ((ElementType)reader.ReadByte()) {
case ElementType.I4: return Instruction.CreateLdcI4(reader.ReadInt32());
case ElementType.I8: return OpCodes.Ldc_I8.ToInstruction(reader.ReadInt64());
@ -400,29 +382,24 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
}
}
static Instruction ldftn_read(BinaryReader reader) {
static Instruction ldftn_read(BinaryReader reader, IInstructionOperandResolver resolver) {
byte code = reader.ReadByte();
int token = reader.ReadInt32();
uint token = reader.ReadUInt32();
Instruction instr;
switch (code) {
case 0:
instr = new Instruction { OpCode = OpCodes.Ldftn, Operand = new TokenOperand(token) };
break;
return new Instruction(OpCodes.Ldftn, resolver.ResolveToken(token));
case 1:
reader.ReadInt32(); // token of newobj .ctor
instr = new Instruction { OpCode = OpCodes.Ldvirtftn, Operand = new TokenOperand(token) };
break;
return new Instruction(OpCodes.Ldvirtftn, resolver.ResolveToken(token));
default:
throw new ApplicationException("Invalid opcode");
}
return instr;
}
static Instruction logical_read(BinaryReader reader) {
static Instruction logical_read(BinaryReader reader, IInstructionOperandResolver resolver) {
switch (reader.ReadByte()) {
case 0: return OpCodes.And.ToInstruction();
case 1: return OpCodes.Or.ToInstruction();
@ -448,7 +425,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return false;
}
static Instruction nop_read(BinaryReader reader) {
static Instruction nop_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return OpCodes.Nop.ToInstruction();
}
@ -456,7 +433,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodBase System.Reflection.Module::ResolveMethod(System.Int32)");
}
static Instruction ret_read(BinaryReader reader) {
static Instruction ret_read(BinaryReader reader, IInstructionOperandResolver resolver) {
reader.ReadInt32(); // token of current method
return OpCodes.Ret.ToInstruction();
}
@ -465,11 +442,11 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return info.ExecuteMethod.Body.Variables.Count == 0;
}
static Instruction rethrow_read(BinaryReader reader) {
static Instruction rethrow_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return OpCodes.Rethrow.ToInstruction();
}
static Instruction stloc_read(BinaryReader reader) {
static Instruction stloc_read(BinaryReader reader, IInstructionOperandResolver resolver) {
bool isStarg = reader.ReadBoolean();
ushort index = reader.ReadUInt16();
@ -487,33 +464,27 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
return instr;
}
static Instruction stobj_read(BinaryReader reader) {
return new Instruction {
OpCode = OpCodes.Stobj,
Operand = null,
};
static Instruction stobj_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return new Instruction(OpCodes.Stobj, null);
}
static Instruction switch_read(BinaryReader reader) {
static Instruction switch_read(BinaryReader reader, IInstructionOperandResolver resolver) {
int numTargets = reader.ReadInt32();
int[] targetDispls = new int[numTargets];
for (int i = 0; i < targetDispls.Length; i++)
targetDispls[i] = reader.ReadInt32();
return new Instruction {
OpCode = OpCodes.Switch,
Operand = new SwitchTargetDisplOperand(targetDispls),
};
return new Instruction(OpCodes.Switch, new SwitchTargetDisplOperand(targetDispls));
}
static bool throw_check(UnknownHandlerInfo info) {
return !DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags)");
}
static Instruction throw_read(BinaryReader reader) {
static Instruction throw_read(BinaryReader reader, IInstructionOperandResolver resolver) {
return OpCodes.Throw.ToInstruction();
}
static Instruction neg_read(BinaryReader reader) {
static Instruction neg_read(BinaryReader reader, IInstructionOperandResolver resolver) {
switch (reader.ReadByte()) {
case 0: return OpCodes.Neg.ToInstruction();
case 1: return OpCodes.Not.ToInstruction();

View File

@ -19,7 +19,7 @@
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
static partial class OpCodeHandlers {
public static readonly OpCodeHandler[][] opcodeHandlers = new OpCodeHandler[][] {
public static readonly OpCodeHandler[][] Handlers = new OpCodeHandler[][] {
new OpCodeHandler[] {
new OpCodeHandler {
Name = "arithmetic",

View File

@ -214,7 +214,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
opCodeHandlers = new List<OpCodeHandler>();
var detected = new List<OpCodeHandler>();
foreach (var handlersList in OpCodeHandlers.opcodeHandlers) {
foreach (var handlersList in OpCodeHandlers.Handlers) {
opCodeHandlers.Clear();
foreach (var handlerType in handlerTypes) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<OpCodeHandler> handlers;
public CompositeHandlerDetector(IList<OpCodeHandler> handlers) {
this.handlers = new List<OpCodeHandler>(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<Block> 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<Block> 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<int, bool> VisitedCompositeBlocks;
public bool Done;
public FindHandlerState(HandlerState compositeState) {
this.CompositeState = compositeState;
this.VisitedCompositeBlocks = new Dictionary<int, bool>();
this.Done = false;
}
public FindHandlerState(HandlerState compositeState, Dictionary<int, bool> visitedCompositeBlocks, bool done) {
this.CompositeState = compositeState;
this.VisitedCompositeBlocks = new Dictionary<int, bool>(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<MatchState> stack = new Stack<MatchState>();
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<Block> 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<Instruction>;
var bl = b as IList<Instruction>;
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;
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<OpCodeHandlerInfo> OpCodeHandlerInfos { get; private set; }
public CompositeOpCodeHandler(TypeDef handlerType, HandlerMethod execMethod) {
this.HandlerType = handlerType;
this.ExecMethod = execMethod;
this.OpCodeHandlerInfos = new List<OpCodeHandlerInfo>();
}
public override string ToString() {
if (OpCodeHandlerInfos.Count == 0)
return "<nothing>";
var sb = new StringBuilder();
foreach (var handler in OpCodeHandlerInfos) {
if (sb.Length != 0)
sb.Append(", ");
sb.Append(handler.Name);
}
return sb.ToString();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<string, VmOpCodeHandlerDetector>)deobfuscatorContext.GetData(dataKey);
if (dict == null)
deobfuscatorContext.SetData(dataKey, dict = new Dictionary<string, VmOpCodeHandlerDetector>(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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}
}

View File

@ -0,0 +1,93 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[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;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] CSVM1_v2 {
get {
object obj = ResourceManager.GetObject("CSVM1_v2", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] CSVM2_v2 {
get {
object obj = ResourceManager.GetObject("CSVM2_v2", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] CSVM3_v2 {
get {
object obj = ResourceManager.GetObject("CSVM3_v2", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="CSVM1_v2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>CSVM1_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="CSVM2_v2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>CSVM2_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="CSVM3_v2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>CSVM3_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Instruction> ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) {
var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions));
var instrs = new List<Instruction>();
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<OpCodeHandlerInfo>() { 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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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,
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
class MethodFinder {
readonly IList<OpCodeHandlerInfo> handlerInfos;
readonly PrimitiveHandlerMethod handlerMethod;
class SigState {
public readonly MethodSigInfo SigInfo;
public SigState(PrimitiveHandlerMethod handlerMethod) {
this.SigInfo = handlerMethod.Sig;
}
}
public MethodFinder(IList<OpCodeHandlerInfo> 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<int, int> sigIndexToHandlerIndex = new Dictionary<int, int>();
Dictionary<int, int> handlerIndexToSigIndex = new Dictionary<int, int>();
Stack<MatchInfo> stack = new Stack<MatchInfo>();
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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<BlockInfo> blockInfos;
public List<BlockInfo> BlockInfos {
get { return blockInfos; }
}
public MethodSigInfo() {
this.blockInfos = new List<BlockInfo>();
}
public MethodSigInfo(IEnumerable<BlockInfo> blockInfos) {
this.blockInfos = new List<BlockInfo>(blockInfos);
}
}
class BlockInfo : IEquatable<BlockInfo> {
readonly List<int> targets;
public byte[] Hash { get; set; }
public List<int> Targets {
get { return targets; }
}
public BlockInfo() {
this.targets = new List<int>();
}
public BlockInfo(byte[] hash, IEnumerable<int> targets) {
this.Hash = hash;
this.targets = new List<int>(targets);
}
public override string ToString() {
if (Hash == null)
return "<null>";
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<Block> allBlocks;
Dictionary<Block, BlockInfo> blockToInfo;
Dictionary<object, int> methodToId = new Dictionary<object, int>();
public void AddId(object key, int id) {
if (key != null)
methodToId[key] = id;
}
int GetId(object key) {
if (key == null)
return int.MinValue;
int id;
if (methodToId.TryGetValue(key, out id))
return id;
return int.MinValue + 1;
}
public MethodSigInfo Create(Blocks blocks) {
methodSigInfo = new MethodSigInfo();
this.blocks = blocks;
allBlocks = blocks.MethodBlocks.GetAllBlocks();
blockToInfo = new Dictionary<Block, BlockInfo>();
foreach (var block in allBlocks) {
var blockInfo = new BlockInfo();
blockToInfo[block] = blockInfo;
methodSigInfo.BlockInfos.Add(blockInfo);
}
foreach (var block in allBlocks) {
var blockInfo = blockToInfo[block];
Update(blockInfo, block);
if (block.FallThrough != null)
blockInfo.Targets.Add(allBlocks.IndexOf(block.FallThrough));
if (block.Targets != null) {
foreach (var target in block.Targets)
blockInfo.Targets.Add(allBlocks.IndexOf(target));
}
}
return methodSigInfo;
}
void Update(BlockInfo blockInfo, Block block) {
using (var hasher = MD5.Create()) {
bool emptyHash;
using (var outStream = new NullStream()) {
using (var csStream = new CryptoStream(outStream, hasher, CryptoStreamMode.Write)) {
var writer = new BinaryWriter(csStream);
Update(writer, blockInfo, block);
}
emptyHash = outStream.Length == 0;
}
if (!emptyHash)
blockInfo.Hash = hasher.Hash;
}
}
void Update(BinaryWriter writer, BlockInfo blockInfo, Block block) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
switch (instr.OpCode.Code) {
case Code.Beq_S:
case Code.Bge_S:
case Code.Bgt_S:
case Code.Ble_S:
case Code.Blt_S:
case Code.Bne_Un_S:
case Code.Bge_Un_S:
case Code.Bgt_Un_S:
case Code.Ble_Un_S:
case Code.Blt_Un_S:
case Code.Brfalse_S:
case Code.Brtrue_S:
case Code.Leave_S:
case Code.Beq:
case Code.Bge:
case Code.Bgt:
case Code.Ble:
case Code.Blt:
case Code.Bne_Un:
case Code.Bge_Un:
case Code.Bgt_Un:
case Code.Ble_Un:
case Code.Blt_Un:
case Code.Brfalse:
case Code.Brtrue:
case Code.Leave:
writer.Write((ushort)SimplifyBranch(instr.OpCode.Code));
break;
case Code.Switch:
writer.Write((ushort)instr.OpCode.Code);
writer.Write(blockInfo.Targets.Count);
break;
case Code.Br_S:
case Code.Br:
break;
case Code.Ret:
break;
case Code.Ldc_I4_M1:
case Code.Ldc_I4_0:
case Code.Ldc_I4_1:
case Code.Ldc_I4_2:
case Code.Ldc_I4_3:
case Code.Ldc_I4_4:
case Code.Ldc_I4_5:
case Code.Ldc_I4_6:
case Code.Ldc_I4_7:
case Code.Ldc_I4_8:
case Code.Ldc_I4:
case Code.Ldc_I4_S:
writer.Write((ushort)Code.Ldc_I4);
writer.Write(instr.GetLdcI4Value());
break;
case Code.Ldc_I8:
writer.Write((ushort)instr.OpCode.Code);
writer.Write((long)instr.Operand);
break;
case Code.Ldc_R4:
writer.Write((ushort)instr.OpCode.Code);
writer.Write((float)instr.Operand);
break;
case Code.Ldc_R8:
writer.Write((ushort)instr.OpCode.Code);
writer.Write((double)instr.Operand);
break;
case Code.Ldfld:
var typeField = instr.Operand as FieldDef;
bool isField = IsTypeField(typeField);
writer.Write((ushort)instr.OpCode.Code);
writer.Write(isField);
if (isField) {
if (i + 1 < instrs.Count && instrs[i + 1].IsLdcI4())
i++;
writer.Write(GetFieldId(typeField));
}
else
Write(writer, instr.Operand);
break;
case Code.Call:
case Code.Callvirt:
case Code.Newobj:
case Code.Jmp:
case Code.Ldftn:
case Code.Ldvirtftn:
case Code.Ldtoken:
case Code.Stfld:
case Code.Ldsfld:
case Code.Stsfld:
case Code.Ldflda:
case Code.Ldsflda:
case Code.Cpobj:
case Code.Ldobj:
case Code.Castclass:
case Code.Isinst:
case Code.Unbox:
case Code.Stobj:
case Code.Box:
case Code.Newarr:
case Code.Ldelema:
case Code.Ldelem:
case Code.Stelem:
case Code.Unbox_Any:
case Code.Refanyval:
case Code.Mkrefany:
case Code.Initobj:
case Code.Constrained:
case Code.Sizeof:
writer.Write((ushort)instr.OpCode.Code);
Write(writer, instr.Operand);
break;
case Code.Ldstr:
writer.Write((ushort)instr.OpCode.Code);
break;
case Code.Ldarg:
case Code.Ldarg_S:
case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
writer.Write((ushort)Code.Ldarg);
writer.Write(instr.Instruction.GetParameterIndex());
break;
case Code.Ldarga:
case Code.Ldarga_S:
writer.Write((ushort)Code.Ldarga);
writer.Write(instr.Instruction.GetParameterIndex());
break;
case Code.Starg:
case Code.Starg_S:
writer.Write((ushort)Code.Starg);
writer.Write(instr.Instruction.GetParameterIndex());
break;
case Code.Ldloc:
case Code.Ldloc_S:
case Code.Ldloc_0:
case Code.Ldloc_1:
case Code.Ldloc_2:
case Code.Ldloc_3:
writer.Write((ushort)Code.Ldloc);
break;
case Code.Ldloca:
case Code.Ldloca_S:
writer.Write((ushort)Code.Ldloca);
break;
case Code.Stloc:
case Code.Stloc_S:
case Code.Stloc_0:
case Code.Stloc_1:
case Code.Stloc_2:
case Code.Stloc_3:
writer.Write((ushort)Code.Stloc);
break;
case Code.Ldnull:
case Code.Throw:
case Code.Rethrow:
case Code.Ldlen:
case Code.Ckfinite:
case Code.Arglist:
case Code.Localloc:
case Code.Volatile:
case Code.Tailcall:
case Code.Cpblk:
case Code.Initblk:
case Code.Refanytype:
case Code.Readonly:
case Code.Break:
case Code.Endfinally:
case Code.Endfilter:
writer.Write((ushort)instr.OpCode.Code);
break;
case Code.Calli:
writer.Write((ushort)instr.OpCode.Code);
Write(writer, instr.Operand);
break;
case Code.Unaligned:
writer.Write((ushort)instr.OpCode.Code);
writer.Write((byte)instr.Operand);
break;
default:
break;
}
}
}
void Write(BinaryWriter writer, object op) {
var fd = op as FieldDef;
if (fd != null) {
Write(writer, fd);
return;
}
var mr = op as MemberRef;
if (mr != null) {
Write(writer, mr);
return;
}
var md = op as MethodDef;
if (md != null) {
Write(writer, md);
return;
}
var ms = op as MethodSpec;
if (ms != null) {
Write(writer, ms);
return;
}
var td = op as TypeDef;
if (td != null) {
Write(writer, td);
return;
}
var tr = op as TypeRef;
if (tr != null) {
Write(writer, tr);
return;
}
var ts = op as TypeSpec;
if (ts != null) {
Write(writer, ts);
return;
}
var fsig = op as FieldSig;
if (fsig != null) {
Write(writer, fsig);
return;
}
var msig = op as MethodSig;
if (msig != null) {
Write(writer, msig);
return;
}
var gsig = op as GenericInstMethodSig;
if (gsig != null) {
Write(writer, gsig);
return;
}
var asmRef = op as AssemblyRef;
if (asmRef != null) {
Write(writer, asmRef);
return;
}
writer.Write((byte)ObjectType.Unknown);
}
enum ObjectType : byte {
// 00..3F = Table.XXX values.
Unknown = 0x40,
TypeSig = 0x41,
FieldSig = 0x42,
MethodSig = 0x43,
GenericInstMethodSig = 0x44,
}
void Write(BinaryWriter writer, TypeSig sig) {
Write(writer, sig, 0);
}
void Write(BinaryWriter writer, TypeSig sig, int level) {
if (level++ > 20)
return;
writer.Write((byte)ObjectType.TypeSig);
var etype = sig.GetElementType();
writer.Write((byte)etype);
switch (etype) {
case ElementType.Ptr:
case ElementType.ByRef:
case ElementType.SZArray:
case ElementType.Pinned:
Write(writer, sig.Next, level);
break;
case ElementType.Array:
var arySig = (ArraySig)sig;
writer.Write(arySig.Rank);
writer.Write(arySig.Sizes.Count);
writer.Write(arySig.LowerBounds.Count);
Write(writer, sig.Next, level);
break;
case ElementType.CModReqd:
case ElementType.CModOpt:
Write(writer, ((ModifierSig)sig).Modifier);
Write(writer, sig.Next, level);
break;
case ElementType.ValueArray:
writer.Write(((ValueArraySig)sig).Size);
Write(writer, sig.Next, level);
break;
case ElementType.Module:
writer.Write(((ModuleSig)sig).Index);
Write(writer, sig.Next, level);
break;
case ElementType.GenericInst:
var gis = (GenericInstSig)sig;
Write(writer, gis.GenericType, level);
foreach (var ga in gis.GenericArguments)
Write(writer, ga, level);
Write(writer, sig.Next, level);
break;
case ElementType.FnPtr:
Write(writer, ((FnPtrSig)sig).Signature);
break;
case ElementType.Var:
case ElementType.MVar:
writer.Write(((GenericSig)sig).Number);
break;
case ElementType.ValueType:
case ElementType.Class:
Write(writer, ((TypeDefOrRefSig)sig).TypeDefOrRef);
break;
case ElementType.End:
case ElementType.Void:
case ElementType.Boolean:
case ElementType.Char:
case ElementType.I1:
case ElementType.U1:
case ElementType.I2:
case ElementType.U2:
case ElementType.I4:
case ElementType.U4:
case ElementType.I8:
case ElementType.U8:
case ElementType.R4:
case ElementType.R8:
case ElementType.String:
case ElementType.TypedByRef:
case ElementType.I:
case ElementType.U:
case ElementType.R:
case ElementType.Object:
case ElementType.Internal:
case ElementType.Sentinel:
default:
break;
}
}
void Write(BinaryWriter writer, FieldSig sig) {
writer.Write((byte)ObjectType.FieldSig);
writer.Write((byte)(sig == null ? 0 : sig.GetCallingConvention()));
Write(writer, sig.GetFieldType());
}
void Write(BinaryWriter writer, MethodSig sig) {
writer.Write((byte)ObjectType.MethodSig);
writer.Write((byte)(sig == null ? 0 : sig.GetCallingConvention()));
Write(writer, sig.GetRetType());
foreach (var p in sig.GetParams())
Write(writer, p);
writer.Write(sig.GetParamCount());
bool hasParamsAfterSentinel = sig.GetParamsAfterSentinel() != null;
writer.Write(hasParamsAfterSentinel);
if (hasParamsAfterSentinel) {
foreach (var p in sig.GetParamsAfterSentinel())
Write(writer, p);
}
}
void Write(BinaryWriter writer, GenericInstMethodSig sig) {
writer.Write((byte)ObjectType.GenericInstMethodSig);
writer.Write((byte)(sig == null ? 0 : sig.GetCallingConvention()));
foreach (var ga in sig.GetGenericArguments())
Write(writer, ga);
}
void Write(BinaryWriter writer, FieldDef fd) {
writer.Write((byte)Table.Field);
Write(writer, fd.DeclaringType);
var attrMask = FieldAttributes.Static | FieldAttributes.InitOnly |
FieldAttributes.Literal | FieldAttributes.SpecialName |
FieldAttributes.PinvokeImpl | FieldAttributes.RTSpecialName;
writer.Write((ushort)(fd == null ? 0 : fd.Attributes & attrMask));
Write(writer, fd == null ? null : fd.Signature);
}
void Write(BinaryWriter writer, MemberRef mr) {
writer.Write((byte)Table.MemberRef);
var parent = mr == null ? null : mr.Class;
Write(writer, parent);
bool canWriteName = IsFromNonObfuscatedAssembly(parent);
writer.Write(canWriteName);
if (canWriteName)
writer.Write(mr.Name);
Write(writer, mr == null ? null : mr.Signature);
}
void Write(BinaryWriter writer, MethodDef md) {
writer.Write((byte)Table.Method);
Write(writer, md.DeclaringType);
var attrMask1 = MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask |
MethodImplAttributes.ForwardRef | MethodImplAttributes.PreserveSig |
MethodImplAttributes.InternalCall;
writer.Write((ushort)(md == null ? 0 : md.ImplAttributes & attrMask1));
var attrMask2 = MethodAttributes.Static | MethodAttributes.Virtual |
MethodAttributes.HideBySig | MethodAttributes.VtableLayoutMask |
MethodAttributes.CheckAccessOnOverride | MethodAttributes.Abstract |
MethodAttributes.SpecialName | MethodAttributes.PinvokeImpl |
MethodAttributes.UnmanagedExport | MethodAttributes.RTSpecialName;
writer.Write((ushort)(md == null ? 0 : md.Attributes & attrMask2));
Write(writer, md == null ? null : md.Signature);
writer.Write(md == null ? 0 : md.ParamDefs.Count);
writer.Write(md == null ? 0 : md.GenericParameters.Count);
writer.Write(md == null ? false : md.HasImplMap);
writer.Write(GetId(md));
}
void Write(BinaryWriter writer, MethodSpec ms) {
writer.Write((byte)Table.MethodSpec);
Write(writer, ms == null ? null : ms.Method);
Write(writer, ms == null ? null : ms.Instantiation);
}
void Write(BinaryWriter writer, TypeDef td) {
writer.Write((byte)Table.TypeDef);
Write(writer, td == null ? null : td.BaseType);
var attrMask = TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask |
TypeAttributes.Abstract | TypeAttributes.SpecialName |
TypeAttributes.Import | TypeAttributes.WindowsRuntime |
TypeAttributes.StringFormatMask | TypeAttributes.RTSpecialName;
writer.Write((uint)(td == null ? 0 : td.Attributes & attrMask));
Write(writer, td == null ? null : td.BaseType);
writer.Write(td == null ? 0 : td.GenericParameters.Count);
writer.Write(td == null ? 0 : td.Interfaces.Count);
if (td != null) {
foreach (var iface in td.Interfaces)
Write(writer, iface);
}
writer.Write(GetId(td));
}
void Write(BinaryWriter writer, TypeRef tr) {
writer.Write((byte)Table.TypeRef);
Write(writer, tr == null ? null : tr.ResolutionScope);
bool canWriteName = IsFromNonObfuscatedAssembly(tr);
writer.Write(canWriteName);
if (canWriteName) {
writer.Write(tr.Namespace);
writer.Write(tr.Name);
}
}
void Write(BinaryWriter writer, TypeSpec ts) {
writer.Write((byte)Table.TypeSpec);
Write(writer, ts == null ? null : ts.TypeSig);
}
void Write(BinaryWriter writer, AssemblyRef asmRef) {
writer.Write((byte)Table.AssemblyRef);
bool canWriteAsm = IsNonObfuscatedAssembly(asmRef);
writer.Write(canWriteAsm);
if (canWriteAsm) {
bool hasPk = !PublicKeyBase.IsNullOrEmpty2(asmRef.PublicKeyOrToken);
writer.Write(hasPk);
if (hasPk)
writer.Write(PublicKeyBase.ToPublicKeyToken(asmRef.PublicKeyOrToken).Data);
writer.Write(asmRef.Name);
writer.Write(asmRef.Culture);
}
}
static bool IsFromNonObfuscatedAssembly(IMemberRefParent mrp) {
return IsFromNonObfuscatedAssembly(mrp as TypeRef);
}
static bool IsFromNonObfuscatedAssembly(TypeRef tr) {
if (tr == null)
return false;
for (int i = 0; i < 100; i++) {
var asmRef = tr.ResolutionScope as AssemblyRef;
if (asmRef != null)
return IsNonObfuscatedAssembly(asmRef);
var tr2 = tr.ResolutionScope as TypeRef;
if (tr2 != null) {
tr = tr2;
continue;
}
break;
}
return false;
}
static bool IsNonObfuscatedAssembly(AssemblyRef asmRef) {
if (asmRef == null)
return false;
// The only external asm refs it uses...
if (asmRef.Name != "mscorlib" && asmRef.Name != "System")
return false;
return true;
}
bool IsTypeField(FieldDef fd) {
return fd != null && fd.DeclaringType == blocks.Method.DeclaringType;
}
int GetFieldId(FieldDef fd) {
if (fd == null)
return int.MinValue;
var fieldType = fd.FieldSig.GetFieldType();
if (fieldType == null)
return int.MinValue + 1;
int result = 0;
for (int i = 0; i < 100; i++) {
result += (int)fieldType.ElementType;
if (fieldType.Next == null)
break;
result += 0x100;
fieldType = fieldType.Next;
}
var td = fieldType.TryGetTypeDef();
if (td != null && td.IsEnum)
return result + 0x10000000;
return result;
}
static Code SimplifyBranch(Code code) {
switch (code) {
case Code.Beq_S: return Code.Beq;
case Code.Bge_S: return Code.Bge;
case Code.Bgt_S: return Code.Bgt;
case Code.Ble_S: return Code.Ble;
case Code.Blt_S: return Code.Blt;
case Code.Bne_Un_S: return Code.Bne_Un;
case Code.Bge_Un_S: return Code.Bge_Un;
case Code.Bgt_Un_S: return Code.Bgt_Un;
case Code.Ble_Un_S: return Code.Ble_Un;
case Code.Blt_Un_S: return Code.Blt_Un;
case Code.Br_S: return Code.Br;
case Code.Brfalse_S: return Code.Brfalse;
case Code.Brtrue_S: return Code.Brtrue;
case Code.Leave_S: return Code.Leave;
default: return code;
}
}
}
}

View File

@ -0,0 +1,38 @@
/*
Copyright (C) 2011-2013 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using dnlib.DotNet;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
class OpCodeHandler {
public OpCodeHandlerInfo OpCodeHandlerInfo { get; private set; }
public TypeDef HandlerType { get; private set; }
public HandlerMethod ExecMethod { get; private set; }
public OpCodeHandler(OpCodeHandlerInfo opCodeHandlerInfo, TypeDef handlerType, HandlerMethod execMethod) {
this.OpCodeHandlerInfo = opCodeHandlerInfo;
this.HandlerType = handlerType;
this.ExecMethod = execMethod;
}
public override string ToString() {
return OpCodeHandlerInfo.Name;
}
}
}

View File

@ -0,0 +1,123 @@
/*
Copyright (C) 2011-2013 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
class OpCodeHandlerInfo {
public HandlerTypeCode TypeCode { get; private set; }
public string Name { get; private set; }
public MethodSigInfo ExecSig { get; private set; }
public OpCodeHandlerInfo(HandlerTypeCode typeCode, MethodSigInfo execSig) {
this.TypeCode = typeCode;
this.Name = GetHandlerName(typeCode);
this.ExecSig = execSig;
}
public override string ToString() {
return Name;
}
static string GetHandlerName(HandlerTypeCode code) {
switch (code) {
case HandlerTypeCode.Add: return "add";
case HandlerTypeCode.Add_Ovf: return "add.ovf";
case HandlerTypeCode.Add_Ovf_Un: return "add.ovf.un";
case HandlerTypeCode.And: return "and";
case HandlerTypeCode.Beq: return "beq";
case HandlerTypeCode.Bge: return "bge";
case HandlerTypeCode.Bge_Un: return "bge.un";
case HandlerTypeCode.Bgt: return "bgt";
case HandlerTypeCode.Bgt_Un: return "bgt.un";
case HandlerTypeCode.Ble: return "ble";
case HandlerTypeCode.Ble_Un: return "ble.un";
case HandlerTypeCode.Blt: return "blt";
case HandlerTypeCode.Blt_Un: return "blt.un";
case HandlerTypeCode.Bne_Un: return "bne.un";
case HandlerTypeCode.Box: return "box";
case HandlerTypeCode.Br: return "br";
case HandlerTypeCode.Brfalse: return "brfalse";
case HandlerTypeCode.Brtrue: return "brtrue";
case HandlerTypeCode.Call: return "call";
case HandlerTypeCode.Callvirt: return "callvirt";
case HandlerTypeCode.Castclass: return "castclass";
case HandlerTypeCode.Ceq: return "ceq";
case HandlerTypeCode.Cgt: return "cgt";
case HandlerTypeCode.Cgt_Un: return "cgt.un";
case HandlerTypeCode.Clt: return "clt";
case HandlerTypeCode.Clt_Un: return "clt.un";
case HandlerTypeCode.Conv: return "conv";
case HandlerTypeCode.Div: return "div";
case HandlerTypeCode.Div_Un: return "div.un";
case HandlerTypeCode.Dup: return "dup";
case HandlerTypeCode.Endfinally: return "endfinally";
case HandlerTypeCode.Initobj: return "initobj";
case HandlerTypeCode.Isinst: return "isinst";
case HandlerTypeCode.Ldarg: return "ldarg";
case HandlerTypeCode.Ldarga: return "ldarga";
case HandlerTypeCode.Ldc: return "ldc";
case HandlerTypeCode.Ldelem: return "ldelem";
case HandlerTypeCode.Ldelema: return "ldelema";
case HandlerTypeCode.Ldfld_Ldsfld: return "ldfld/ldsfld";
case HandlerTypeCode.Ldflda_Ldsflda:return "ldflda/ldsflda";
case HandlerTypeCode.Ldftn: return "ldftn";
case HandlerTypeCode.Ldlen: return "ldlen";
case HandlerTypeCode.Ldloc: return "ldloc";
case HandlerTypeCode.Ldloca: return "ldloca";
case HandlerTypeCode.Ldobj: return "ldobj";
case HandlerTypeCode.Ldstr: return "ldstr";
case HandlerTypeCode.Ldtoken: return "ldtoken";
case HandlerTypeCode.Ldvirtftn: return "ldvirtftn";
case HandlerTypeCode.Leave: return "leave";
case HandlerTypeCode.Mul: return "mul";
case HandlerTypeCode.Mul_Ovf: return "mul.ovf";
case HandlerTypeCode.Mul_Ovf_Un: return "mul.ovf.un";
case HandlerTypeCode.Neg: return "neg";
case HandlerTypeCode.Newarr: return "newarr";
case HandlerTypeCode.Newobj: return "newobj";
case HandlerTypeCode.Nop: return "nop";
case HandlerTypeCode.Not: return "not";
case HandlerTypeCode.Or: return "or";
case HandlerTypeCode.Pop: return "pop";
case HandlerTypeCode.Rem: return "rem";
case HandlerTypeCode.Rem_Un: return "rem.un";
case HandlerTypeCode.Ret: return "ret";
case HandlerTypeCode.Rethrow: return "rethrow";
case HandlerTypeCode.Shl: return "shl";
case HandlerTypeCode.Shr: return "shr";
case HandlerTypeCode.Shr_Un: return "shr.un";
case HandlerTypeCode.Starg: return "starg";
case HandlerTypeCode.Stelem: return "stelem";
case HandlerTypeCode.Stfld_Stsfld: return "stfld/stsfld";
case HandlerTypeCode.Stloc: return "stloc";
case HandlerTypeCode.Stobj: return "stobj";
case HandlerTypeCode.Sub: return "sub";
case HandlerTypeCode.Sub_Ovf: return "sub.ovf";
case HandlerTypeCode.Sub_Ovf_Un: return "sub.ovf.un";
case HandlerTypeCode.Switch: return "switch";
case HandlerTypeCode.Throw: return "throw";
case HandlerTypeCode.Unbox_Any: return "unbox.any";
case HandlerTypeCode.Xor: return "xor";
default: throw new ApplicationException("Invalid handler type code");
}
}
}
}

View File

@ -0,0 +1,528 @@
/*
Copyright (C) 2011-2013 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;
using dnlib.DotNet.Emit;
using dnlib.DotNet;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
class OpCodeHandlerInfoReader {
IInstructionOperandResolver resolver;
Dictionary<HandlerTypeCode, Func<BinaryReader, Instruction>> readHandlers;
public OpCodeHandlerInfoReader(IInstructionOperandResolver resolver) {
this.resolver = resolver;
this.readHandlers = new Dictionary<HandlerTypeCode, Func<BinaryReader, Instruction>> {
{ HandlerTypeCode.Add, Handler_Add },
{ HandlerTypeCode.Add_Ovf, Handler_Add_Ovf },
{ HandlerTypeCode.Add_Ovf_Un, Handler_Add_Ovf_Un },
{ HandlerTypeCode.And, Handler_And },
{ HandlerTypeCode.Beq, Handler_Beq },
{ HandlerTypeCode.Bge, Handler_Bge },
{ HandlerTypeCode.Bge_Un, Handler_Bge_Un },
{ HandlerTypeCode.Bgt, Handler_Bgt },
{ HandlerTypeCode.Bgt_Un, Handler_Bgt_Un },
{ HandlerTypeCode.Ble, Handler_Ble },
{ HandlerTypeCode.Ble_Un, Handler_Ble_Un },
{ HandlerTypeCode.Blt, Handler_Blt },
{ HandlerTypeCode.Blt_Un, Handler_Blt_Un },
{ HandlerTypeCode.Bne_Un, Handler_Bne_Un },
{ HandlerTypeCode.Box, Handler_Box },
{ HandlerTypeCode.Br, Handler_Br },
{ HandlerTypeCode.Brfalse, Handler_Brfalse },
{ HandlerTypeCode.Brtrue, Handler_Brtrue },
{ HandlerTypeCode.Call, Handler_Call },
{ HandlerTypeCode.Callvirt, Handler_Callvirt },
{ HandlerTypeCode.Castclass, Handler_Castclass },
{ HandlerTypeCode.Ceq, Handler_Ceq },
{ HandlerTypeCode.Cgt, Handler_Cgt },
{ HandlerTypeCode.Cgt_Un, Handler_Cgt_Un },
{ HandlerTypeCode.Clt, Handler_Clt },
{ HandlerTypeCode.Clt_Un, Handler_Clt_Un },
{ HandlerTypeCode.Conv, Handler_Conv },
{ HandlerTypeCode.Div, Handler_Div },
{ HandlerTypeCode.Div_Un, Handler_Div_Un },
{ HandlerTypeCode.Dup, Handler_Dup },
{ HandlerTypeCode.Endfinally, Handler_Endfinally },
{ HandlerTypeCode.Initobj, Handler_Initobj },
{ HandlerTypeCode.Isinst, Handler_Isinst },
{ HandlerTypeCode.Ldarg, Handler_Ldarg },
{ HandlerTypeCode.Ldarga, Handler_Ldarga },
{ HandlerTypeCode.Ldc, Handler_Ldc },
{ HandlerTypeCode.Ldelem, Handler_Ldelem },
{ HandlerTypeCode.Ldelema, Handler_Ldelema },
{ HandlerTypeCode.Ldfld_Ldsfld, Handler_Ldfld_Ldsfld },
{ HandlerTypeCode.Ldflda_Ldsflda, Handler_Ldflda_Ldsflda },
{ HandlerTypeCode.Ldftn, Handler_Ldftn },
{ HandlerTypeCode.Ldlen, Handler_Ldlen },
{ HandlerTypeCode.Ldloc, Handler_Ldloc },
{ HandlerTypeCode.Ldloca, Handler_Ldloca },
{ HandlerTypeCode.Ldobj, Handler_Ldobj },
{ HandlerTypeCode.Ldstr, Handler_Ldstr },
{ HandlerTypeCode.Ldtoken, Handler_Ldtoken },
{ HandlerTypeCode.Ldvirtftn, Handler_Ldvirtftn },
{ HandlerTypeCode.Leave, Handler_Leave },
{ HandlerTypeCode.Mul, Handler_Mul },
{ HandlerTypeCode.Mul_Ovf, Handler_Mul_Ovf },
{ HandlerTypeCode.Mul_Ovf_Un, Handler_Mul_Ovf_Un },
{ HandlerTypeCode.Neg, Handler_Neg },
{ HandlerTypeCode.Newarr, Handler_Newarr },
{ HandlerTypeCode.Newobj, Handler_Newobj },
{ HandlerTypeCode.Nop, Handler_Nop },
{ HandlerTypeCode.Not, Handler_Not },
{ HandlerTypeCode.Or, Handler_Or },
{ HandlerTypeCode.Pop, Handler_Pop },
{ HandlerTypeCode.Rem, Handler_Rem },
{ HandlerTypeCode.Rem_Un, Handler_Rem_Un },
{ HandlerTypeCode.Ret, Handler_Ret },
{ HandlerTypeCode.Rethrow, Handler_Rethrow },
{ HandlerTypeCode.Shl, Handler_Shl },
{ HandlerTypeCode.Shr, Handler_Shr },
{ HandlerTypeCode.Shr_Un, Handler_Shr_Un },
{ HandlerTypeCode.Starg, Handler_Starg },
{ HandlerTypeCode.Stelem, Handler_Stelem },
{ HandlerTypeCode.Stfld_Stsfld, Handler_Stfld_Stsfld },
{ HandlerTypeCode.Stloc, Handler_Stloc },
{ HandlerTypeCode.Stobj, Handler_Stobj },
{ HandlerTypeCode.Sub, Handler_Sub },
{ HandlerTypeCode.Sub_Ovf, Handler_Sub_Ovf },
{ HandlerTypeCode.Sub_Ovf_Un, Handler_Sub_Ovf_Un },
{ HandlerTypeCode.Switch, Handler_Switch },
{ HandlerTypeCode.Throw, Handler_Throw },
{ HandlerTypeCode.Unbox_Any, Handler_Unbox_Any },
{ HandlerTypeCode.Xor, Handler_Xor },
};
}
public Instruction Read(HandlerTypeCode typeCode, BinaryReader reader) {
Func<BinaryReader, Instruction> readHandler;
if (!readHandlers.TryGetValue(typeCode, out readHandler))
throw new ApplicationException("Invalid handler type");
return readHandler(reader);
}
Instruction Handler_Add(BinaryReader reader) {
return OpCodes.Add.ToInstruction();
}
Instruction Handler_Add_Ovf(BinaryReader reader) {
return OpCodes.Add_Ovf.ToInstruction();
}
Instruction Handler_Add_Ovf_Un(BinaryReader reader) {
return OpCodes.Add_Ovf_Un.ToInstruction();
}
Instruction Handler_And(BinaryReader reader) {
return OpCodes.And.ToInstruction();
}
Instruction Handler_Beq(BinaryReader reader) {
return new Instruction(OpCodes.Beq, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Bge(BinaryReader reader) {
return new Instruction(OpCodes.Bge, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Bge_Un(BinaryReader reader) {
return new Instruction(OpCodes.Bge_Un, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Bgt(BinaryReader reader) {
return new Instruction(OpCodes.Bgt, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Bgt_Un(BinaryReader reader) {
return new Instruction(OpCodes.Bgt_Un, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Ble(BinaryReader reader) {
return new Instruction(OpCodes.Ble, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Ble_Un(BinaryReader reader) {
return new Instruction(OpCodes.Ble_Un, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Blt(BinaryReader reader) {
return new Instruction(OpCodes.Blt, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Blt_Un(BinaryReader reader) {
return new Instruction(OpCodes.Blt_Un, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Bne_Un(BinaryReader reader) {
return new Instruction(OpCodes.Bne_Un, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Box(BinaryReader reader) {
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
return OpCodes.Box.ToInstruction(type);
}
Instruction Handler_Br(BinaryReader reader) {
return new Instruction(OpCodes.Br, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Brfalse(BinaryReader reader) {
return new Instruction(OpCodes.Brfalse, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Brtrue(BinaryReader reader) {
return new Instruction(OpCodes.Brtrue, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Call(BinaryReader reader) {
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
return OpCodes.Call.ToInstruction(method);
}
Instruction Handler_Callvirt(BinaryReader reader) {
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
return OpCodes.Callvirt.ToInstruction(method);
}
Instruction Handler_Castclass(BinaryReader reader) {
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
return OpCodes.Castclass.ToInstruction(type);
}
Instruction Handler_Ceq(BinaryReader reader) {
return OpCodes.Ceq.ToInstruction();
}
Instruction Handler_Cgt(BinaryReader reader) {
return OpCodes.Cgt.ToInstruction();
}
Instruction Handler_Cgt_Un(BinaryReader reader) {
return OpCodes.Cgt_Un.ToInstruction();
}
Instruction Handler_Clt(BinaryReader reader) {
return OpCodes.Clt.ToInstruction();
}
Instruction Handler_Clt_Un(BinaryReader reader) {
return OpCodes.Clt_Un.ToInstruction();
}
class ConvInfo {
public byte Type { get; private set; }
public bool Second { get; private set; }
public bool Third { get; private set; }
public OpCode OpCode { get; private set; }
public ConvInfo(byte type, bool second, bool third, OpCode opCode) {
this.Type = type;
this.Second = second;
this.Third = third;
this.OpCode = opCode;
}
}
readonly static List<ConvInfo> instructionInfos1 = new List<ConvInfo> {
new ConvInfo(0, false, false, OpCodes.Conv_I1),
new ConvInfo(1, false, false, OpCodes.Conv_I2),
new ConvInfo(2, false, false, OpCodes.Conv_I4),
new ConvInfo(3, false, false, OpCodes.Conv_I8),
new ConvInfo(4, false, false, OpCodes.Conv_R4),
new ConvInfo(5, false, false, OpCodes.Conv_R8),
new ConvInfo(6, false, false, OpCodes.Conv_U1),
new ConvInfo(7, false, false, OpCodes.Conv_U2),
new ConvInfo(8, false, false, OpCodes.Conv_U4),
new ConvInfo(9, false, false, OpCodes.Conv_U8),
new ConvInfo(10, false, false, OpCodes.Conv_I),
new ConvInfo(11, false, false, OpCodes.Conv_U),
new ConvInfo(0, true, false, OpCodes.Conv_Ovf_I1),
new ConvInfo(1, true, false, OpCodes.Conv_Ovf_I2),
new ConvInfo(2, true, false, OpCodes.Conv_Ovf_I4),
new ConvInfo(3, true, false, OpCodes.Conv_Ovf_I8),
new ConvInfo(6, true, false, OpCodes.Conv_Ovf_U1),
new ConvInfo(7, true, false, OpCodes.Conv_Ovf_U2),
new ConvInfo(8, true, false, OpCodes.Conv_Ovf_U4),
new ConvInfo(9, true, false, OpCodes.Conv_Ovf_U8),
new ConvInfo(10, true, false, OpCodes.Conv_Ovf_I),
new ConvInfo(11, true, false, OpCodes.Conv_Ovf_U),
new ConvInfo(0, true, true, OpCodes.Conv_Ovf_I1_Un),
new ConvInfo(1, true, true, OpCodes.Conv_Ovf_I2_Un),
new ConvInfo(2, true, true, OpCodes.Conv_Ovf_I4_Un),
new ConvInfo(3, true, true, OpCodes.Conv_Ovf_I8_Un),
new ConvInfo(6, true, true, OpCodes.Conv_Ovf_U1_Un),
new ConvInfo(7, true, true, OpCodes.Conv_Ovf_U2_Un),
new ConvInfo(8, true, true, OpCodes.Conv_Ovf_U4_Un),
new ConvInfo(9, true, true, OpCodes.Conv_Ovf_U8_Un),
new ConvInfo(10, true, true, OpCodes.Conv_Ovf_I_Un),
new ConvInfo(11, true, true, OpCodes.Conv_Ovf_U_Un),
new ConvInfo(12, true, true, OpCodes.Conv_R_Un),
};
Instruction Handler_Conv(BinaryReader reader) {
byte type = reader.ReadByte();
bool second = reader.ReadBoolean();
bool third = reader.ReadBoolean();
Instruction instr = null;
foreach (var info in instructionInfos1) {
if (type != info.Type || info.Second != second || info.Third != third)
continue;
instr = new Instruction { OpCode = info.OpCode };
break;
}
if (instr == null)
throw new ApplicationException("Invalid opcode");
return instr;
}
Instruction Handler_Div(BinaryReader reader) {
return OpCodes.Div.ToInstruction();
}
Instruction Handler_Div_Un(BinaryReader reader) {
return OpCodes.Div_Un.ToInstruction();
}
Instruction Handler_Dup(BinaryReader reader) {
return OpCodes.Dup.ToInstruction();
}
Instruction Handler_Endfinally(BinaryReader reader) {
return OpCodes.Endfinally.ToInstruction();
}
Instruction Handler_Initobj(BinaryReader reader) {
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
return OpCodes.Initobj.ToInstruction(type);
}
Instruction Handler_Isinst(BinaryReader reader) {
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
return OpCodes.Isinst.ToInstruction(type);
}
Instruction Handler_Ldarg(BinaryReader reader) {
return new Instruction(OpCodes.Ldarg, new ArgOperand(reader.ReadUInt16()));
}
Instruction Handler_Ldarga(BinaryReader reader) {
return new Instruction(OpCodes.Ldarga, new ArgOperand(reader.ReadUInt16()));
}
Instruction Handler_Ldc(BinaryReader reader) {
switch ((ElementType)reader.ReadByte()) {
case ElementType.I4: return Instruction.CreateLdcI4(reader.ReadInt32());
case ElementType.I8: return OpCodes.Ldc_I8.ToInstruction(reader.ReadInt64());
case ElementType.R4: return OpCodes.Ldc_R4.ToInstruction(reader.ReadSingle());
case ElementType.R8: return OpCodes.Ldc_R8.ToInstruction(reader.ReadDouble());
case ElementType.Object: return OpCodes.Ldnull.ToInstruction();
default: throw new ApplicationException("Invalid instruction");
}
}
Instruction Handler_Ldelem(BinaryReader reader) {
return new Instruction(OpCodes.Ldelem, null);
}
Instruction Handler_Ldelema(BinaryReader reader) {
return new Instruction(OpCodes.Ldelema, null);
}
Instruction Handler_Ldfld_Ldsfld(BinaryReader reader) {
var field = resolver.ResolveToken(reader.ReadUInt32()) as IField;
return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsfld, OpCodes.Ldfld, field));
}
Instruction Handler_Ldflda_Ldsflda(BinaryReader reader) {
var field = resolver.ResolveToken(reader.ReadUInt32()) as IField;
return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsflda, OpCodes.Ldflda, field));
}
Instruction Handler_Ldftn(BinaryReader reader) {
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
return OpCodes.Ldftn.ToInstruction(method);
}
Instruction Handler_Ldlen(BinaryReader reader) {
return OpCodes.Ldlen.ToInstruction();
}
Instruction Handler_Ldloc(BinaryReader reader) {
return new Instruction(OpCodes.Ldloc, new LocalOperand(reader.ReadUInt16()));
}
Instruction Handler_Ldloca(BinaryReader reader) {
return new Instruction(OpCodes.Ldloca, new LocalOperand(reader.ReadUInt16()));
}
Instruction Handler_Ldobj(BinaryReader reader) {
return new Instruction(OpCodes.Ldobj, null);
}
Instruction Handler_Ldstr(BinaryReader reader) {
return OpCodes.Ldstr.ToInstruction(reader.ReadString());
}
Instruction Handler_Ldtoken(BinaryReader reader) {
var member = resolver.ResolveToken(reader.ReadUInt32()) as ITokenOperand;
return OpCodes.Ldtoken.ToInstruction(member);
}
Instruction Handler_Ldvirtftn(BinaryReader reader) {
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
reader.ReadUInt32();
return OpCodes.Ldvirtftn.ToInstruction(method);
}
Instruction Handler_Leave(BinaryReader reader) {
return new Instruction(OpCodes.Leave, new TargetDisplOperand(reader.ReadInt32()));
}
Instruction Handler_Mul(BinaryReader reader) {
return OpCodes.Mul.ToInstruction();
}
Instruction Handler_Mul_Ovf(BinaryReader reader) {
return OpCodes.Mul_Ovf.ToInstruction();
}
Instruction Handler_Mul_Ovf_Un(BinaryReader reader) {
return OpCodes.Mul_Ovf_Un.ToInstruction();
}
Instruction Handler_Neg(BinaryReader reader) {
return OpCodes.Neg.ToInstruction();
}
Instruction Handler_Newarr(BinaryReader reader) {
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
return OpCodes.Newarr.ToInstruction(type);
}
Instruction Handler_Newobj(BinaryReader reader) {
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
return OpCodes.Newobj.ToInstruction(method);
}
Instruction Handler_Nop(BinaryReader reader) {
return OpCodes.Nop.ToInstruction();
}
Instruction Handler_Not(BinaryReader reader) {
return OpCodes.Not.ToInstruction();
}
Instruction Handler_Or(BinaryReader reader) {
return OpCodes.Or.ToInstruction();
}
Instruction Handler_Pop(BinaryReader reader) {
return OpCodes.Pop.ToInstruction();
}
Instruction Handler_Rem(BinaryReader reader) {
return OpCodes.Rem.ToInstruction();
}
Instruction Handler_Rem_Un(BinaryReader reader) {
return OpCodes.Rem_Un.ToInstruction();
}
Instruction Handler_Ret(BinaryReader reader) {
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
return OpCodes.Ret.ToInstruction();
}
Instruction Handler_Rethrow(BinaryReader reader) {
return OpCodes.Rethrow.ToInstruction();
}
Instruction Handler_Shl(BinaryReader reader) {
return OpCodes.Shl.ToInstruction();
}
Instruction Handler_Shr(BinaryReader reader) {
return OpCodes.Shr.ToInstruction();
}
Instruction Handler_Shr_Un(BinaryReader reader) {
return OpCodes.Shr_Un.ToInstruction();
}
Instruction Handler_Starg(BinaryReader reader) {
return new Instruction(OpCodes.Starg, new ArgOperand(reader.ReadUInt16()));
}
Instruction Handler_Stelem(BinaryReader reader) {
return new Instruction(OpCodes.Stelem, null);
}
Instruction Handler_Stfld_Stsfld(BinaryReader reader) {
var field = resolver.ResolveToken(reader.ReadUInt32()) as IField;
return new Instruction(null, new FieldInstructionOperand(OpCodes.Stsfld, OpCodes.Stfld, field));
}
Instruction Handler_Stloc(BinaryReader reader) {
ushort loc = reader.ReadUInt16();
var etype = (ElementType)reader.ReadInt32();
return new Instruction(OpCodes.Stloc, new LocalOperand(loc));
}
Instruction Handler_Stobj(BinaryReader reader) {
return new Instruction(OpCodes.Stobj, null);
}
Instruction Handler_Sub(BinaryReader reader) {
return OpCodes.Sub.ToInstruction();
}
Instruction Handler_Sub_Ovf(BinaryReader reader) {
return OpCodes.Sub_Ovf.ToInstruction();
}
Instruction Handler_Sub_Ovf_Un(BinaryReader reader) {
return OpCodes.Sub_Ovf_Un.ToInstruction();
}
Instruction Handler_Switch(BinaryReader reader) {
int size = reader.ReadInt32();
var offsets = new int[size];
for (int i = 0; i < size; i++)
offsets[i] = reader.ReadInt32();
return new Instruction(OpCodes.Switch, new SwitchTargetDisplOperand(offsets));
}
Instruction Handler_Throw(BinaryReader reader) {
return OpCodes.Throw.ToInstruction();
}
Instruction Handler_Unbox_Any(BinaryReader reader) {
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
return OpCodes.Unbox_Any.ToInstruction(type);
}
Instruction Handler_Xor(BinaryReader reader) {
return OpCodes.Xor.ToInstruction();
}
}
}

View File

@ -0,0 +1,98 @@
/*
Copyright (C) 2011-2013 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
static class OpCodeHandlerInfos {
enum OpCodeHandlersFileVersion : int {
V1 = 1,
}
public static void Write(BinaryWriter writer, IList<OpCodeHandlerInfo> handlerInfos) {
WriteV1(writer, handlerInfos);
}
public static void WriteV1(BinaryWriter writer, IList<OpCodeHandlerInfo> handlerInfos) {
writer.Write((int)OpCodeHandlersFileVersion.V1);
writer.Write(handlerInfos.Count);
foreach (var handler in handlerInfos) {
writer.Write((int)handler.TypeCode);
var infos = handler.ExecSig.BlockInfos;
writer.Write(infos.Count);
foreach (var info in infos) {
if (info.Hash == null)
writer.Write(0);
else {
writer.Write(info.Hash.Length);
writer.Write(info.Hash);
}
writer.Write(info.Targets.Count);
foreach (var target in info.Targets)
writer.Write(target);
}
}
}
public static List<OpCodeHandlerInfo> Read(BinaryReader reader) {
switch ((OpCodeHandlersFileVersion)reader.ReadInt32()) {
case OpCodeHandlersFileVersion.V1: return ReadV1(reader);
default: throw new ApplicationException("Invalid file version");
}
}
static List<OpCodeHandlerInfo> ReadV1(BinaryReader reader) {
int numHandlers = reader.ReadInt32();
var list = new List<OpCodeHandlerInfo>(numHandlers);
for (int i = 0; i < numHandlers; i++) {
var typeCode = (HandlerTypeCode)reader.ReadInt32();
int numInfos = reader.ReadInt32();
var sigInfo = new MethodSigInfo();
for (int j = 0; j < numInfos; j++) {
var info = new BlockInfo();
info.Hash = reader.ReadBytes(reader.ReadInt32());
if (info.Hash.Length == 0)
info.Hash = null;
int numTargets = reader.ReadInt32();
for (int k = 0; k < numTargets; k++)
info.Targets.Add(reader.ReadInt32());
sigInfo.BlockInfos.Add(info);
}
list.Add(new OpCodeHandlerInfo(typeCode, sigInfo));
}
return list;
}
public static readonly IList<OpCodeHandlerInfo>[] HandlerInfos = new IList<OpCodeHandlerInfo>[] {
ReadOpCodeHandlerInfos(CsvmResources.CSVM1_v2),
ReadOpCodeHandlerInfos(CsvmResources.CSVM2_v2),
ReadOpCodeHandlerInfos(CsvmResources.CSVM3_v2),
};
static IList<OpCodeHandlerInfo> ReadOpCodeHandlerInfos(byte[] data) {
return OpCodeHandlerInfos.Read(new BinaryReader(new MemoryStream(data)));
}
}
}

View File

@ -0,0 +1,44 @@
/*
Copyright (C) 2011-2013 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.Text;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
class VmOpCode {
public List<OpCodeHandlerInfo> OpCodeHandlerInfos { get; private set; }
public VmOpCode(List<OpCodeHandlerInfo> opCodeHandlerInfos) {
this.OpCodeHandlerInfos = new List<OpCodeHandlerInfo>(opCodeHandlerInfos.Count);
this.OpCodeHandlerInfos.AddRange(opCodeHandlerInfos);
}
public override string ToString() {
if (OpCodeHandlerInfos.Count == 0)
return "<nothing>";
var sb = new StringBuilder();
foreach (var handler in OpCodeHandlerInfos) {
if (sb.Length != 0)
sb.Append(", ");
sb.Append(handler.Name);
}
return sb.ToString();
}
}
}

View File

@ -0,0 +1,297 @@
/*
Copyright (C) 2011-2013 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using de4dot.blocks;
using de4dot.blocks.cflow;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
class MyDeobfuscator {
CliSecureRtType cliSecureRtType;
StringDecrypter stringDecrypter;
StaticStringInliner staticStringInliner = new StaticStringInliner();
public MyDeobfuscator(ModuleDefMD module) {
cliSecureRtType = new CliSecureRtType(module);
cliSecureRtType.Find(null);
stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterMethod);
stringDecrypter.Find();
cliSecureRtType.FindStringDecrypterMethod();
stringDecrypter.Method = cliSecureRtType.StringDecrypterMethod;
staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0]));
}
void RestoreMethod(Blocks blocks) {
IList<Instruction> allInstructions;
IList<ExceptionHandler> allExceptionHandlers;
blocks.GetCode(out allInstructions, out allExceptionHandlers);
DotNetUtils.RestoreBody(blocks.Method, allInstructions, allExceptionHandlers);
}
public void DecryptStrings(MethodDef method) {
var blocks = new Blocks(method);
DecryptStrings(blocks);
RestoreMethod(blocks);
}
public void DecryptStrings(Blocks blocks) {
staticStringInliner.Decrypt(blocks);
}
public void Deobfuscate(MethodDef method) {
DecryptStrings(method);
}
}
class VmOpCodeHandlerDetector {
const int NUM_HANDLERS = 78;
ModuleDefMD module;
List<VmOpCode> vmOpCodes;
MyDeobfuscator deobfuscator;
public IList<VmOpCode> Handlers {
get { return vmOpCodes; }
}
public VmOpCodeHandlerDetector(ModuleDefMD module) {
this.module = module;
}
public void FindHandlers() {
if (vmOpCodes != null)
return;
deobfuscator = new MyDeobfuscator(module);
var csvmInfo = new CsvmInfo(module);
csvmInfo.Initialize();
var vmHandlerTypes = FindVmHandlerTypes();
if (vmHandlerTypes == null)
throw new ApplicationException("Could not find CSVM opcode handler types");
var composites = CreateCompositeOpCodeHandlers(csvmInfo, vmHandlerTypes);
foreach (var handlerInfos in OpCodeHandlerInfos.HandlerInfos) {
var otherHandlers = CreateOtherHandlers(csvmInfo, handlerInfos);
if (!DetectCompositeHandlers(composites, otherHandlers))
continue;
vmOpCodes = CreateVmOpCodes(composites);
break;
}
if (vmOpCodes == null)
throw new ApplicationException("Could not find any/all CSVM handlers");
}
static List<VmOpCode> CreateVmOpCodes(IList<CompositeOpCodeHandler> composites) {
var list = new List<VmOpCode>(composites.Count);
foreach (var composite in composites)
list.Add(new VmOpCode(composite.OpCodeHandlerInfos));
return list;
}
bool DetectCompositeHandlers(IEnumerable<CompositeOpCodeHandler> composites, List<OpCodeHandler> otherHandlers) {
var detector = new CompositeHandlerDetector(otherHandlers);
foreach (var composite in composites) {
if (!detector.FindHandlers(composite))
return false;
}
return true;
}
static MethodDef SimplifyInstructions(MethodDef method) {
if (method.Body == null)
return method;
method.Body.SimplifyMacros(method.Parameters);
return method;
}
List<CompositeOpCodeHandler> CreateCompositeOpCodeHandlers(CsvmInfo csvmInfo, List<TypeDef> handlers) {
var list = new List<CompositeOpCodeHandler>(handlers.Count);
foreach (var handler in handlers) {
var execHandler = new HandlerMethod(GetExecMethod(handler));
list.Add(new CompositeOpCodeHandler(handler, execHandler));
}
return list;
}
MethodDef GetExecMethod(TypeDef type) {
MethodDef readMethod, execMethod;
GetReadAndExecMethods(type, out readMethod, out execMethod);
deobfuscator.Deobfuscate(execMethod);
SimplifyInstructions(execMethod);
return execMethod;
}
static MethodSigInfoCreator CreateMethodSigInfoCreator(CsvmInfo csvmInfo) {
var creator = new MethodSigInfoCreator();
creator.AddId(csvmInfo.LogicalOpShrUn, 1);
creator.AddId(csvmInfo.LogicalOpShl, 2);
creator.AddId(csvmInfo.LogicalOpShr, 3);
creator.AddId(csvmInfo.LogicalOpAnd, 4);
creator.AddId(csvmInfo.LogicalOpXor, 5);
creator.AddId(csvmInfo.LogicalOpOr, 6);
creator.AddId(csvmInfo.CompareLt, 7);
creator.AddId(csvmInfo.CompareLte, 8);
creator.AddId(csvmInfo.CompareGt, 9);
creator.AddId(csvmInfo.CompareGte, 10);
creator.AddId(csvmInfo.CompareEq, 11);
creator.AddId(csvmInfo.CompareEqz, 12);
creator.AddId(csvmInfo.ArithmeticSubOvfUn, 13);
creator.AddId(csvmInfo.ArithmeticMulOvfUn, 14);
creator.AddId(csvmInfo.ArithmeticRemUn, 15);
creator.AddId(csvmInfo.ArithmeticRem, 16);
creator.AddId(csvmInfo.ArithmeticDivUn, 17);
creator.AddId(csvmInfo.ArithmeticDiv, 18);
creator.AddId(csvmInfo.ArithmeticMul, 19);
creator.AddId(csvmInfo.ArithmeticMulOvf, 20);
creator.AddId(csvmInfo.ArithmeticSub, 21);
creator.AddId(csvmInfo.ArithmeticSubOvf, 22);
creator.AddId(csvmInfo.ArithmeticAddOvfUn, 23);
creator.AddId(csvmInfo.ArithmeticAddOvf, 24);
creator.AddId(csvmInfo.ArithmeticAdd, 25);
creator.AddId(csvmInfo.UnaryNot, 26);
creator.AddId(csvmInfo.UnaryNeg, 27);
creator.AddId(csvmInfo.ArgsGet, 28);
creator.AddId(csvmInfo.ArgsSet, 29);
creator.AddId(csvmInfo.LocalsGet, 30);
creator.AddId(csvmInfo.LocalsSet, 31);
return creator;
}
static void GetReadAndExecMethods(TypeDef handler, out MethodDef readMethod, out MethodDef execMethod) {
readMethod = execMethod = null;
foreach (var method in handler.Methods) {
if (!method.IsVirtual)
continue;
if (DotNetUtils.IsMethod(method, "System.Void", "(System.IO.BinaryReader)")) {
if (readMethod != null)
throw new ApplicationException("Found another read method");
readMethod = method;
}
else if (!DotNetUtils.HasReturnValue(method) && method.MethodSig.GetParamCount() == 1) {
if (execMethod != null)
throw new ApplicationException("Found another execute method");
execMethod = method;
}
}
if (readMethod == null)
throw new ApplicationException("Could not find read method");
if (execMethod == null)
throw new ApplicationException("Could not find execute method");
}
IEnumerable<TypeDef> GetVmHandlerTypes(TypeDef baseType) {
foreach (var type in module.Types) {
if (type.BaseType == baseType)
yield return type;
}
}
List<TypeDef> FindBasicVmHandlerTypes(CsvmInfo csvmInfo) {
var list = new List<TypeDef>();
if (csvmInfo.VmHandlerBaseType == null)
return list;
foreach (var type in module.Types) {
if (list.Count == NUM_HANDLERS)
break;
if (type.BaseType == csvmInfo.VmHandlerBaseType)
list.Add(type);
}
return list;
}
List<TypeDef> FindVmHandlerTypes() {
var requiredFields = new string[] {
null,
"System.Collections.Generic.Dictionary`2<System.UInt16,System.Type>",
"System.UInt16",
};
var cflowDeobfuscator = new CflowDeobfuscator();
foreach (var type in module.Types) {
var cctor = type.FindStaticConstructor();
if (cctor == null)
continue;
requiredFields[0] = type.FullName;
if (!new FieldTypes(type).Exactly(requiredFields))
continue;
cflowDeobfuscator.Deobfuscate(cctor);
var handlers = FindVmHandlerTypes(cctor);
if (handlers.Count < NUM_HANDLERS)
continue;
return handlers;
}
return null;
}
static List<TypeDef> FindVmHandlerTypes(MethodDef method) {
var list = new List<TypeDef>();
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code != Code.Ldtoken)
continue;
var type = instr.Operand as TypeDef;
if (type == null)
continue;
list.Add(type);
}
return list;
}
List<OpCodeHandler> CreateOtherHandlers(CsvmInfo csvmInfo, IList<OpCodeHandlerInfo> handlerInfos) {
var list = new List<OpCodeHandler>(NUM_HANDLERS);
foreach (var type in GetVmHandlerTypes(csvmInfo.VmHandlerBaseType)) {
if (list.Count == NUM_HANDLERS)
break;
var execHandler = new PrimitiveHandlerMethod(GetExecMethod(type));
execHandler.Sig = CreateMethodSigInfoCreator(csvmInfo).Create(execHandler.Blocks);
var finder = new MethodFinder(handlerInfos, execHandler);
var handler = finder.FindHandler();
if (handler == null)
continue;
list.Add(handler);
}
list.Sort((a, b) => a.OpCodeHandlerInfo.Name.ToUpperInvariant().CompareTo(b.OpCodeHandlerInfo.Name.ToUpperInvariant()));
return list;
}
}
}

View File

@ -0,0 +1,87 @@
/*
Copyright (C) 2011-2013 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
namespace de4dot.code.deobfuscators {
class NullStream : Stream {
long offset = 0;
long length = 0;
public override bool CanRead {
get { return false; }
}
public override bool CanSeek {
get { return true; }
}
public override bool CanWrite {
get { return true; }
}
public override void Flush() {
}
public override long Length {
get { return length; }
}
public override long Position {
get { return offset; }
set { offset = value; }
}
public override int Read(byte[] buffer, int offset, int count) {
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin) {
switch (origin) {
case SeekOrigin.Begin:
this.offset = offset;
break;
case SeekOrigin.Current:
this.offset += offset;
break;
case SeekOrigin.End:
this.offset = length + offset;
break;
default:
throw new NotSupportedException();
}
return this.offset;
}
public override void SetLength(long value) {
this.length = value;
}
public override void Write(byte[] buffer, int offset, int count) {
this.offset += count;
if (this.offset > this.length)
this.length = this.offset;
}
}
}

View File

@ -20,12 +20,21 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Security;
using dnlib.DotNet;
using dnlib.DotNet.MD;
using dnlib.PE;
using de4dot.blocks;
namespace System.Runtime.ExceptionServices {
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
class HandleProcessCorruptedStateExceptionsAttribute : Attribute {
}
}
namespace de4dot.mdecrypt {
public class DynamicMethodsDecrypter {
static DynamicMethodsDecrypter instance;
@ -132,6 +141,9 @@ namespace de4dot.mdecrypt {
[DllImport("kernel32")]
static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32")]
static extern bool GetModuleHandleEx(uint dwFlags, IntPtr lpModuleName, out IntPtr phModule);
delegate IntPtr GetJit();
delegate int CompileMethod(IntPtr jitter, IntPtr comp, IntPtr info, uint flags, IntPtr nativeEntry, IntPtr nativeSizeOfCode, out bool handled);
delegate int ReturnMethodToken();
@ -364,13 +376,15 @@ namespace de4dot.mdecrypt {
// We're not decrypting methods
var info2 = (CORINFO_METHOD_INFO*)info;
if (info2->scope != moduleToDecryptScope) {
if (info2->scope != moduleToDecryptScope ||
decryptMethodsInfo.moduleCctorBytes == null ||
moduleCctorCodeRva == 0) {
handled = false;
return 0;
}
uint codeRva = (uint)((byte*)info2->ILCode - (byte*)hInstModule);
if (decryptMethodsInfo.moduleCctorBytes != null && moduleCctorCodeRva != 0 && moduleCctorCodeRva == codeRva) {
if (moduleCctorCodeRva == codeRva) {
fixed (byte* newIlCodeBytes = &decryptMethodsInfo.moduleCctorBytes[0]) {
WriteCompileMethod(origCompileMethod);
info2->ILCode = new IntPtr(newIlCodeBytes);
@ -553,30 +567,78 @@ namespace de4dot.mdecrypt {
if (hasInstalledCompileMethod2)
return;
if (!PatchDword(*(IntPtr*)jitterVtbl, 0x30000, origCompileMethod, ourCompileMethodInfo.ptrInDll))
if (!PatchCM(*(IntPtr*)jitterVtbl, origCompileMethod, ourCompileMethodInfo.ptrInDll))
throw new ApplicationException("Couldn't patch compileMethod");
hasInstalledCompileMethod2 = true;
return;
}
unsafe bool PatchDword(IntPtr addr, int size, IntPtr origValue, IntPtr newValue) {
addr = new IntPtr(addr.ToInt64() & ~0xFFF);
var endAddr = new IntPtr(addr.ToInt64() + size);
for (; addr.ToPointer() < endAddr.ToPointer(); addr = new IntPtr(addr.ToInt64() + 0x1000)) {
try {
for (int i = 0; i < 0x1000; i += IntPtr.Size) {
var addr2 = (IntPtr*)((byte*)addr + i);
if (*addr2 == origValue) {
*addr2 = newValue;
return true;
static IntPtr GetModuleHandle(IntPtr addr) {
IntPtr hModule;
if (!GetModuleHandleEx(4, addr, out hModule))
throw new ApplicationException("GetModuleHandleEx() failed");
return hModule;
}
static unsafe bool PatchCM(IntPtr addr, IntPtr origValue, IntPtr newValue) {
var baseAddr = GetModuleHandle(addr);
IntPtr patchAddr;
using (var peImage = new PEImage(baseAddr))
patchAddr = FindCMAddress(peImage, baseAddr, origValue);
if (patchAddr == IntPtr.Zero)
return false;
*(IntPtr*)patchAddr = newValue;
return true;
}
[HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0
static unsafe IntPtr FindCMAddress(PEImage peImage, IntPtr baseAddr, IntPtr origValue) {
const int offset1_CLR2 = 0x78;
const int offset1_CLR4 = 0x74;
int offset1 = Environment.Version.Major == 2 ? offset1_CLR2 : offset1_CLR4;
const int offset2_CLR2 = 0x10;
const int offset2_CLR4 = 0x28;
int offset2 = Environment.Version.Major == 2 ? offset2_CLR2 : offset2_CLR4;
foreach (var section in peImage.ImageSectionHeaders) {
const uint RW = 0x80000000 | 0x40000000;
if ((section.Characteristics & RW) != RW)
continue;
byte* p = (byte*)baseAddr + (uint)section.VirtualAddress + ((section.VirtualSize + IntPtr.Size - 1) & ~(IntPtr.Size - 1)) - IntPtr.Size;
for (; p >= (byte*)baseAddr; p -= IntPtr.Size) {
try {
byte* p2 = (byte*)*(IntPtr**)p;
if ((ulong)p2 >= 0x10000) {
p2 += offset1;
if (*(IntPtr*)p2 == origValue)
return new IntPtr(p2);
}
}
}
catch {
catch {
}
try {
byte* p2 = (byte*)*(IntPtr**)p;
if ((ulong)p2 >= 0x10000) {
p2 += offset2;
if (*(IntPtr*)p2 == origValue)
return new IntPtr(p2);
}
}
catch {
}
try {
if (*(IntPtr*)p == origValue)
return new IntPtr(p);
}
catch {
}
}
}
return false;
return IntPtr.Zero;
}
}
}