From f776148574e970a10d5f9120313294c330d22534 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 23 Oct 2011 13:43:32 +0200 Subject: [PATCH] Add proxy delegate fixer --- de4dot.code/de4dot.code.csproj | 1 + .../CliSecure/ProxyDelegateFinder.cs | 36 +++- .../CryptoObfuscator/Deobfuscator.cs | 17 ++ .../CryptoObfuscator/ProxyDelegateFinder.cs | 191 ++++++++++++++++++ de4dot.code/deobfuscators/DeobfuscatorBase.cs | 2 +- .../deobfuscators/ProxyDelegateFinderBase.cs | 129 +++++++----- .../SmartAssembly/ProxyDelegateFinder.cs | 36 +++- 7 files changed, 350 insertions(+), 62 deletions(-) create mode 100644 de4dot.code/deobfuscators/CryptoObfuscator/ProxyDelegateFinder.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 7f3850a6..6edc798f 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -60,6 +60,7 @@ + diff --git a/de4dot.code/deobfuscators/CliSecure/ProxyDelegateFinder.cs b/de4dot.code/deobfuscators/CliSecure/ProxyDelegateFinder.cs index 1db27687..6bd5ef62 100644 --- a/de4dot.code/deobfuscators/CliSecure/ProxyDelegateFinder.cs +++ b/de4dot.code/deobfuscators/CliSecure/ProxyDelegateFinder.cs @@ -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 memberReferences; + public ProxyDelegateFinder(ModuleDefinition module) : base(module) { + this.memberReferences = new List(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; } } } diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs index a6c20309..eba497e9 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs @@ -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) diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/ProxyDelegateFinder.cs b/de4dot.code/deobfuscators/CryptoObfuscator/ProxyDelegateFinder.cs new file mode 100644 index 00000000..1e0ee90d --- /dev/null +++ b/de4dot.code/deobfuscators/CryptoObfuscator/ProxyDelegateFinder.cs @@ -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 . +*/ + +using System; +using System.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; + +namespace de4dot.deobfuscators.CryptoObfuscator { + class ProxyDelegateFinder : ProxyDelegateFinderBase { + Dictionary methodToType = new Dictionary(); + + 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; + } + } +} diff --git a/de4dot.code/deobfuscators/DeobfuscatorBase.cs b/de4dot.code/deobfuscators/DeobfuscatorBase.cs index 4866d097..2d66d363 100644 --- a/de4dot.code/deobfuscators/DeobfuscatorBase.cs +++ b/de4dot.code/deobfuscators/DeobfuscatorBase.cs @@ -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() { diff --git a/de4dot.code/deobfuscators/ProxyDelegateFinderBase.cs b/de4dot.code/deobfuscators/ProxyDelegateFinderBase.cs index ec6be447..93e44be0 100644 --- a/de4dot.code/deobfuscators/ProxyDelegateFinderBase.cs +++ b/de4dot.code/deobfuscators/ProxyDelegateFinderBase.cs @@ -25,46 +25,60 @@ using de4dot.blocks; namespace de4dot.deobfuscators { abstract class ProxyDelegateFinderBase { - ModuleDefinition module; - IList memberReferences; - MethodDefinition delegateCreatorMethod; + protected ModuleDefinition module; + List delegateCreatorMethods = new List(); Dictionary delegateTypesDict = new Dictionary(); Dictionary fieldToDelegateInfo = new Dictionary(); + Dictionary proxyMethodToField = new Dictionary(); 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 DelegateTypes { get { return delegateTypesDict.Keys; } } - public MethodDefinition DelegateCreatorMethod { - get { return delegateCreatorMethod; } + + public IEnumerable 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(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(); - 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(); + 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(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); } diff --git a/de4dot.code/deobfuscators/SmartAssembly/ProxyDelegateFinder.cs b/de4dot.code/deobfuscators/SmartAssembly/ProxyDelegateFinder.cs index 1c78eecd..f1749bb8 100644 --- a/de4dot.code/deobfuscators/SmartAssembly/ProxyDelegateFinder.cs +++ b/de4dot.code/deobfuscators/SmartAssembly/ProxyDelegateFinder.cs @@ -17,8 +17,10 @@ along with de4dot. If not, see . */ +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 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(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) {