de4dot-cex/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs
2015-10-29 22:45:26 +01:00

814 lines
23 KiB
C#

/*
Copyright (C) 2011-2015 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <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.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 = FindUnaryOpMethod1(Code.Not);
UnaryNeg = FindUnaryOpMethod1(Code.Neg);
if (UnaryNot != null && UnaryNeg != null)
return true;
return FindUnaryOpMethod2();
}
MethodDef FindUnaryOpMethod1(Code code) {
foreach (var type in module.Types) {
if (type.BaseType != VmHandlerBaseType)
continue;
if (type.Methods.Count != 4)
continue;
var method = FindUnaryMethod(type, code);
if (method != null)
return method;
}
return null;
}
bool FindUnaryOpMethod2() {
foreach (var type in module.Types) {
if (type.BaseType == null || type.BaseType.FullName != "System.Object")
continue;
if (type.Methods.Count != 3)
continue;
UnaryNot = FindUnaryMethod(type, Code.Not);
UnaryNeg = FindUnaryMethod(type, Code.Neg);
if (UnaryNot != null && UnaryNeg != null)
return true;
}
return false;
}
MethodDef FindUnaryMethod(TypeDef type, Code code) {
foreach (var method in type.Methods) {
if (!IsUnsaryMethod(method, code))
continue;
return method;
}
return null;
}
bool IsUnsaryMethod(MethodDef method, Code code) {
if (!method.HasBody || !method.IsStatic)
return false;
if (!DotNetUtils.IsMethod(method, "System.Object", "(System.Object)"))
return false;
if (CountThrows(method) != 1)
return false;
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 true;
}
return false;
}
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;
}
}
}