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

594 lines
13 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 System.Collections.Generic;
using System.Text;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
class MethodSigInfo {
public HandlerTypeCode TypeCode { get; set; }
public List<BlockSigInfo> BlockSigInfos { get; private set; }
public MethodSigInfo(List<BlockSigInfo> blockSigInfos) {
this.BlockSigInfos = blockSigInfos;
}
public MethodSigInfo(List<BlockSigInfo> blockSigInfos, HandlerTypeCode typeCode) {
this.BlockSigInfos = blockSigInfos;
this.TypeCode = typeCode;
}
public override string ToString() {
return OpCodeHandlerInfo.GetHandlerName(TypeCode);
}
}
class BlockSigInfo {
readonly List<int> targets;
public List<BlockElementHash> Hashes { get; private set; }
public List<int> Targets {
get { return targets; }
}
public bool HasFallThrough { get; set; }
public bool EndsInRet { get; set; }
public BlockSigInfo() {
this.targets = new List<int>();
this.Hashes = new List<BlockElementHash>();
}
public BlockSigInfo(List<BlockElementHash> hashes, List<int> targets) {
this.Hashes = hashes;
this.targets = targets;
}
}
enum BlockElementHash : int {
}
class SigCreator {
const int BASE_INDEX = 0x40000000;
Blocks blocks;
Dictionary<object, int> objToId = new Dictionary<object, int>();
CRC32 hasher = new CRC32();
public SigCreator() {
}
public void AddId(object key, int id) {
if (key != null)
objToId[key] = id;
}
int? GetId(object key) {
if (key == null)
return null;
int id;
if (objToId.TryGetValue(key, out id))
return id;
return null;
}
public List<BlockSigInfo> Create(MethodDef method) {
blocks = new Blocks(method);
var allBlocks = blocks.MethodBlocks.GetAllBlocks();
var blockInfos = new List<BlockSigInfo>(allBlocks.Count);
foreach (var block in allBlocks) {
var blockInfo = new BlockSigInfo();
blockInfo.HasFallThrough = block.FallThrough != null;
blockInfo.EndsInRet = block.LastInstr.OpCode.Code == Code.Ret;
blockInfos.Add(blockInfo);
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var info = CalculateHash(instrs, ref i);
if (info != null)
blockInfo.Hashes.Add(info.Value);
}
}
for (int i = 0; i < blockInfos.Count; i++) {
var block = allBlocks[i];
var blockInfo = blockInfos[i];
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 blockInfos;
}
BlockElementHash? CalculateHash(IList<Instr> instrs, ref int index) {
hasher.Initialize();
var instr = instrs[index];
switch (instr.OpCode.Code) {
case Code.Beq:
case Code.Beq_S:
return GetHash(BASE_INDEX + 0);
case Code.Bge:
case Code.Bge_S:
return GetHash(BASE_INDEX + 1);
case Code.Bge_Un:
case Code.Bge_Un_S:
return GetHash(BASE_INDEX + 2);
case Code.Bgt:
case Code.Bgt_S:
return GetHash(BASE_INDEX + 3);
case Code.Bgt_Un:
case Code.Bgt_Un_S:
return GetHash(BASE_INDEX + 4);
case Code.Ble:
case Code.Ble_S:
return GetHash(BASE_INDEX + 5);
case Code.Ble_Un:
case Code.Ble_Un_S:
return GetHash(BASE_INDEX + 6);
case Code.Blt:
case Code.Blt_S:
return GetHash(BASE_INDEX + 7);
case Code.Blt_Un:
case Code.Blt_Un_S:
return GetHash(BASE_INDEX + 8);
case Code.Bne_Un:
case Code.Bne_Un_S:
return GetHash(BASE_INDEX + 9);
case Code.Brfalse:
case Code.Brfalse_S:
return GetHash(BASE_INDEX + 10);
case Code.Brtrue:
case Code.Brtrue_S:
return GetHash(BASE_INDEX + 11);
case Code.Switch:
return GetHash(BASE_INDEX + 12);
case Code.Ceq:
return GetHash(BASE_INDEX + 13);
case Code.Cgt:
return GetHash(BASE_INDEX + 14);
case Code.Cgt_Un:
return GetHash(BASE_INDEX + 15);
case Code.Clt:
return GetHash(BASE_INDEX + 16);
case Code.Clt_Un:
return GetHash(BASE_INDEX + 17);
case Code.Ldc_I4:
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_M1:
case Code.Ldc_I4_S:
return GetHash(instr.GetLdcI4Value());
case Code.Ldstr:
return GetHash(instr.Operand as string);
case Code.Rethrow:
return GetHash(BASE_INDEX + 18);
case Code.Throw:
return GetHash(BASE_INDEX + 19);
case Code.Call:
case Code.Callvirt:
Hash(instr.Operand);
return (BlockElementHash)hasher.GetHash();
case Code.Ldfld:
var field = instr.Operand as FieldDef;
if (!IsTypeField(field))
return null;
if (index + 1 >= instrs.Count || !instrs[index + 1].IsLdcI4())
return null;
index++;
return GetHash(GetFieldId(field));
default:
break;
}
return null;
}
bool IsTypeField(FieldDef fd) {
return fd != null && fd.DeclaringType == blocks.Method.DeclaringType;
}
static int GetFieldId(FieldDef fd) {
if (fd == null)
return int.MinValue;
var fieldType = fd.FieldSig.GetFieldType();
if (fieldType == null)
return int.MinValue + 1;
int result = BASE_INDEX + 0x1000;
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;
}
void Hash(object op) {
var md = op as MethodDef;
if (md != null) {
Hash(md);
return;
}
var mr = op as MemberRef;
if (mr != null) {
Hash(mr);
return;
}
var td = op as TypeDef;
if (td != null) {
Hash(td);
return;
}
var tr = op as TypeRef;
if (tr != null) {
Hash(tr);
return;
}
var ts = op as TypeSpec;
if (ts != null) {
Hash(ts);
return;
}
var fsig = op as FieldSig;
if (fsig != null) {
Hash(fsig);
return;
}
var msig = op as MethodSig;
if (msig != null) {
Hash(msig);
return;
}
var gsig = op as GenericInstMethodSig;
if (gsig != null) {
Hash(gsig);
return;
}
var asmRef = op as AssemblyRef;
if (asmRef != null) {
Hash(asmRef);
return;
}
var tsig = op as TypeSig;
if (tsig != null) {
Hash(tsig);
return;
}
return;
}
void Hash(TypeSig sig) {
Hash(sig, 0);
}
void Hash(TypeSig sig, int level) {
if (sig == null)
return;
if (level++ > 20)
return;
hasher.Hash((byte)0x41);
var etype = sig.GetElementType();
hasher.Hash((byte)etype);
switch (etype) {
case ElementType.Ptr:
case ElementType.ByRef:
case ElementType.SZArray:
case ElementType.Pinned:
Hash(sig.Next, level);
break;
case ElementType.Array:
var arySig = (ArraySig)sig;
hasher.Hash(arySig.Rank);
hasher.Hash(arySig.Sizes.Count);
hasher.Hash(arySig.LowerBounds.Count);
Hash(sig.Next, level);
break;
case ElementType.CModReqd:
case ElementType.CModOpt:
Hash(((ModifierSig)sig).Modifier);
Hash(sig.Next, level);
break;
case ElementType.ValueArray:
hasher.Hash(((ValueArraySig)sig).Size);
Hash(sig.Next, level);
break;
case ElementType.Module:
hasher.Hash(((ModuleSig)sig).Index);
Hash(sig.Next, level);
break;
case ElementType.GenericInst:
var gis = (GenericInstSig)sig;
Hash(gis.GenericType, level);
foreach (var ga in gis.GenericArguments)
Hash(ga, level);
Hash(sig.Next, level);
break;
case ElementType.FnPtr:
Hash(((FnPtrSig)sig).Signature);
break;
case ElementType.Var:
case ElementType.MVar:
hasher.Hash(((GenericSig)sig).Number);
break;
case ElementType.ValueType:
case ElementType.Class:
Hash(((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 Hash(MethodDef md) {
if (md == null)
return;
var attrMask1 = MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask |
MethodImplAttributes.ForwardRef | MethodImplAttributes.PreserveSig |
MethodImplAttributes.InternalCall;
hasher.Hash((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;
hasher.Hash((ushort)(md.Attributes & attrMask2));
Hash(md.Signature);
hasher.Hash(md.ParamDefs.Count);
hasher.Hash(md.GenericParameters.Count);
hasher.Hash(md.HasImplMap ? 1 : 0);
var id = GetId(md);
if (id != null)
hasher.Hash(id.Value);
}
void Hash(MemberRef mr) {
if (mr == null)
return;
Hash(mr.Class);
if (IsFromNonObfuscatedAssembly(mr.Class))
Hash(mr.Name);
Hash(mr.Signature);
}
void Hash(TypeDef td) {
if (td == null)
return;
Hash(td.BaseType);
var attrMask = TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask |
TypeAttributes.Abstract | TypeAttributes.SpecialName |
TypeAttributes.Import | TypeAttributes.WindowsRuntime |
TypeAttributes.StringFormatMask | TypeAttributes.RTSpecialName;
hasher.Hash((uint)(td.Attributes & attrMask));
hasher.Hash(td.GenericParameters.Count);
hasher.Hash(td.Interfaces.Count);
foreach (var iface in td.Interfaces)
Hash(iface.Interface);
var id = GetId(td);
if (id != null)
hasher.Hash(id.Value);
}
void Hash(TypeRef tr) {
if (tr == null)
return;
Hash(tr.ResolutionScope);
if (IsFromNonObfuscatedAssembly(tr)) {
Hash(tr.Namespace);
Hash(tr.Name);
}
}
void Hash(TypeSpec ts) {
if (ts == null)
return;
Hash(ts.TypeSig);
}
void Hash(FieldSig sig) {
if (sig == null)
return;
hasher.Hash((byte)sig.GetCallingConvention());
Hash(sig.GetFieldType());
}
void Hash(MethodSig sig) {
if (sig == null)
return;
hasher.Hash((byte)sig.GetCallingConvention());
Hash(sig.GetRetType());
foreach (var p in sig.GetParams())
Hash(p);
hasher.Hash(sig.GetParamCount());
if (sig.GetParamsAfterSentinel() != null) {
foreach (var p in sig.GetParamsAfterSentinel())
Hash(p);
}
}
void Hash(GenericInstMethodSig sig) {
if (sig == null)
return;
hasher.Hash((byte)sig.GetCallingConvention());
foreach (var ga in sig.GetGenericArguments())
Hash(ga);
}
void Hash(AssemblyRef asmRef) {
if (asmRef == null)
return;
bool canWriteAsm = IsNonObfuscatedAssembly(asmRef);
hasher.Hash(canWriteAsm ? 1 : 0);
if (canWriteAsm) {
bool hasPk = !PublicKeyBase.IsNullOrEmpty2(asmRef.PublicKeyOrToken);
if (hasPk)
hasher.Hash(PublicKeyBase.ToPublicKeyToken(asmRef.PublicKeyOrToken).Data);
Hash(asmRef.Name);
Hash(asmRef.Culture);
}
}
void Hash(string s) {
if (s != null)
hasher.Hash(Encoding.UTF8.GetBytes(s));
}
BlockElementHash GetHash(int val) {
hasher.Hash(val);
return (BlockElementHash)hasher.GetHash();
}
BlockElementHash GetHash(string s) {
Hash(s);
return (BlockElementHash)hasher.GetHash();
}
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(IAssembly asm) {
if (asm == null)
return false;
// The only external asm refs it uses...
if (asm.Name != "mscorlib" && asm.Name != "System")
return false;
return true;
}
}
}