From 1fc70d8d9efda1fbe5d22ce63da8bb15716d27ab Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 29 Dec 2011 08:26:36 +0100 Subject: [PATCH] Add Goliath.NET obfuscator support --- de4dot.code/de4dot.code.csproj | 11 + .../Goliath_NET/ArrayDecrypter.cs | 47 +++ .../Goliath_NET/ArrayValueInliner.cs | 96 ++++++ .../Goliath_NET/DecrypterBase.cs | 283 ++++++++++++++++++ .../deobfuscators/Goliath_NET/Deobfuscator.cs | 278 +++++++++++++++++ .../Goliath_NET/IntegerDecrypter.cs | 48 +++ .../Goliath_NET/IntegerValueInliner.cs | 74 +++++ .../Goliath_NET/LocalsRestorer.cs | 148 +++++++++ .../Goliath_NET/LogicalExpressionFixer.cs | 45 +++ .../Goliath_NET/ProxyDelegateFinder.cs | 144 +++++++++ .../Goliath_NET/StringDecrypter.cs | 49 +++ .../Goliath_NET/StrongNameChecker.cs | 127 ++++++++ de4dot.cui/Program.cs | 1 + 13 files changed, 1351 insertions(+) create mode 100644 de4dot.code/deobfuscators/Goliath_NET/ArrayDecrypter.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/ArrayValueInliner.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/DecrypterBase.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/Deobfuscator.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/IntegerDecrypter.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/IntegerValueInliner.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/LocalsRestorer.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/LogicalExpressionFixer.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/ProxyDelegateFinder.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/StringDecrypter.cs create mode 100644 de4dot.code/deobfuscators/Goliath_NET/StrongNameChecker.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 187942ec..45283a08 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -104,6 +104,17 @@ + + + + + + + + + + + diff --git a/de4dot.code/deobfuscators/Goliath_NET/ArrayDecrypter.cs b/de4dot.code/deobfuscators/Goliath_NET/ArrayDecrypter.cs new file mode 100644 index 00000000..92992773 --- /dev/null +++ b/de4dot.code/deobfuscators/Goliath_NET/ArrayDecrypter.cs @@ -0,0 +1,47 @@ +/* + 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 Mono.Cecil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Goliath_NET { + class ArrayDecrypter : DecrypterBase { + public ArrayDecrypter(ModuleDefinition module) + : base(module) { + } + + protected override string[] getRequiredFieldTypes() { + return new string[] { + "System.Byte[]", + "System.Collections.Generic.Dictionary`2", + }; + } + + protected override bool checkDelegateInvokeMethod(MethodDefinition invokeMethod) { + return DotNetUtils.isMethod(invokeMethod, "System.Byte[]", "(System.Int32)"); + } + + public byte[] decrypt(MethodDefinition method) { + var info = getInfo(method); + decryptedReader.BaseStream.Position = info.offset; + return decryptedReader.ReadBytes(decryptedReader.ReadInt32()); + } + } +} diff --git a/de4dot.code/deobfuscators/Goliath_NET/ArrayValueInliner.cs b/de4dot.code/deobfuscators/Goliath_NET/ArrayValueInliner.cs new file mode 100644 index 00000000..8889dc73 --- /dev/null +++ b/de4dot.code/deobfuscators/Goliath_NET/ArrayValueInliner.cs @@ -0,0 +1,96 @@ +/* + 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.code.deobfuscators.Goliath_NET { + class ArrayValueInliner : MethodReturnValueInliner { + MethodDefinitionAndDeclaringTypeDict> intDecrypters = new MethodDefinitionAndDeclaringTypeDict>(); + InitializedDataCreator initializedDataCreator; + ModuleDefinition module; + MethodReference initializeArrayMethod; + + class MyCallResult : CallResult { + public MethodReference methodReference; + public MyCallResult(Block block, int callEndIndex, MethodReference method) + : base(block, callEndIndex) { + this.methodReference = method; + } + } + + public bool HasHandlers { + get { return intDecrypters.Count != 0; } + } + + public ArrayValueInliner(ModuleDefinition module, InitializedDataCreator initializedDataCreator) { + this.module = module; + this.initializedDataCreator = initializedDataCreator; + + var runtimeHelpersType = DotNetUtils.findOrCreateTypeReference(module, module.TypeSystem.Corlib as AssemblyNameReference, "System.Runtime.CompilerServices", "RuntimeHelpers", false); + initializeArrayMethod = new MethodReference("InitializeArray", module.TypeSystem.Void, runtimeHelpersType); + var systemArrayType = DotNetUtils.findOrCreateTypeReference(module, module.TypeSystem.Corlib as AssemblyNameReference, "System", "Array", false); + var runtimeFieldHandleType = DotNetUtils.findOrCreateTypeReference(module, module.TypeSystem.Corlib as AssemblyNameReference, "System", "RuntimeFieldHandle", true); + initializeArrayMethod.Parameters.Add(new ParameterDefinition(systemArrayType)); + initializeArrayMethod.Parameters.Add(new ParameterDefinition(runtimeFieldHandleType)); + } + + public void add(MethodDefinition method, Func handler) { + if (method == null) + return; + if (intDecrypters.find(method) != null) + throw new ApplicationException(string.Format("Handler for method {0:X8} has already been added", method.MetadataToken.ToInt32())); + intDecrypters.add(method, handler); + } + + protected override void inlineReturnValues(IList callResults) { + foreach (var callResult in callResults) { + var block = callResult.block; + int num = callResult.callEndIndex - callResult.callStartIndex + 1; + + var ary = (byte[])callResult.returnValue; + int index = callResult.callStartIndex; + block.replace(index++, num, DotNetUtils.createLdci4(ary.Length)); + block.insert(index++, Instruction.Create(OpCodes.Newarr, module.TypeSystem.Byte)); + block.insert(index++, Instruction.Create(OpCodes.Dup)); + block.insert(index++, Instruction.Create(OpCodes.Ldtoken, initializedDataCreator.create(ary))); + block.insert(index++, Instruction.Create(OpCodes.Call, initializeArrayMethod)); + + Log.v("Decrypted array: {0} bytes", ary.Length); + } + } + + protected override void inlineAllCalls() { + foreach (var tmp in callResults) { + var callResult = (MyCallResult)tmp; + var handler = intDecrypters.find(callResult.methodReference); + callResult.returnValue = handler((MethodDefinition)callResult.methodReference, callResult.args); + } + } + + protected override CallResult createCallResult(MethodReference method, Block block, int callInstrIndex) { + if (intDecrypters.find(method) == null) + return null; + return new MyCallResult(block, callInstrIndex, method); + } + } +} diff --git a/de4dot.code/deobfuscators/Goliath_NET/DecrypterBase.cs b/de4dot.code/deobfuscators/Goliath_NET/DecrypterBase.cs new file mode 100644 index 00000000..bd16c256 --- /dev/null +++ b/de4dot.code/deobfuscators/Goliath_NET/DecrypterBase.cs @@ -0,0 +1,283 @@ +/* + 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 System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Goliath_NET { + abstract class DecrypterBase { + ModuleDefinition module; + EmbeddedResource encryptedResource; + TypeDefinition decrypterType; + TypeDefinition delegateType; + TypeDefinition delegateInitType; + protected BinaryReader decryptedReader; + MethodDefinitionAndDeclaringTypeDict decrypterMethods = new MethodDefinitionAndDeclaringTypeDict(); + + protected class Info { + public MethodDefinition method; + public int offset; + public bool referenced = false; + public Info(MethodDefinition method, int offset) { + this.method = method; + this.offset = offset; + } + } + + public bool Detected { + get { return encryptedResource != null; } + } + + public Resource EncryptedResource { + get { return encryptedResource; } + } + + public TypeDefinition Type { + get { return decrypterType; } + } + + public TypeDefinition DelegateInitType { + get { return delegateInitType ?? findDelegateInitType();} + } + + public TypeDefinition DelegateType { + get { return delegateType; } + } + + public IEnumerable DecrypterTypes { + get { + var types = new TypeDefinitionDict(); + foreach (var info in decrypterMethods.getAll()) { + if (info.referenced) + types.add(info.method.DeclaringType, info.method.DeclaringType); + } + return types.getAll(); + } + } + + public DecrypterBase(ModuleDefinition module) { + this.module = module; + } + + protected Info getInfo(MethodDefinition method) { + var info = decrypterMethods.find(method); + if (info == null) + return null; + + info.referenced = true; + return info; + } + + protected abstract string[] getRequiredFieldTypes(); + + public void find() { + var requiredFields = getRequiredFieldTypes(); + foreach (var type in module.Types) { + var resourceName = type.FullName + ".resources"; + var resource = DotNetUtils.getResource(module, resourceName) as EmbeddedResource; + if (resource == null) + continue; + if (!new FieldTypes(type).exactly(requiredFields)) + continue; + + encryptedResource = resource; + decrypterType = type; + break; + } + } + + public void initialize() { + if (encryptedResource == null) + return; + + decryptedReader = new BinaryReader(new MemoryStream(decrypt(encryptedResource.GetResourceData()))); + + delegateType = null; + foreach (var type in module.GetTypes()) { + var cctor = DotNetUtils.getMethod(type, ".cctor"); + if (cctor == null) + continue; + + if (type.Fields.Count != 1) + continue; + var field = type.Fields[0]; + var tmpDelegateType = DotNetUtils.getType(module, field.FieldType); + if (tmpDelegateType == null) + continue; + + if (!checkDelegateType(tmpDelegateType)) + continue; + if (delegateType != null && delegateType != tmpDelegateType) + continue; + + if (!checkCctor(cctor)) + continue; + + delegateType = tmpDelegateType; + + foreach (var method in type.Methods) { + if (method.Name == ".cctor") + continue; + if (!method.IsStatic || method.Body == null) + continue; + if (method.Parameters.Count != 0) + continue; + if (method.MethodReturnType.ReturnType.FullName == "System.Void") + continue; + var info = getDecrypterInfo(method, field); + if (info == null) + continue; + + decrypterMethods.add(info.method, info); + } + } + } + + Info getDecrypterInfo(MethodDefinition method, FieldDefinition delegateField) { + try { + int index = 0; + var instrs = method.Body.Instructions; + if (instrs[index].OpCode.Code != Code.Ldsfld) + return null; + var field = instrs[index++].Operand as FieldDefinition; + if (field != delegateField) + return null; + + if (!DotNetUtils.isLdcI4(instrs[index])) + return null; + int offset = DotNetUtils.getLdcI4Value(instrs[index++]); + + if (instrs[index].OpCode.Code != Code.Call && instrs[index].OpCode.Code != Code.Callvirt) + return null; + var calledMethod = instrs[index++].Operand as MethodReference; + if (calledMethod.Name != "Invoke") + return null; + + if (instrs[index].OpCode.Code == Code.Unbox_Any) + index++; + + if (instrs[index++].OpCode.Code != Code.Ret) + return null; + + return new Info(method, offset); + } + catch (ArgumentOutOfRangeException) { + return null; + } + } + + bool checkCctor(MethodDefinition cctor) { + var ldtokenType = getLdtokenType(cctor); + if (!MemberReferenceHelper.compareTypes(ldtokenType, cctor.DeclaringType)) + return false; + + MethodDefinition initMethod = null; + foreach (var info in DotNetUtils.getCalledMethods(module, cctor)) { + var method = info.Item2; + if (DotNetUtils.isMethod(method, "System.Void", "(System.Type)")) { + initMethod = method; + break; + } + } + if (initMethod == null || initMethod.Body == null) + return false; + + return true; + } + + static TypeReference getLdtokenType(MethodDefinition method) { + if (method == null || method.Body == null) + return null; + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code != Code.Ldtoken) + continue; + return instr.Operand as TypeReference; + } + return null; + } + + bool checkDelegateType(TypeDefinition type) { + if (!DotNetUtils.derivesFromDelegate(type)) + return false; + var invoke = DotNetUtils.getMethod(type, "Invoke"); + if (invoke == null) + return false; + return checkDelegateInvokeMethod(invoke); + } + + protected abstract bool checkDelegateInvokeMethod(MethodDefinition invokeMethod); + + byte[] decrypt(byte[] encryptedData) { + const int KEY_LEN = 0x100; + if (encryptedData.Length < KEY_LEN) + throw new ApplicationException("Invalid encrypted data length"); + var decryptedData = new byte[encryptedData.Length - KEY_LEN]; + var pkt = module.Assembly.Name.PublicKeyToken; + if (pkt == null || pkt.Length == 0) + pkt = new byte[8]; + + for (int i = 0, j = 0, ki = 0; i < decryptedData.Length; i++) { + ki = (ki + 1) % (KEY_LEN - 1); + j = (j + encryptedData[ki] + pkt[i % 8]) % (KEY_LEN - 1); + var tmp = encryptedData[j]; + encryptedData[j] = encryptedData[ki]; + encryptedData[ki] = tmp; + decryptedData[i] = (byte)(encryptedData[KEY_LEN + i] ^ encryptedData[(encryptedData[j] + encryptedData[ki]) % (KEY_LEN - 1)]); + } + + return decryptedData; + } + + TypeDefinition findDelegateInitType() { + if (delegateType == null) + return null; + + foreach (var type in module.Types) { + if (type.HasProperties || type.HasEvents || type.HasFields) + continue; + + foreach (var method in type.Methods) { + if (!method.IsStatic || method.IsPrivate || method.Body == null) + continue; + var ldtokenType = getLdtokenType(method); + if (ldtokenType == null) + continue; + if (!MemberReferenceHelper.compareTypes(ldtokenType, delegateType)) + continue; + + delegateInitType = type; + return delegateInitType; + } + } + + return null; + } + + public IEnumerable getMethods() { + var list = new List(decrypterMethods.Count); + foreach (var info in decrypterMethods.getAll()) + list.Add(info.method); + return list; + } + } +} diff --git a/de4dot.code/deobfuscators/Goliath_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Goliath_NET/Deobfuscator.cs new file mode 100644 index 00000000..4a54a6e3 --- /dev/null +++ b/de4dot.code/deobfuscators/Goliath_NET/Deobfuscator.cs @@ -0,0 +1,278 @@ +/* + 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.Collections.Generic; +using Mono.Cecil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Goliath_NET { + public class DeobfuscatorInfo : DeobfuscatorInfoBase { + public const string THE_NAME = "Goliath.NET"; + public const string THE_TYPE = "go"; + const string DEFAULT_REGEX = @"!^[A-Za-z]{1,2}(?:`\d+)?$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX; + BoolOption inlineMethods; + BoolOption removeInlinedMethods; + BoolOption restoreLocals; + BoolOption decryptIntegers; + BoolOption decryptArrays; + BoolOption removeAntiStrongName; + + public DeobfuscatorInfo() + : base(DEFAULT_REGEX) { + inlineMethods = new BoolOption(null, makeArgName("inline"), "Inline short methods", true); + removeInlinedMethods = new BoolOption(null, makeArgName("remove-inlined"), "Remove inlined methods", true); + restoreLocals = new BoolOption(null, makeArgName("locals"), "Restore locals", true); + decryptIntegers = new BoolOption(null, makeArgName("ints"), "Decrypt integers", true); + decryptArrays = new BoolOption(null, makeArgName("arrays"), "Decrypt arrays", true); + removeAntiStrongName = new BoolOption(null, makeArgName("sn"), "Remove anti strong name code", true); + } + + public override string Name { + get { return THE_NAME; } + } + + public override string Type { + get { return THE_TYPE; } + } + + public override IDeobfuscator createDeobfuscator() { + return new Deobfuscator(new Deobfuscator.Options { + RenameResourcesInCode = false, + ValidNameRegex = validNameRegex.get(), + InlineMethods = inlineMethods.get(), + RemoveInlinedMethods = removeInlinedMethods.get(), + RestoreLocals = restoreLocals.get(), + DecryptIntegers = decryptIntegers.get(), + DecryptArrays = decryptArrays.get(), + RemoveAntiStrongName = removeAntiStrongName.get(), + }); + } + + protected override IEnumerable