Add proxy delegate fixer

This commit is contained in:
de4dot 2011-10-23 13:43:32 +02:00
parent 32bb14fa5a
commit f776148574
7 changed files with 350 additions and 62 deletions

View File

@ -60,6 +60,7 @@
<Compile Include="deobfuscators\CryptoObfuscator\AntiDebugger.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\AssemblyResolver.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\Deobfuscator.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\ProxyDelegateFinder.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\ResourceDecrypter.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\ResourceResolver.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\StringDecrypter.cs" />

View File

@ -20,22 +20,50 @@
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.deobfuscators.CliSecure {
class ProxyDelegateFinder : ProxyDelegateFinderBase {
IList<MemberReference> memberReferences;
public ProxyDelegateFinder(ModuleDefinition module)
: base(module) {
this.memberReferences = new List<MemberReference>(module.GetMemberReferences());
}
protected override void getCallInfo(FieldDefinition field, out int methodIndex, out bool isVirtual) {
protected override object checkCctor(TypeDefinition type, MethodDefinition cctor) {
var instrs = cctor.Body.Instructions;
if (instrs.Count != 3)
return null;
if (!DotNetUtils.isLdcI4(instrs[0].OpCode.Code))
return null;
if (instrs[1].OpCode != OpCodes.Call || !isDelegateCreatorMethod(instrs[1].Operand as MethodDefinition))
return null;
if (instrs[2].OpCode != OpCodes.Ret)
return null;
int delegateToken = 0x02000001 + DotNetUtils.getLdcI4Value(instrs[0]);
if (type.MetadataToken.ToInt32() != delegateToken) {
Log.w("Delegate token is not current type");
return null;
}
return new object();
}
protected override void getCallInfo(object context, FieldDefinition field, out MethodReference calledMethod, out OpCode callOpcode) {
var name = field.Name;
isVirtual = false;
callOpcode = OpCodes.Call;
if (name.EndsWith("%", StringComparison.Ordinal)) {
isVirtual = true;
callOpcode = OpCodes.Callvirt;
name = name.TrimEnd(new char[] { '%' });
}
byte[] value = Convert.FromBase64String(name);
methodIndex = BitConverter.ToInt32(value, 0); // 0-based memberRef index
int methodIndex = BitConverter.ToInt32(value, 0); // 0-based memberRef index
if (methodIndex >= memberReferences.Count)
throw new ApplicationException(string.Format("methodIndex ({0}) >= memberReferences.Count ({1})", methodIndex, memberReferences.Count));
calledMethod = memberReferences[methodIndex] as MethodReference;
}
}
}

View File

@ -60,6 +60,7 @@ namespace de4dot.deobfuscators.CryptoObfuscator {
bool foundCryptoObfuscatorAttribute = false;
bool foundObfuscatedSymbols = false;
ProxyDelegateFinder proxyDelegateFinder;
ResourceDecrypter resourceDecrypter;
ResourceResolver resourceResolver;
AssemblyResolver assemblyResolver;
@ -102,6 +103,8 @@ namespace de4dot.deobfuscators.CryptoObfuscator {
val += 10;
if (tamperDetection.Detected)
val += 10;
if (proxyDelegateFinder.Detected)
val += 10;
return val;
}
@ -117,6 +120,8 @@ namespace de4dot.deobfuscators.CryptoObfuscator {
if (checkCryptoObfuscator())
foundObfuscatedSymbols = true;
proxyDelegateFinder = new ProxyDelegateFinder(module);
proxyDelegateFinder.findDelegateCreator(module);
stringDecrypter = new StringDecrypter(module);
stringDecrypter.find();
tamperDetection = new TamperDetection(module);
@ -182,9 +187,21 @@ namespace de4dot.deobfuscators.CryptoObfuscator {
addTypeToBeRemoved(antiDebugger.AntiDebuggerType, "Anti-debugger type");
addTypeToBeRemoved(stringDecrypter.StringDecrypterType, "String decrypter type");
proxyDelegateFinder.find();
dumpEmbeddedAssemblies();
}
public override void deobfuscateMethodEnd(Blocks blocks) {
proxyDelegateFinder.deobfuscate(blocks);
base.deobfuscateMethodEnd(blocks);
}
public override void deobfuscateEnd() {
removeProxyDelegates(proxyDelegateFinder);
base.deobfuscateEnd();
}
void decryptResources() {
var rsrc = resourceResolver.mergeResources();
if (rsrc == null)

View File

@ -0,0 +1,191 @@
/*
Copyright (C) 2011 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 Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.deobfuscators.CryptoObfuscator {
class ProxyDelegateFinder : ProxyDelegateFinderBase {
Dictionary<MethodDefinition, ProxyCreatorType> methodToType = new Dictionary<MethodDefinition, ProxyCreatorType>();
public ProxyDelegateFinder(ModuleDefinition module)
: base(module) {
}
enum ProxyCreatorType {
None,
CallOrCallvirt,
CallCtor,
Newobj,
}
class Context {
public int typeToken;
public int methodToken;
public int declaringTypeToken;
public ProxyCreatorType proxyCreatorType;
public Context(int typeToken, int methodToken, int declaringTypeToken, ProxyCreatorType proxyCreatorType) {
this.typeToken = typeToken;
this.methodToken = methodToken;
this.declaringTypeToken = declaringTypeToken;
this.proxyCreatorType = proxyCreatorType;
}
}
protected override object checkCctor(TypeDefinition type, MethodDefinition cctor) {
var instructions = cctor.Body.Instructions;
for (int i = 0; i < instructions.Count; i++) {
var instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Ldc_I4, OpCodes.Ldc_I4, OpCodes.Ldc_I4, OpCodes.Call);
if (instrs == null)
continue;
int typeToken = (int)instrs[0].Operand;
int methodToken = (int)instrs[1].Operand;
int declaringTypeToken = (int)instrs[2].Operand;
var createMethod = instrs[3].Operand as MethodDefinition;
ProxyCreatorType proxyCreatorType;
if (!methodToType.TryGetValue(createMethod, out proxyCreatorType))
continue;
return new Context(typeToken, methodToken, declaringTypeToken, proxyCreatorType);
}
return null;
}
protected override void onFoundProxyDelegate(TypeDefinition type) {
foreach (var method in type.Methods) {
if (!method.IsStatic || !method.HasBody || method.Name == ".ctor")
continue;
var instructions = method.Body.Instructions;
for (int i = 0; i < instructions.Count; i++) {
var instr = instructions[i];
if (instr.OpCode.Code != Code.Ldsfld)
continue;
add(method, (FieldDefinition)instr.Operand);
break;
}
}
}
protected override void getCallInfo(object context, FieldDefinition field, out MethodReference calledMethod, out OpCode callOpcode) {
var ctx = (Context)context;
switch (ctx.proxyCreatorType) {
case ProxyCreatorType.CallOrCallvirt:
callOpcode = field.IsFamilyOrAssembly ? OpCodes.Callvirt : OpCodes.Call;
break;
case ProxyCreatorType.CallCtor:
callOpcode = OpCodes.Call;
break;
case ProxyCreatorType.Newobj:
callOpcode = OpCodes.Newobj;
break;
default:
throw new ApplicationException(string.Format("Invalid proxy creator type: {0}", ctx.proxyCreatorType));
}
calledMethod = module.LookupToken(ctx.methodToken) as MethodReference;
}
public void findDelegateCreator(ModuleDefinition module) {
foreach (var type in module.Types) {
var createMethod = getProxyCreateMethod(type);
if (createMethod == null)
continue;
var proxyCreatorType = getProxyCreatorType(type, createMethod);
if (proxyCreatorType == ProxyCreatorType.None)
continue;
methodToType[createMethod] = proxyCreatorType;
setDelegateCreatorMethod(createMethod);
}
}
MethodDefinition getProxyCreateMethod(TypeDefinition type) {
if (type.Fields.Count != 1)
return null;
if (DotNetUtils.findFieldType(type, "System.ModuleHandle", true) == null)
return null;
MethodDefinition createMethod = null;
foreach (var m in type.Methods) {
if (m.Name == ".ctor" || m.Name == ".cctor")
continue;
if (createMethod == null || DotNetUtils.isMethod(m, "System.Void", "(System.Int32,System.Int32,System.Int32)")) {
createMethod = m;
continue;
}
return null;
}
if (!createMethod.HasBody)
return null;
if (type.HasEvents || type.HasProperties)
return null;
if (!findLdci4(createMethod, 0xFFFFFF))
return null;
return createMethod;
}
bool findLdci4(MethodDefinition method, int value) {
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code == Code.Ldc_I4 && (int)instr.Operand == value)
return true;
}
return false;
}
ProxyCreatorType getProxyCreatorType(TypeDefinition type, MethodDefinition createMethod) {
int numCalls = 0, numCallvirts = 0, numNewobjs = 0;
foreach (var instr in createMethod.Body.Instructions) {
if (instr.OpCode.Code != Code.Ldsfld)
continue;
var field = instr.Operand as FieldReference;
if (field == null)
continue;
switch (field.FullName) {
case "System.Reflection.Emit.OpCode System.Reflection.Emit.OpCodes::Call":
numCalls++;
break;
case "System.Reflection.Emit.OpCode System.Reflection.Emit.OpCodes::Callvirt":
numCallvirts++;
break;
case "System.Reflection.Emit.OpCode System.Reflection.Emit.OpCodes::Newobj":
numNewobjs++;
break;
}
}
if (numCalls == 1 && numCallvirts == 1 && numNewobjs == 0)
return ProxyCreatorType.CallOrCallvirt;
if (numCalls == 1 && numCallvirts == 0 && numNewobjs == 0)
return ProxyCreatorType.CallCtor;
if (numCalls == 0 && numCallvirts == 0 && numNewobjs == 1)
return ProxyCreatorType.Newobj;
return ProxyCreatorType.None;
}
}
}

View File

@ -470,7 +470,7 @@ namespace de4dot.deobfuscators {
protected void removeProxyDelegates(ProxyDelegateFinderBase proxyDelegateFinder) {
addTypesToBeRemoved(proxyDelegateFinder.DelegateTypes, "Proxy delegate type");
if (proxyDelegateFinder.RemovedDelegateCreatorCalls > 0)
addTypeToBeRemoved(proxyDelegateFinder.DelegateCreatorMethod.DeclaringType, "Proxy delegate creator type");
addTypesToBeRemoved(proxyDelegateFinder.DelegateCreatorTypes, "Proxy delegate creator type");
}
protected TypeDefinition getModuleType() {

View File

@ -25,46 +25,60 @@ using de4dot.blocks;
namespace de4dot.deobfuscators {
abstract class ProxyDelegateFinderBase {
ModuleDefinition module;
IList<MemberReference> memberReferences;
MethodDefinition delegateCreatorMethod;
protected ModuleDefinition module;
List<MethodDefinition> delegateCreatorMethods = new List<MethodDefinition>();
Dictionary<TypeDefinition, bool> delegateTypesDict = new Dictionary<TypeDefinition, bool>();
Dictionary<FieldReferenceAndDeclaringTypeKey, DelegateInfo> fieldToDelegateInfo = new Dictionary<FieldReferenceAndDeclaringTypeKey, DelegateInfo>();
Dictionary<MethodDefinition, FieldDefinition> proxyMethodToField = new Dictionary<MethodDefinition, FieldDefinition>();
class DelegateInfo {
public MethodReference methodRef; // Method we should call
public FieldDefinition field; // Field holding the Delegate instance
public bool isVirtual;
public DelegateInfo(FieldDefinition field, MethodReference methodRef, bool isVirtual) {
public OpCode callOpcode;
public DelegateInfo(FieldDefinition field, MethodReference methodRef, OpCode callOpcode) {
this.field = field;
this.methodRef = methodRef;
this.isVirtual = isVirtual;
this.callOpcode = callOpcode;
}
}
public int RemovedDelegateCreatorCalls { get; set; }
public IEnumerable<TypeDefinition> DelegateTypes {
get { return delegateTypesDict.Keys; }
}
public MethodDefinition DelegateCreatorMethod {
get { return delegateCreatorMethod; }
public IEnumerable<TypeDefinition> DelegateCreatorTypes {
get {
foreach (var method in delegateCreatorMethods)
yield return method.DeclaringType;
}
}
public bool Detected {
get { return delegateCreatorMethod != null; }
get { return delegateCreatorMethods.Count != 0; }
}
public ProxyDelegateFinderBase(ModuleDefinition module) {
this.module = module;
this.memberReferences = new List<MemberReference>(module.GetMemberReferences());
}
public void setDelegateCreatorMethod(MethodDefinition delegateCreatorMethod) {
this.delegateCreatorMethod = delegateCreatorMethod;
if (delegateCreatorMethod == null)
return;
delegateCreatorMethods.Add(delegateCreatorMethod);
}
protected bool isDelegateCreatorMethod(MethodDefinition method) {
foreach (var m in delegateCreatorMethods) {
if (m == method)
return true;
}
return false;
}
public void find() {
if (delegateCreatorMethod == null)
if (delegateCreatorMethods.Count == 0)
return;
Log.v("Finding all proxy delegates");
@ -77,47 +91,44 @@ namespace de4dot.deobfuscators {
if (!type.HasFields)
continue;
var instrs = cctor.Body.Instructions;
if (instrs.Count != 3)
object context = checkCctor(type, cctor);
if (context == null)
continue;
if (!DotNetUtils.isLdcI4(instrs[0].OpCode.Code))
continue;
if (instrs[1].OpCode != OpCodes.Call || instrs[1].Operand != delegateCreatorMethod)
continue;
if (instrs[2].OpCode != OpCodes.Ret)
continue;
int delegateToken = 0x02000001 + DotNetUtils.getLdcI4Value(instrs[0]);
if (type.MetadataToken.ToInt32() != delegateToken) {
Log.w("Delegate token is not current type");
continue;
}
Log.v("Found proxy delegate: {0} ({1:X8})", type, type.MetadataToken.ToUInt32());
RemovedDelegateCreatorCalls++;
onFoundProxyDelegate(type);
Log.indent();
foreach (var field in type.Fields) {
if (!field.IsStatic || field.IsPublic)
continue;
int methodIndex;
bool isVirtual;
getCallInfo(field, out methodIndex, out isVirtual);
if (methodIndex >= memberReferences.Count)
throw new ApplicationException(string.Format("methodIndex ({0}) >= memberReferences.Count ({1})", methodIndex, memberReferences.Count));
MethodReference calledMethod;
OpCode callOpcode;
getCallInfo(context, field, out calledMethod, out callOpcode);
var methodRef = memberReferences[methodIndex] as MethodReference;
if (methodRef == null)
throw new ApplicationException("methodRef is null");
fieldToDelegateInfo[new FieldReferenceAndDeclaringTypeKey(field)] = new DelegateInfo(field, methodRef, isVirtual);
Log.v("Field: {0}, Virtual: {1}, Method: {2}, RID: {3}", field.Name, isVirtual, methodRef, methodIndex + 1);
if (calledMethod == null)
throw new ApplicationException("calledMethod is null");
fieldToDelegateInfo[new FieldReferenceAndDeclaringTypeKey(field)] = new DelegateInfo(field, calledMethod, callOpcode);
Log.v("Field: {0}, Opcode: {1}, Method: {2} ({3:X8})", field.Name, callOpcode, calledMethod, calledMethod.MetadataToken.ToUInt32());
}
Log.deIndent();
delegateTypesDict[type] = true;
}
}
protected abstract void getCallInfo(FieldDefinition field, out int methodIndex, out bool isVirtual);
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;
}
MethodDefinition findMethod(TypeDefinition type, string name) {
if (!type.HasMethods)
@ -158,23 +169,35 @@ namespace de4dot.deobfuscators {
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var ldsfld = instrs[i];
if (ldsfld.OpCode != OpCodes.Ldsfld)
continue;
var di = getDelegateInfo(ldsfld.Operand as FieldReference);
if (di == null)
continue;
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);
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 {
Log.w("Could not fix proxy call. Method: {0} ({1:X8}), Proxy type: {2} ({3:X8})",
blocks.Method, blocks.Method.MetadataToken.ToInt32(),
di.field.DeclaringType, di.field.DeclaringType.MetadataToken.ToInt32());
}
}
else {
Log.w("Could not fix proxy call. Method: {0} ({1:X8}), Proxy type: {2} ({3:X8})",
blocks.Method, blocks.Method.MetadataToken.ToInt32(),
di.field.DeclaringType, di.field.DeclaringType.MetadataToken.ToInt32());
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);
}
}
}
@ -184,7 +207,7 @@ namespace de4dot.deobfuscators {
var removeIndexes = new List<int>(list.Count);
foreach (var info in list) {
if (info.IsCall) {
var opcode = info.DelegateInfo.isVirtual ? OpCodes.Callvirt : OpCodes.Call;
var opcode = info.DelegateInfo.callOpcode;
var newInstr = Instruction.Create(opcode, info.DelegateInfo.methodRef);
block.replace(info.Index, 1, newInstr);
}

View File

@ -17,8 +17,10 @@
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.deobfuscators.SmartAssembly {
@ -35,6 +37,8 @@ namespace de4dot.deobfuscators.SmartAssembly {
'\x9E', '\x9F',
};
IList<MemberReference> memberReferences;
static ProxyDelegateFinder() {
for (int i = 0; i < specialChars.Length; i++)
specialCharsDict[specialChars[i]] = i;
@ -42,17 +46,38 @@ namespace de4dot.deobfuscators.SmartAssembly {
public ProxyDelegateFinder(ModuleDefinition module)
: base(module) {
this.memberReferences = new List<MemberReference>(module.GetMemberReferences());
}
protected override void getCallInfo(FieldDefinition field, out int methodIndex, out bool isVirtual) {
isVirtual = false;
protected override object checkCctor(TypeDefinition type, MethodDefinition cctor) {
var instrs = cctor.Body.Instructions;
if (instrs.Count != 3)
return null;
if (!DotNetUtils.isLdcI4(instrs[0].OpCode.Code))
return null;
if (instrs[1].OpCode != OpCodes.Call || !isDelegateCreatorMethod(instrs[1].Operand as MethodDefinition))
return null;
if (instrs[2].OpCode != OpCodes.Ret)
return null;
int delegateToken = 0x02000001 + DotNetUtils.getLdcI4Value(instrs[0]);
if (type.MetadataToken.ToInt32() != delegateToken) {
Log.w("Delegate token is not current type");
return null;
}
return new object();
}
protected override void getCallInfo(object context, FieldDefinition field, out MethodReference calledMethod, out OpCode callOpcode) {
callOpcode = OpCodes.Call;
string name = field.Name;
methodIndex = 0;
int methodIndex = 0;
for (int i = name.Length - 1; i >= 0; i--) {
char c = name[i];
if (c == '~') {
isVirtual = true;
callOpcode = OpCodes.Callvirt;
break;
}
@ -60,6 +85,9 @@ namespace de4dot.deobfuscators.SmartAssembly {
if (specialCharsDict.TryGetValue(c, out val))
methodIndex = methodIndex * specialChars.Length + val;
}
if (methodIndex >= memberReferences.Count)
throw new ApplicationException(string.Format("methodIndex ({0}) >= memberReferences.Count ({1})", methodIndex, memberReferences.Count));
calledMethod = memberReferences[methodIndex] as MethodReference;
}
public void findDelegateCreator(ModuleDefinition module) {