de4dot-cex/de4dot.code/deobfuscators/ProxyDelegateFinderBase.cs

360 lines
11 KiB
C#
Raw Normal View History

2011-09-22 10:55:30 +08:00
/*
2012-01-10 06:02:47 +08:00
Copyright (C) 2011-2012 de4dot@gmail.com
2011-09-22 10:55:30 +08:00
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 Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators {
2011-09-22 10:55:30 +08:00
abstract class ProxyDelegateFinderBase {
2011-10-23 19:43:32 +08:00
protected ModuleDefinition module;
protected List<MethodDefinition> delegateCreatorMethods = new List<MethodDefinition>();
2011-12-28 20:33:10 +08:00
protected Dictionary<TypeDefinition, bool> delegateTypesDict = new Dictionary<TypeDefinition, bool>();
FieldDefinitionAndDeclaringTypeDict<DelegateInfo> fieldToDelegateInfo = new FieldDefinitionAndDeclaringTypeDict<DelegateInfo>();
2011-10-23 19:43:32 +08:00
Dictionary<MethodDefinition, FieldDefinition> proxyMethodToField = new Dictionary<MethodDefinition, FieldDefinition>();
int errors = 0;
public int Errors {
get { return errors; }
}
2011-09-22 10:55:30 +08:00
2011-12-28 20:33:10 +08:00
protected class DelegateInfo {
2011-09-22 10:55:30 +08:00
public MethodReference methodRef; // Method we should call
public FieldDefinition field; // Field holding the Delegate instance
2011-10-23 19:43:32 +08:00
public OpCode callOpcode;
public DelegateInfo(FieldDefinition field, MethodReference methodRef, OpCode callOpcode) {
2011-09-22 10:55:30 +08:00
this.field = field;
this.methodRef = methodRef;
2011-10-23 19:43:32 +08:00
this.callOpcode = callOpcode;
2011-09-22 10:55:30 +08:00
}
}
public int RemovedDelegateCreatorCalls { get; set; }
2011-10-23 19:43:32 +08:00
2011-09-22 10:55:30 +08:00
public IEnumerable<TypeDefinition> DelegateTypes {
get { return delegateTypesDict.Keys; }
}
2011-10-23 19:43:32 +08:00
public IEnumerable<TypeDefinition> DelegateCreatorTypes {
get {
foreach (var method in delegateCreatorMethods)
yield return method.DeclaringType;
}
2011-09-22 10:55:30 +08:00
}
public bool Detected {
2011-10-23 19:43:32 +08:00
get { return delegateCreatorMethods.Count != 0; }
2011-09-22 10:55:30 +08:00
}
public ProxyDelegateFinderBase(ModuleDefinition module) {
2011-09-22 10:55:30 +08:00
this.module = module;
}
public ProxyDelegateFinderBase(ModuleDefinition module, ProxyDelegateFinderBase oldOne) {
this.module = module;
foreach (var method in oldOne.delegateCreatorMethods)
delegateCreatorMethods.Add(lookup(method, "Could not find delegate creator method"));
foreach (var kv in oldOne.delegateTypesDict)
delegateTypesDict[lookup(kv.Key, "Could not find delegate type")] = kv.Value;
foreach (var key in oldOne.fieldToDelegateInfo.getKeys())
fieldToDelegateInfo.add(lookup(key, "Could not find field"), copy(oldOne.fieldToDelegateInfo.find(key)));
foreach (var kv in oldOne.proxyMethodToField) {
var key = lookup(kv.Key, "Could not find proxy method");
var value = lookup(kv.Value, "Could not find proxy field");
proxyMethodToField[key] = value;
}
}
DelegateInfo copy(DelegateInfo di) {
var method = lookup(di.methodRef, "Could not find method ref");
var field = lookup(di.field, "Could not find delegate field");
return new DelegateInfo(field, method, di.callOpcode);
}
2012-02-07 21:53:34 +08:00
protected T lookup<T>(T def, string errorMessage) where T : MemberReference {
return DeobUtils.lookup(module, def, errorMessage);
}
2011-09-22 10:55:30 +08:00
public void setDelegateCreatorMethod(MethodDefinition delegateCreatorMethod) {
2011-10-23 19:43:32 +08:00
if (delegateCreatorMethod == null)
return;
delegateCreatorMethods.Add(delegateCreatorMethod);
}
protected bool isDelegateCreatorMethod(MethodDefinition method) {
foreach (var m in delegateCreatorMethods) {
if (m == method)
return true;
}
return false;
2011-09-22 10:55:30 +08:00
}
protected virtual IEnumerable<TypeDefinition> getDelegateTypes() {
foreach (var type in module.Types) {
if (type.BaseType == null || type.BaseType.FullName != "System.MulticastDelegate")
continue;
yield return type;
}
}
2011-09-22 10:55:30 +08:00
public void find() {
2011-10-23 19:43:32 +08:00
if (delegateCreatorMethods.Count == 0)
2011-09-22 10:55:30 +08:00
return;
Log.v("Finding all proxy delegates");
foreach (var type in getDelegateTypes()) {
var cctor = DotNetUtils.getMethod(type, ".cctor");
2011-09-22 10:55:30 +08:00
if (cctor == null || !cctor.HasBody)
continue;
if (!type.HasFields)
continue;
2011-10-23 19:43:32 +08:00
object context = checkCctor(type, cctor);
if (context == null)
2011-09-22 10:55:30 +08:00
continue;
Log.v("Found proxy delegate: {0} ({1:X8})", Utils.removeNewlines(type), type.MetadataToken.ToUInt32());
2011-09-22 10:55:30 +08:00
RemovedDelegateCreatorCalls++;
2011-10-23 19:43:32 +08:00
onFoundProxyDelegate(type);
2011-09-22 10:55:30 +08:00
Log.indent();
foreach (var field in type.Fields) {
if (!field.IsStatic)
2011-09-22 10:55:30 +08:00
continue;
2011-10-23 19:43:32 +08:00
MethodReference calledMethod;
OpCode callOpcode;
getCallInfo(context, field, out calledMethod, out callOpcode);
if (calledMethod == null)
continue;
2011-12-28 20:33:10 +08:00
addDelegateInfo(new DelegateInfo(field, calledMethod, callOpcode));
Log.v("Field: {0}, Opcode: {1}, Method: {2} ({3:X8})",
Utils.removeNewlines(field.Name),
callOpcode,
Utils.removeNewlines(calledMethod),
calledMethod.MetadataToken.ToUInt32());
2011-09-22 10:55:30 +08:00
}
Log.deIndent();
delegateTypesDict[type] = true;
}
}
2011-12-28 20:33:10 +08:00
protected void addDelegateInfo(DelegateInfo di) {
fieldToDelegateInfo.add(di.field, di);
}
2011-10-23 19:43:32 +08:00
protected virtual void onFoundProxyDelegate(TypeDefinition type) {
}
protected abstract object checkCctor(TypeDefinition type, MethodDefinition cctor);
protected abstract void getCallInfo(object context, FieldDefinition field, out MethodReference calledMethod, out OpCode callOpcode);
protected void add(MethodDefinition proxyMethod, FieldDefinition proxyField) {
if (proxyMethod == null || proxyField == null)
return;
proxyMethodToField[proxyMethod] = proxyField;
}
2011-09-22 10:55:30 +08:00
DelegateInfo getDelegateInfo(FieldReference field) {
if (field == null)
return null;
2011-12-28 20:33:10 +08:00
return fieldToDelegateInfo.find(field);
2011-09-22 10:55:30 +08:00
}
class BlockInstr {
public Block Block { get; set; }
public int Index { get; set; }
}
class RemoveInfo {
public int Index { get; set; }
public DelegateInfo DelegateInfo { get; set; }
public bool IsCall {
get { return DelegateInfo != null; }
}
}
protected virtual bool ProxyCallIsObfuscated {
get { return false; }
}
2011-09-22 10:55:30 +08:00
public void deobfuscate(Blocks blocks) {
2011-12-28 20:33:10 +08:00
if (blocks.Method.DeclaringType != null && delegateTypesDict.ContainsKey(blocks.Method.DeclaringType))
return;
var allBlocks = blocks.MethodBlocks.getAllBlocks();
int loops = ProxyCallIsObfuscated ? 50 : 1;
for (int i = 0; i < loops; i++) {
if (!deobfuscate(blocks, allBlocks))
break;
}
fixBrokenCalls(blocks.Method, allBlocks);
}
bool deobfuscate(Blocks blocks, IList<Block> allBlocks) {
2011-09-22 10:55:30 +08:00
var removeInfos = new Dictionary<Block, List<RemoveInfo>>();
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
2011-10-23 19:43:32 +08:00
var instr = instrs[i];
if (instr.OpCode == OpCodes.Ldsfld) {
var di = getDelegateInfo(instr.Operand as FieldReference);
if (di == null)
continue;
var visited = new Dictionary<Block, bool>();
var callInfo = findProxyCall(di, block, i, visited, 1);
if (callInfo != null) {
add(removeInfos, block, i, null);
add(removeInfos, callInfo.Block, callInfo.Index, di);
}
else {
errors++;
2011-10-23 19:43:32 +08:00
Log.w("Could not fix proxy call. Method: {0} ({1:X8}), Proxy type: {2} ({3:X8})",
Utils.removeNewlines(blocks.Method),
blocks.Method.MetadataToken.ToInt32(),
Utils.removeNewlines(di.field.DeclaringType),
di.field.DeclaringType.MetadataToken.ToInt32());
2011-10-23 19:43:32 +08:00
}
2011-09-22 10:55:30 +08:00
}
2011-10-23 19:43:32 +08:00
else if (instr.OpCode == OpCodes.Call) {
var method = instr.Operand as MethodDefinition;
if (method == null)
continue;
FieldDefinition field;
if (!proxyMethodToField.TryGetValue(method, out field))
continue;
var di = getDelegateInfo(field);
if (di == null)
continue;
add(removeInfos, block, i, di);
2011-09-22 10:55:30 +08:00
}
}
}
foreach (var block in removeInfos.Keys) {
var list = removeInfos[block];
var removeIndexes = new List<int>(list.Count);
foreach (var info in list) {
if (info.IsCall) {
2011-10-23 19:43:32 +08:00
var opcode = info.DelegateInfo.callOpcode;
2011-09-22 10:55:30 +08:00
var newInstr = Instruction.Create(opcode, info.DelegateInfo.methodRef);
block.replace(info.Index, 1, newInstr);
}
else
removeIndexes.Add(info.Index);
}
block.remove(removeIndexes);
}
return removeInfos.Count > 0;
2011-09-22 10:55:30 +08:00
}
void add(Dictionary<Block, List<RemoveInfo>> removeInfos, Block block, int index, DelegateInfo di) {
List<RemoveInfo> list;
if (!removeInfos.TryGetValue(block, out list))
removeInfos[block] = list = new List<RemoveInfo>();
list.Add(new RemoveInfo {
Index = index,
DelegateInfo = di,
});
}
BlockInstr findProxyCall(DelegateInfo di, Block block, int index, Dictionary<Block, bool> visited, int stack) {
if (visited.ContainsKey(block))
return null;
if (index <= 0)
visited[block] = true;
var instrs = block.Instructions;
for (int i = index + 1; i < instrs.Count; i++) {
if (stack <= 0)
return null;
var instr = instrs[i];
2011-10-17 06:22:22 +08:00
DotNetUtils.updateStack(instr.Instruction, ref stack, false);
2011-09-22 10:55:30 +08:00
if (stack < 0)
return null;
if (instr.OpCode != OpCodes.Call && instr.OpCode != OpCodes.Callvirt) {
if (stack <= 0)
return null;
2011-09-22 10:55:30 +08:00
continue;
}
var calledMethod = instr.Operand as MethodReference;
if (calledMethod == null)
return null;
if (stack != (DotNetUtils.hasReturnValue(calledMethod) ? 1 : 0))
2011-09-22 10:55:30 +08:00
continue;
if (calledMethod.Name != "Invoke")
return null;
2011-09-22 10:55:30 +08:00
return new BlockInstr {
Block = block,
Index = i,
};
}
if (stack <= 0)
return null;
foreach (var target in block.getTargets()) {
var info = findProxyCall(di, target, -1, visited, stack);
if (info != null)
return info;
}
return null;
}
// The obfuscator could be buggy and call a proxy delegate without pushing the
// instance field. SA has done it, so let's fix it.
void fixBrokenCalls(MethodDefinition obfuscatedMethod, IList<Block> allBlocks) {
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var call = instrs[i];
if (call.OpCode != OpCodes.Call && call.OpCode != OpCodes.Callvirt)
continue;
var methodRef = call.Operand as MethodReference;
if (methodRef.Name != "Invoke")
continue;
var method = DotNetUtils.getMethod(module, methodRef);
if (method == null)
continue;
var declaringType = DotNetUtils.getType(module, method.DeclaringType);
if (declaringType == null)
continue;
if (!delegateTypesDict.ContainsKey(declaringType))
continue;
// Oooops!!! The obfuscator is buggy. Well, let's hope it is, or it's my code. ;)
Log.w("Holy obfuscator bugs, Batman! Found a proxy delegate call with no instance push in {0:X8}. Replacing it with a throw...", obfuscatedMethod.MetadataToken.ToInt32());
block.insert(i, Instruction.Create(OpCodes.Ldnull));
block.replace(i + 1, 1, Instruction.Create(OpCodes.Throw));
i++;
}
}
}
}
}