/*
Copyright (C) 2011-2013 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see .
*/
using System;
using System.Collections.Generic;
using de4dot.blocks;
using de4dot.blocks.cflow;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
namespace de4dot.code.deobfuscators.DeepSea {
class CastDeobfuscator : IBlocksDeobfuscator {
Blocks blocks;
Dictionary localInfos = new Dictionary();
class LocalInfo {
public readonly Local local;
ITypeDefOrRef type;
bool isValid;
public ITypeDefOrRef CastType {
get { return type; }
set {
if (!isValid)
return;
if (value == null) {
invalid();
return;
}
if (type != null && !new SigComparer().Equals(type, value)) {
invalid();
return;
}
type = value;
}
}
public LocalInfo(Local local) {
this.local = local;
this.isValid = true;
}
public void invalid() {
isValid = false;
type = null;
}
public override string ToString() {
if (type == null)
return string.Format("{0} - INVALID", local);
return string.Format("{0} - {1:X8} {2}", local, type.MDToken.ToInt32(), type.FullName);
}
}
public bool ExecuteOnNoChange {
get { return true; }
}
public void DeobfuscateBegin(Blocks blocks) {
this.blocks = blocks;
}
public bool Deobfuscate(List allBlocks) {
if (!Initialize(allBlocks))
return false;
bool changed = false;
var indexesToRemove = new List();
foreach (var block in allBlocks) {
indexesToRemove.Clear();
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count - 1; i++) {
var instr = instrs[i];
if (instr.OpCode.Code == Code.Ldloca || instr.OpCode.Code == Code.Ldloca_S) {
var local = instr.Operand as Local;
if (local == null)
continue;
localInfos[local].invalid();
}
else if (instr.IsLdloc()) {
var local = instr.Instruction.GetLocal(blocks.Locals);
if (local == null)
continue;
var localInfo = localInfos[local];
var cast = instrs[i + 1];
if (localInfo.CastType == null)
continue;
if (!IsCast(cast))
throw new ApplicationException("Not a cast instr");
indexesToRemove.Add(i + 1);
}
}
if (indexesToRemove.Count > 0) {
block.Remove(indexesToRemove);
changed = true;
}
}
foreach (var info in localInfos.Values) {
if (info.CastType == null)
continue;
info.local.Type = info.CastType.ToTypeSig();
}
if (changed) {
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count - 1; i++) {
var instr = instrs[i];
int castIndex = i + 1;
if (instr.OpCode.Code == Code.Dup) {
if (i == 0)
continue;
castIndex = i;
instr = instrs[i - 1];
}
if (instr.IsLdarg())
AddCast(block, castIndex, i + 1, instr.Instruction.GetArgumentType(blocks.Method.MethodSig, blocks.Method.DeclaringType));
else if (instr.OpCode.Code == Code.Ldfld || instr.OpCode.Code == Code.Ldsfld) {
var field = instr.Operand as IField;
if (field == null)
continue;
AddCast(block, castIndex, i + 1, field.FieldSig.GetFieldType());
}
else if (instr.OpCode.Code == Code.Call || instr.OpCode.Code == Code.Callvirt) {
var calledMethod = instr.Operand as IMethod;
if (calledMethod == null || !DotNetUtils.HasReturnValue(calledMethod))
continue;
AddCast(block, castIndex, i + 1, calledMethod.MethodSig.GetRetType());
}
}
}
}
return changed;
}
bool AddCast(Block block, int castIndex, int index, TypeSig type) {
if (type == null)
return false;
if (castIndex >= block.Instructions.Count || index >= block.Instructions.Count)
return false;
var stloc = block.Instructions[index];
if (!stloc.IsStloc())
return false;
var local = stloc.Instruction.GetLocal(blocks.Locals);
if (local == null)
return false;
var localInfo = localInfos[local];
if (localInfo.CastType == null)
return false;
if (!new SigComparer().Equals(localInfo.CastType, type))
block.Insert(castIndex, new Instruction(OpCodes.Castclass, localInfo.CastType));
return true;
}
bool Initialize(List allBlocks) {
localInfos.Clear();
foreach (var local in blocks.Locals)
localInfos[local] = new LocalInfo(local);
if (localInfos.Count == 0)
return false;
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count - 1; i++) {
var ldloc = instrs[i];
if (!ldloc.IsLdloc())
continue;
var local = ldloc.Instruction.GetLocal(blocks.Locals);
if (local == null)
continue;
var localInfo = localInfos[local];
localInfo.CastType = GetCastType(instrs[i + 1]);
}
}
return true;
}
static bool IsCast(Instr instr) {
return instr.OpCode.Code == Code.Castclass || instr.OpCode.Code == Code.Isinst;
}
static ITypeDefOrRef GetCastType(Instr instr) {
if (!IsCast(instr))
return null;
return instr.Operand as ITypeDefOrRef;
}
}
}