From 85ce802131fe88accf0a9e6633136a832202e107 Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 30 Jul 2012 09:16:52 +0200 Subject: [PATCH] Add Confuser 1.0 proxy call fixer --- de4dot.code/de4dot.code.csproj | 1 + .../deobfuscators/Confuser/Deobfuscator.cs | 24 +- .../Confuser/ProxyCallFixerV1.cs | 335 ++++++++++++++++++ 3 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 de4dot.code/deobfuscators/Confuser/ProxyCallFixerV1.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index e26ce48c..3bd41d59 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -78,6 +78,7 @@ + diff --git a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs index 7076b8d5..86fda9b3 100644 --- a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs @@ -69,6 +69,7 @@ namespace de4dot.code.deobfuscators.Confuser { JitMethodsDecrypter jitMethodsDecrypter; MemoryMethodsDecrypter memoryMethodsDecrypter; ProxyCallFixer proxyCallFixer; + ProxyCallFixerV1 proxyCallFixerV1; AntiDebugger antiDebugger; AntiDumping antiDumping; ResourceDecrypter resourceDecrypter; @@ -122,6 +123,7 @@ namespace de4dot.code.deobfuscators.Confuser { int sum = toInt32(jitMethodsDecrypter != null ? jitMethodsDecrypter.Detected : false) + toInt32(memoryMethodsDecrypter != null ? memoryMethodsDecrypter.Detected : false) + toInt32(proxyCallFixer != null ? proxyCallFixer.Detected : false) + + toInt32(proxyCallFixerV1 != null ? proxyCallFixerV1.Detected : false) + toInt32(antiDebugger != null ? antiDebugger.Detected : false) + toInt32(antiDumping != null ? antiDumping.Detected : false) + toInt32(resourceDecrypter != null ? resourceDecrypter.Detected : false) + @@ -157,6 +159,10 @@ namespace de4dot.code.deobfuscators.Confuser { initializeConstantsDecrypter(); proxyCallFixer = new ProxyCallFixer(module, getFileData(), DeobfuscatedFile); proxyCallFixer.findDelegateCreator(); + if (!proxyCallFixer.Detected) { + proxyCallFixerV1 = new ProxyCallFixerV1(module); + proxyCallFixerV1.findDelegateCreator(); + } antiDebugger = new AntiDebugger(module); antiDebugger.find(); antiDumping = new AntiDumping(module); @@ -235,7 +241,10 @@ namespace de4dot.code.deobfuscators.Confuser { addTypeToBeRemoved(antiDumping.Type, "Anti dumping type"); } - proxyCallFixer.find(); + if (proxyCallFixer != null) + proxyCallFixer.find(); + if (proxyCallFixerV1 != null) + proxyCallFixerV1.find(); startedDeobfuscating = true; } @@ -283,7 +292,10 @@ namespace de4dot.code.deobfuscators.Confuser { } public override void deobfuscateMethodEnd(Blocks blocks) { - proxyCallFixer.deobfuscate(blocks); + if (proxyCallFixer != null) + proxyCallFixer.deobfuscate(blocks); + if (proxyCallFixerV1 != null) + proxyCallFixerV1.deobfuscate(blocks); resourceDecrypter.deobfuscate(blocks); int32ValueInliner.decrypt(blocks); int64ValueInliner.decrypt(blocks); @@ -293,7 +305,13 @@ namespace de4dot.code.deobfuscators.Confuser { } public override void deobfuscateEnd() { - removeProxyDelegates(proxyCallFixer); + if (proxyCallFixer != null) + removeProxyDelegates(proxyCallFixer); + if (proxyCallFixerV1 != null) { + if (removeProxyDelegates(proxyCallFixerV1)) + addFieldsToBeRemoved(proxyCallFixerV1.Fields, "Proxy delegate instance field"); + proxyCallFixerV1.cleanUp(); + } constantsDecrypter.cleanUp(); base.deobfuscateEnd(); diff --git a/de4dot.code/deobfuscators/Confuser/ProxyCallFixerV1.cs b/de4dot.code/deobfuscators/Confuser/ProxyCallFixerV1.cs new file mode 100644 index 00000000..5a65e9bd --- /dev/null +++ b/de4dot.code/deobfuscators/Confuser/ProxyCallFixerV1.cs @@ -0,0 +1,335 @@ +/* + Copyright (C) 2011-2012 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 System.IO; +using System.Text; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Confuser { + class ProxyCallFixerV1 : ProxyCallFixer2 { + MethodDefinitionAndDeclaringTypeDict methodToInfo = new MethodDefinitionAndDeclaringTypeDict(); + FieldDefinitionAndDeclaringTypeDict fieldToMethod = new FieldDefinitionAndDeclaringTypeDict(); + string ourAsm; + + enum ProxyCreatorType { + None, + CallOrCallvirt, + Newobj, + } + + class ProxyCreatorInfo { + public readonly MethodDefinition creatorMethod; + public readonly ProxyCreatorType proxyCreatorType; + + public ProxyCreatorInfo(MethodDefinition creatorMethod, ProxyCreatorType proxyCreatorType) { + this.creatorMethod = creatorMethod; + this.proxyCreatorType = proxyCreatorType; + } + } + + class DelegateInitInfo { + public readonly byte[] data; + public readonly FieldDefinition field; + public readonly MethodDefinition creatorMethod; + + public DelegateInitInfo(string data, FieldDefinition field, MethodDefinition creatorMethod) { + this.data = Convert.FromBase64String(data); + this.field = field; + this.creatorMethod = creatorMethod; + } + } + + protected override bool ProxyCallIsObfuscated { + get { return true; } + } + + public IEnumerable Fields { + get { return fieldToMethod.getKeys(); } + } + + public override IEnumerable> OtherMethods { + get { + var list = new List>(); + foreach (var creatorMethod in methodToInfo.getKeys()) { + list.Add(new Tuple { + Item1 = creatorMethod, + Item2 = "Delegate creator method", + }); + } + foreach (var method in fieldToMethod.getValues()) { + list.Add(new Tuple { + Item1 = method, + Item2 = "Proxy delegate method", + }); + } + return list; + } + } + + public ProxyCallFixerV1(ModuleDefinition module) + : base(module) { + ourAsm = (module.Assembly.Name ?? new AssemblyNameReference(" -1-1-1-1-1- ", new Version(1, 2, 3, 4))).FullName; + } + + protected override object checkCctor(TypeDefinition type, MethodDefinition cctor) { + throw new NotSupportedException(); + } + + protected override void getCallInfo(object context, FieldDefinition field, out MethodReference calledMethod, out OpCode callOpcode) { + var info = (DelegateInitInfo)context; + var creatorInfo = methodToInfo.find(info.creatorMethod); + + var reader = new BinaryReader(new MemoryStream(info.data)); + + bool isCallvirt = false; + if (creatorInfo.proxyCreatorType == ProxyCreatorType.CallOrCallvirt) + isCallvirt = reader.ReadBoolean(); + + var asmRef = readAssemblyNameReference(reader); + uint token = reader.ReadUInt32(); + if (reader.BaseStream.Position != reader.BaseStream.Length) + throw new ApplicationException("Extra data"); + + if (asmRef.FullName == ourAsm) + calledMethod = (MethodReference)module.LookupToken((int)token); + else + calledMethod = createMethodReference(asmRef, token); + + callOpcode = getCallOpCode(creatorInfo, isCallvirt); + } + + // A method token is not a stable value so this method can fail to return the correct method! + // There's nothing I can do about that. It's an obfuscator bug. + MethodReference createMethodReference(AssemblyNameReference asmRef, uint methodToken) { + var asm = AssemblyResolver.Instance.Resolve(asmRef); + if (asm == null) + return null; + + var method = asm.MainModule.LookupToken((int)methodToken) as MethodDefinition; + if (method == null) + return null; + + return module.Import(method); + } + + static AssemblyNameReference readAssemblyNameReference(BinaryReader reader) { + var name = readString(reader); + var version = new Version(reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16(), reader.ReadInt16()); + var culture = readString(reader); + byte[] pkt = reader.ReadBoolean() ? reader.ReadBytes(8) : null; + return new AssemblyNameReference(name, version) { + Culture = culture, + PublicKeyToken = pkt, + }; + } + + static string readString(BinaryReader reader) { + int len = reader.ReadByte(); + var bytes = new byte[len]; + for (int i = 0; i < len; i++) + bytes[i] = (byte)(reader.ReadByte() ^ len); + return Encoding.UTF8.GetString(bytes); + } + + static OpCode getCallOpCode(ProxyCreatorInfo info, bool isCallvirt) { + switch (info.proxyCreatorType) { + case ProxyCreatorType.Newobj: + return OpCodes.Newobj; + + case ProxyCreatorType.CallOrCallvirt: + return isCallvirt ? OpCodes.Callvirt : OpCodes.Call; + + default: throw new NotImplementedException(); + } + } + + public void findDelegateCreator() { + var type = DotNetUtils.getModuleType(module); + foreach (var method in type.Methods) { + if (method.Body == null || !method.IsStatic || !method.IsAssembly) + continue; + if (!DotNetUtils.isMethod(method, "System.Void", "(System.String,System.RuntimeFieldHandle)")) + continue; + var proxyType = getProxyCreatorType(method); + if (proxyType == ProxyCreatorType.None) + continue; + setDelegateCreatorMethod(method); + methodToInfo.add(method, new ProxyCreatorInfo(method, proxyType)); + } + } + + static ProxyCreatorType getProxyCreatorType(MethodDefinition method) { + foreach (var instr in method.Body.Instructions) { + var field = instr.Operand as FieldReference; + if (field == null) + continue; + switch (field.FullName) { + case "System.Reflection.Emit.OpCode System.Reflection.Emit.OpCodes::Call": + case "System.Reflection.Emit.OpCode System.Reflection.Emit.OpCodes::Callvirt": + return ProxyCreatorType.CallOrCallvirt; + + case "System.Reflection.Emit.OpCode System.Reflection.Emit.OpCodes::Newobj": + return ProxyCreatorType.Newobj; + } + } + return ProxyCreatorType.None; + } + + public new void find() { + if (delegateCreatorMethods.Count == 0) + return; + var cctor = DotNetUtils.getModuleTypeCctor(module); + if (cctor == null) + return; + + Log.v("Finding all proxy delegates"); + + var delegateInfos = createDelegateInitInfos(cctor); + fieldToMethod = createFieldToMethodDictionary(cctor.DeclaringType); + if (delegateInfos.Count != fieldToMethod.Count) + throw new ApplicationException("Missing proxy delegates"); + var delegateToFields = new Dictionary>(); + foreach (var field in fieldToMethod.getKeys()) { + List list; + if (!delegateToFields.TryGetValue((TypeDefinition)field.FieldType, out list)) + delegateToFields[(TypeDefinition)field.FieldType] = list = new List(); + list.Add(field); + } + + foreach (var kv in delegateToFields) { + var type = kv.Key; + var fields = kv.Value; + + Log.v("Found proxy delegate: {0} ({1:X8})", Utils.removeNewlines(type), type.MetadataToken.ToInt32()); + RemovedDelegateCreatorCalls++; + + Log.indent(); + foreach (var field in fields) { + var proxyMethod = fieldToMethod.find(field); + if (proxyMethod == null) + continue; + var info = delegateInfos.find(field); + if (info == null) + throw new ApplicationException("Missing proxy info"); + + MethodReference calledMethod; + OpCode callOpcode; + getCallInfo(info, field, out calledMethod, out callOpcode); + + if (calledMethod == null) + continue; + add(proxyMethod, 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()); + } + Log.deIndent(); + delegateTypesDict[type] = true; + } + } + + FieldDefinitionAndDeclaringTypeDict createDelegateInitInfos(MethodDefinition method) { + var infos = new FieldDefinitionAndDeclaringTypeDict(); + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 2; i++) { + var ldstr = instrs[i]; + if (ldstr.OpCode.Code != Code.Ldstr) + continue; + var info = ldstr.Operand as string; + if (info == null) + continue; + + var ldtoken = instrs[i + 1]; + if (ldtoken.OpCode.Code != Code.Ldtoken) + continue; + var delegateField = ldtoken.Operand as FieldDefinition; + if (delegateField == null) + continue; + var delegateType = delegateField.FieldType as TypeDefinition; + if (!DotNetUtils.derivesFromDelegate(delegateType)) + continue; + + var call = instrs[i + 2]; + if (call.OpCode.Code != Code.Call) + continue; + var delegateCreatorMethod = call.Operand as MethodDefinition; + if (delegateCreatorMethod == null || !isDelegateCreatorMethod(delegateCreatorMethod)) + continue; + + infos.add(delegateField, new DelegateInitInfo(info, delegateField, delegateCreatorMethod)); + i += 2; + } + return infos; + } + + static FieldDefinitionAndDeclaringTypeDict createFieldToMethodDictionary(TypeDefinition type) { + var dict = new FieldDefinitionAndDeclaringTypeDict(); + foreach (var method in type.Methods) { + if (!method.IsStatic || method.Body == null || method.Name == ".cctor") + continue; + var delegateField = getDelegateField(method); + if (delegateField == null) + continue; + dict.add(delegateField, method); + } + return dict; + } + + static FieldDefinition getDelegateField(MethodDefinition method) { + if (method == null || method.Body == null) + return null; + + FieldDefinition field = null; + bool foundInvoke = false; + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code == Code.Ldsfld) { + var field2 = instr.Operand as FieldDefinition; + if (field2 == null || field2.DeclaringType != method.DeclaringType) + continue; + if (field != null) + return null; + if (!DotNetUtils.derivesFromDelegate(field2.FieldType as TypeDefinition)) + continue; + field = field2; + } + else if (instr.OpCode.Code == Code.Call || instr.OpCode.Code == Code.Callvirt) { + var calledMethod = instr.Operand as MethodReference; + foundInvoke |= calledMethod != null && calledMethod.Name == "Invoke"; + } + } + return foundInvoke ? field : null; + } + + public void cleanUp() { + if (!Detected) + return; + var cctor = DotNetUtils.getModuleTypeCctor(module); + if (cctor == null) + return; + cctor.Body.Instructions.Clear(); + cctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); + } + } +}