From 1b1cf4ec60abdb12281e3ba7edd1c1c7419f7aaa Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 8 Jul 2012 08:12:41 +0200 Subject: [PATCH 01/15] Add another getMethod() for generics --- blocks/DotNetUtils.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/blocks/DotNetUtils.cs b/blocks/DotNetUtils.cs index 03462a8b..1934060f 100644 --- a/blocks/DotNetUtils.cs +++ b/blocks/DotNetUtils.cs @@ -458,11 +458,23 @@ namespace de4dot.blocks { } public static MethodDefinition getMethod(ModuleDefinition module, MethodReference method) { + if (method == null) + return null; + return getMethod(module, method, method.DeclaringType); + } + + public static MethodDefinition getMethod2(ModuleDefinition module, MethodReference method) { + if (method == null) + return null; + return getMethod(module, method, method.DeclaringType.GetElementType()); + } + + static MethodDefinition getMethod(ModuleDefinition module, MethodReference method, TypeReference declaringType) { if (method == null) return null; if (method is MethodDefinition) return (MethodDefinition)method; - return getMethod(getType(module, method.DeclaringType), method); + return getMethod(getType(module, declaringType), method); } public static MethodDefinition getMethod(TypeDefinition type, string returnType, string parameters) { From 9f8cac4daca8686c231bfaf34550d0ab465a9d85 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 8 Jul 2012 08:14:36 +0200 Subject: [PATCH 02/15] Fix #56 --- de4dot.code/deobfuscators/UnusedMethodsFinder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de4dot.code/deobfuscators/UnusedMethodsFinder.cs b/de4dot.code/deobfuscators/UnusedMethodsFinder.cs index 7f446da9..8fbc37f6 100644 --- a/de4dot.code/deobfuscators/UnusedMethodsFinder.cs +++ b/de4dot.code/deobfuscators/UnusedMethodsFinder.cs @@ -79,7 +79,7 @@ namespace de4dot.code.deobfuscators { continue; } - var calledMethod = DotNetUtils.getMethod(module, instr.Operand as MethodReference); + var calledMethod = DotNetUtils.getMethod2(module, instr.Operand as MethodReference); if (calledMethod == null) continue; if (possiblyUnusedMethods.ContainsKey(calledMethod)) From ca65972c64063d856bc4db60883537a0a89764d3 Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 16 Jul 2012 18:02:32 +0200 Subject: [PATCH 03/15] Add a force option to deobfuscate() method --- de4dot.code/ObfuscatedFile.cs | 6 +++++- de4dot.code/deobfuscators/ISimpleDeobfuscator.cs | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index 15c38078..e120a1ef 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -731,7 +731,11 @@ namespace de4dot.code { } void ISimpleDeobfuscator.deobfuscate(MethodDefinition method) { - if (check(method, SimpleDeobFlags.HasDeobfuscated)) + ((ISimpleDeobfuscator)this).deobfuscate(method, false); + } + + void ISimpleDeobfuscator.deobfuscate(MethodDefinition method, bool force) { + if (!force && check(method, SimpleDeobFlags.HasDeobfuscated)) return; deobfuscate(method, "Deobfuscating control flow", (blocks) => { diff --git a/de4dot.code/deobfuscators/ISimpleDeobfuscator.cs b/de4dot.code/deobfuscators/ISimpleDeobfuscator.cs index 527bcf3d..6b028100 100644 --- a/de4dot.code/deobfuscators/ISimpleDeobfuscator.cs +++ b/de4dot.code/deobfuscators/ISimpleDeobfuscator.cs @@ -22,6 +22,7 @@ using Mono.Cecil; namespace de4dot.code.deobfuscators { public interface ISimpleDeobfuscator { void deobfuscate(MethodDefinition method); + void deobfuscate(MethodDefinition method, bool force); void decryptStrings(MethodDefinition method, IDeobfuscator deob); } } From 6766c10969bf3af9936c37ae3457cf8ac6708fec Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 16 Jul 2012 18:04:20 +0200 Subject: [PATCH 04/15] Split array state into a new class --- de4dot.code/de4dot.code.csproj | 1 + .../DeepSea/ArrayBlockDeobfuscator.cs | 157 +-------------- .../deobfuscators/DeepSea/ArrayBlockState.cs | 190 ++++++++++++++++++ .../deobfuscators/DeepSea/Deobfuscator.cs | 12 +- 4 files changed, 205 insertions(+), 155 deletions(-) create mode 100644 de4dot.code/deobfuscators/DeepSea/ArrayBlockState.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index f1f330ca..5fdbf208 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -67,6 +67,7 @@ + diff --git a/de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs b/de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs index ac100559..79bf6340 100644 --- a/de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs +++ b/de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs @@ -20,73 +20,17 @@ using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; -using Mono.Cecil.Metadata; using de4dot.blocks; using de4dot.blocks.cflow; namespace de4dot.code.deobfuscators.DeepSea { class ArrayBlockDeobfuscator : BlockDeobfuscator { - ModuleDefinition module; - FieldDefinitionAndDeclaringTypeDict fieldToInfo = new FieldDefinitionAndDeclaringTypeDict(); - Dictionary localToInfo = new Dictionary(); + ArrayBlockState arrayBlockState; + Dictionary localToInfo = new Dictionary(); DsConstantsReader constantsReader; - class FieldInfo { - public readonly FieldDefinition field; - public readonly FieldDefinition arrayInitField; - public readonly byte[] array; - - public FieldInfo(FieldDefinition field, FieldDefinition arrayInitField) { - this.field = field; - this.arrayInitField = arrayInitField; - this.array = (byte[])arrayInitField.InitialValue.Clone(); - } - } - - public bool Detected { - get { return fieldToInfo.Count != 0; } - } - - public ArrayBlockDeobfuscator(ModuleDefinition module) { - this.module = module; - } - - public void init() { - initializeArrays(DotNetUtils.getModuleTypeCctor(module)); - } - - void initializeArrays(MethodDefinition method) { - if (method == null || method.Body == null) - return; - - var instructions = method.Body.Instructions; - for (int i = 0; i < instructions.Count; i++) { - var ldci4 = instructions[i]; - if (!DotNetUtils.isLdcI4(ldci4)) - continue; - i++; - var instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Newarr, OpCodes.Dup, OpCodes.Ldtoken, OpCodes.Call, OpCodes.Stsfld); - if (instrs == null) - continue; - - var arrayType = instrs[0].Operand as TypeReference; - if (arrayType == null || arrayType.EType != ElementType.U1) - continue; - - var arrayInitField = instrs[2].Operand as FieldDefinition; - if (arrayInitField == null || arrayInitField.InitialValue == null || arrayInitField.InitialValue.Length == 0) - continue; - - var calledMethod = instrs[3].Operand as MethodReference; - if (calledMethod == null || calledMethod.FullName != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)") - continue; - - var targetField = instrs[4].Operand as FieldDefinition; - if (targetField == null) - continue; - - fieldToInfo.add(targetField, new FieldInfo(targetField, arrayInitField)); - } + public ArrayBlockDeobfuscator(ArrayBlockState arrayBlockState) { + this.arrayBlockState = arrayBlockState; } public override void deobfuscateBegin(Blocks blocks) { @@ -107,7 +51,7 @@ namespace de4dot.code.deobfuscators.DeepSea { if (!stloc.isStloc()) continue; - var info = fieldToInfo.find((FieldReference)ldsfld.Operand); + var info = arrayBlockState.getFieldInfo((FieldReference)ldsfld.Operand); if (info == null) continue; var local = DotNetUtils.getLocalVar(blocks.Locals, stloc.Instruction); @@ -158,7 +102,7 @@ namespace de4dot.code.deobfuscators.DeepSea { var local = DotNetUtils.getLocalVar(blocks.Locals, ldloc.Instruction); if (local == null) return false; - FieldInfo info; + ArrayBlockState.FieldInfo info; if (!localToInfo.TryGetValue(local, out info)) return false; @@ -183,7 +127,7 @@ namespace de4dot.code.deobfuscators.DeepSea { var ldsfld = instrs[i]; if (ldsfld.OpCode.Code != Code.Ldsfld) return false; - var info = fieldToInfo.find(ldsfld.Operand as FieldReference); + var info = arrayBlockState.getFieldInfo(ldsfld.Operand as FieldReference); if (info == null) return false; @@ -209,7 +153,7 @@ namespace de4dot.code.deobfuscators.DeepSea { var ldsfld = instrs[i]; if (ldsfld.OpCode.Code != Code.Ldsfld) return false; - var info = fieldToInfo.find(ldsfld.Operand as FieldReference); + var info = arrayBlockState.getFieldInfo(ldsfld.Operand as FieldReference); if (info == null) return false; @@ -237,90 +181,5 @@ namespace de4dot.code.deobfuscators.DeepSea { return constantsReader; return constantsReader = new DsConstantsReader(block.Instructions); } - - public IEnumerable cleanUp() { - var removedFields = new List(); - var moduleCctor = DotNetUtils.getModuleTypeCctor(module); - if (moduleCctor == null) - return removedFields; - var moduleCctorBlocks = new Blocks(moduleCctor); - - var keep = findFieldsToKeep(); - foreach (var fieldInfo in fieldToInfo.getValues()) { - if (keep.ContainsKey(fieldInfo)) - continue; - if (removeInitCode(moduleCctorBlocks, fieldInfo)) { - removedFields.Add(fieldInfo.field); - removedFields.Add(fieldInfo.arrayInitField); - } - fieldInfo.arrayInitField.InitialValue = new byte[1]; - fieldInfo.arrayInitField.FieldType = module.TypeSystem.Byte; - } - - IList allInstructions; - IList allExceptionHandlers; - moduleCctorBlocks.getCode(out allInstructions, out allExceptionHandlers); - DotNetUtils.restoreBody(moduleCctorBlocks.Method, allInstructions, allExceptionHandlers); - return removedFields; - } - - bool removeInitCode(Blocks blocks, FieldInfo info) { - bool removedSomething = false; - foreach (var block in blocks.MethodBlocks.getAllBlocks()) { - var instrs = block.Instructions; - for (int i = 0; i < instrs.Count - 5; i++) { - var ldci4 = instrs[i]; - if (!ldci4.isLdcI4()) - continue; - if (instrs[i + 1].OpCode.Code != Code.Newarr) - continue; - if (instrs[i + 2].OpCode.Code != Code.Dup) - continue; - var ldtoken = instrs[i + 3]; - if (ldtoken.OpCode.Code != Code.Ldtoken) - continue; - if (ldtoken.Operand != info.arrayInitField) - continue; - var call = instrs[i + 4]; - if (call.OpCode.Code != Code.Call) - continue; - var calledMethod = call.Operand as MethodReference; - if (calledMethod == null || calledMethod.FullName != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)") - continue; - var stsfld = instrs[i + 5]; - if (stsfld.OpCode.Code != Code.Stsfld) - continue; - if (stsfld.Operand != info.field) - continue; - block.remove(i, 6); - i--; - removedSomething = true; - } - } - return removedSomething; - } - - Dictionary findFieldsToKeep() { - var keep = new Dictionary(); - foreach (var type in module.GetTypes()) { - foreach (var method in type.Methods) { - if (type == DotNetUtils.getModuleType(module) && method.Name == ".cctor") - continue; - if (method.Body == null) - continue; - - foreach (var instr in method.Body.Instructions) { - var field = instr.Operand as FieldReference; - if (field == null) - continue; - var fieldInfo = fieldToInfo.find(field); - if (fieldInfo == null) - continue; - keep[fieldInfo] = true; - } - } - } - return keep; - } } } diff --git a/de4dot.code/deobfuscators/DeepSea/ArrayBlockState.cs b/de4dot.code/deobfuscators/DeepSea/ArrayBlockState.cs new file mode 100644 index 00000000..383129ee --- /dev/null +++ b/de4dot.code/deobfuscators/DeepSea/ArrayBlockState.cs @@ -0,0 +1,190 @@ +/* + 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.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Metadata; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.DeepSea { + class ArrayBlockState { + ModuleDefinition module; + FieldDefinitionAndDeclaringTypeDict fieldToInfo = new FieldDefinitionAndDeclaringTypeDict(); + + public class FieldInfo { + public readonly FieldDefinition field; + public readonly FieldDefinition arrayInitField; + public readonly byte[] array; + + public FieldInfo(FieldDefinition field, FieldDefinition arrayInitField) { + this.field = field; + this.arrayInitField = arrayInitField; + this.array = (byte[])arrayInitField.InitialValue.Clone(); + } + } + + public bool Detected { + get { return fieldToInfo.Count != 0; } + } + + public ArrayBlockState(ModuleDefinition module) { + this.module = module; + } + + public void init(ISimpleDeobfuscator simpleDeobfuscator) { + initializeArrays(simpleDeobfuscator, DotNetUtils.getModuleTypeCctor(module)); + } + + void initializeArrays(ISimpleDeobfuscator simpleDeobfuscator, MethodDefinition method) { + if (method == null || method.Body == null) + return; + while (initializeArrays2(simpleDeobfuscator, method)) { + } + } + + bool initializeArrays2(ISimpleDeobfuscator simpleDeobfuscator, MethodDefinition method) { + bool foundField = false; + simpleDeobfuscator.deobfuscate(method, true); + var instructions = method.Body.Instructions; + for (int i = 0; i < instructions.Count; i++) { + var ldci4 = instructions[i]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + i++; + var instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Newarr, OpCodes.Dup, OpCodes.Ldtoken, OpCodes.Call, OpCodes.Stsfld); + if (instrs == null) + continue; + + var arrayType = instrs[0].Operand as TypeReference; + if (arrayType == null || arrayType.EType != ElementType.U1) + continue; + + var arrayInitField = instrs[2].Operand as FieldDefinition; + if (arrayInitField == null || arrayInitField.InitialValue == null || arrayInitField.InitialValue.Length == 0) + continue; + + var calledMethod = instrs[3].Operand as MethodReference; + if (calledMethod == null || calledMethod.FullName != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)") + continue; + + var targetField = instrs[4].Operand as FieldDefinition; + if (targetField == null) + continue; + + if (fieldToInfo.find(targetField) == null) { + fieldToInfo.add(targetField, new FieldInfo(targetField, arrayInitField)); + foundField = true; + } + } + return foundField; + } + + public FieldInfo getFieldInfo(FieldReference fieldRef) { + if (fieldRef == null) + return null; + return fieldToInfo.find(fieldRef); + } + + public IEnumerable cleanUp() { + var removedFields = new List(); + var moduleCctor = DotNetUtils.getModuleTypeCctor(module); + if (moduleCctor == null) + return removedFields; + var moduleCctorBlocks = new Blocks(moduleCctor); + + var keep = findFieldsToKeep(); + foreach (var fieldInfo in fieldToInfo.getValues()) { + if (keep.ContainsKey(fieldInfo)) + continue; + if (removeInitCode(moduleCctorBlocks, fieldInfo)) { + removedFields.Add(fieldInfo.field); + removedFields.Add(fieldInfo.arrayInitField); + } + fieldInfo.arrayInitField.InitialValue = new byte[1]; + fieldInfo.arrayInitField.FieldType = module.TypeSystem.Byte; + } + + IList allInstructions; + IList allExceptionHandlers; + moduleCctorBlocks.getCode(out allInstructions, out allExceptionHandlers); + DotNetUtils.restoreBody(moduleCctorBlocks.Method, allInstructions, allExceptionHandlers); + return removedFields; + } + + bool removeInitCode(Blocks blocks, FieldInfo info) { + bool removedSomething = false; + foreach (var block in blocks.MethodBlocks.getAllBlocks()) { + var instrs = block.Instructions; + for (int i = 0; i < instrs.Count - 5; i++) { + var ldci4 = instrs[i]; + if (!ldci4.isLdcI4()) + continue; + if (instrs[i + 1].OpCode.Code != Code.Newarr) + continue; + if (instrs[i + 2].OpCode.Code != Code.Dup) + continue; + var ldtoken = instrs[i + 3]; + if (ldtoken.OpCode.Code != Code.Ldtoken) + continue; + if (ldtoken.Operand != info.arrayInitField) + continue; + var call = instrs[i + 4]; + if (call.OpCode.Code != Code.Call) + continue; + var calledMethod = call.Operand as MethodReference; + if (calledMethod == null || calledMethod.FullName != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)") + continue; + var stsfld = instrs[i + 5]; + if (stsfld.OpCode.Code != Code.Stsfld) + continue; + if (stsfld.Operand != info.field) + continue; + block.remove(i, 6); + i--; + removedSomething = true; + } + } + return removedSomething; + } + + Dictionary findFieldsToKeep() { + var keep = new Dictionary(); + foreach (var type in module.GetTypes()) { + foreach (var method in type.Methods) { + if (type == DotNetUtils.getModuleType(module) && method.Name == ".cctor") + continue; + if (method.Body == null) + continue; + + foreach (var instr in method.Body.Instructions) { + var field = instr.Operand as FieldReference; + if (field == null) + continue; + var fieldInfo = fieldToInfo.find(field); + if (fieldInfo == null) + continue; + keep[fieldInfo] = true; + } + } + } + return keep; + } + } +} diff --git a/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs b/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs index 7eac25c9..00f82d86 100644 --- a/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs @@ -88,7 +88,7 @@ namespace de4dot.code.deobfuscators.DeepSea { ResourceResolver resourceResolver; AssemblyResolver assemblyResolver; FieldsRestorer fieldsRestorer; - ArrayBlockDeobfuscator arrayBlockDeobfuscator; + ArrayBlockState arrayBlockState; internal class Options : OptionsBase { public bool InlineMethods { get; set; } @@ -127,8 +127,8 @@ namespace de4dot.code.deobfuscators.DeepSea { List getBlocksDeobfuscators() { var list = new List(); - if (arrayBlockDeobfuscator.Detected) - list.Add(arrayBlockDeobfuscator); + if (arrayBlockState != null && arrayBlockState.Detected) + list.Add(new ArrayBlockDeobfuscator(arrayBlockState)); if (!startedDeobfuscating || options.CastDeobfuscation) list.Add(new CastDeobfuscator()); return list; @@ -158,8 +158,8 @@ namespace de4dot.code.deobfuscators.DeepSea { protected override void scanForObfuscator() { staticStringInliner.UseUnknownArgs = true; - arrayBlockDeobfuscator = new ArrayBlockDeobfuscator(module); - arrayBlockDeobfuscator.init(); + arrayBlockState = new ArrayBlockState(module); + arrayBlockState.init(DeobfuscatedFile); stringDecrypter = new StringDecrypter(module); stringDecrypter.find(DeobfuscatedFile); resourceResolver = new ResourceResolver(module, DeobfuscatedFile, this); @@ -278,7 +278,7 @@ done: stringDecrypter.cleanup(); } - addFieldsToBeRemoved(arrayBlockDeobfuscator.cleanUp(), "Control flow obfuscation array"); + addFieldsToBeRemoved(arrayBlockState.cleanUp(), "Control flow obfuscation array"); base.deobfuscateEnd(); } From d0712b46aaa1e996aabff5b5b886086e6cf7b326 Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 16 Jul 2012 20:00:37 +0200 Subject: [PATCH 05/15] Update detection of resource resolver class --- de4dot.code/deobfuscators/DeepSea/ResolverBase.cs | 1 + de4dot.code/deobfuscators/DeepSea/ResourceResolver.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/de4dot.code/deobfuscators/DeepSea/ResolverBase.cs b/de4dot.code/deobfuscators/DeepSea/ResolverBase.cs index df8dd39c..6648e6fe 100644 --- a/de4dot.code/deobfuscators/DeepSea/ResolverBase.cs +++ b/de4dot.code/deobfuscators/DeepSea/ResolverBase.cs @@ -90,6 +90,7 @@ namespace de4dot.code.deobfuscators.DeepSea { } bool checkResolverInitMethodDesktop(MethodDefinition resolverInitMethod) { + simpleDeobfuscator.deobfuscate(resolverInitMethod); if (!checkResolverInitMethodInternal(resolverInitMethod)) return false; diff --git a/de4dot.code/deobfuscators/DeepSea/ResourceResolver.cs b/de4dot.code/deobfuscators/DeepSea/ResourceResolver.cs index f8c623fa..aeeeb64a 100644 --- a/de4dot.code/deobfuscators/DeepSea/ResourceResolver.cs +++ b/de4dot.code/deobfuscators/DeepSea/ResourceResolver.cs @@ -123,6 +123,8 @@ namespace de4dot.code.deobfuscators.DeepSea { var calledMethod = instr.Operand as MethodDefinition; if (calledMethod == null) continue; + if (getLdtokenField(calledMethod) == null) + continue; var args = DsUtils.getArgValues(instrs, i); if (args == null) continue; From 9b71da3633b4cefe32f0a7984c679c6519eb02a6 Mon Sep 17 00:00:00 2001 From: de4dot Date: Wed, 18 Jul 2012 14:39:27 +0200 Subject: [PATCH 06/15] Remove call to InitializeArray --- .../deobfuscators/DeepSea/StringDecrypter.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/de4dot.code/deobfuscators/DeepSea/StringDecrypter.cs b/de4dot.code/deobfuscators/DeepSea/StringDecrypter.cs index 3eb6a3f7..0399f891 100644 --- a/de4dot.code/deobfuscators/DeepSea/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/DeepSea/StringDecrypter.cs @@ -127,6 +127,29 @@ namespace de4dot.code.deobfuscators.DeepSea { return false; } + static void removeInitializeArrayCall(MethodDefinition method, FieldDefinition field) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 1; i++) { + var ldtoken = instrs[i]; + if (ldtoken.OpCode.Code != Code.Ldtoken) + continue; + if (ldtoken.Operand != field) + continue; + + var call = instrs[i + 1]; + if (call.OpCode.Code != Code.Call) + continue; + var calledMethod = call.Operand as MethodReference; + if (calledMethod == null) + continue; + if (calledMethod.ToString() != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)") + continue; + + instrs[i] = Instruction.Create(OpCodes.Pop); + instrs[i + 1] = Instruction.Create(OpCodes.Nop); + } + } + class DecrypterInfo41 : IDecrypterInfo { MethodDefinition cctor; int magic; @@ -358,6 +381,7 @@ namespace de4dot.code.deobfuscators.DeepSea { public void cleanup() { arrayInfo.initField.InitialValue = new byte[1]; arrayInfo.initField.FieldType = arrayInfo.initField.Module.TypeSystem.Byte; + removeInitializeArrayCall(cctor, arrayInfo.initField); } } @@ -516,6 +540,7 @@ namespace de4dot.code.deobfuscators.DeepSea { public void cleanup() { encryptedDataField.InitialValue = new byte[1]; encryptedDataField.FieldType = encryptedDataField.Module.TypeSystem.Byte; + removeInitializeArrayCall(cctor, encryptedDataField); } } From d9b3a81ba905f64248738f523952b35f13fa46d2 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 20 Jul 2012 14:47:55 +0200 Subject: [PATCH 07/15] Add little endian encrypt/decrypt methods --- de4dot.code/deobfuscators/Blowfish.cs | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/de4dot.code/deobfuscators/Blowfish.cs b/de4dot.code/deobfuscators/Blowfish.cs index 6dff1461..02cde9f4 100644 --- a/de4dot.code/deobfuscators/Blowfish.cs +++ b/de4dot.code/deobfuscators/Blowfish.cs @@ -318,6 +318,22 @@ namespace de4dot.code.deobfuscators { } } + public void encrypt_LE(byte[] data) { + for (int i = 0; i + 8 <= data.Length; i += 8) { + uint xl = BitConverter.ToUInt32(data, i); + uint xr = BitConverter.ToUInt32(data, i + 4); + encrypt(ref xl, ref xr); + data[i] = (byte)xl; + data[i + 1] = (byte)(xl >> 8); + data[i + 2] = (byte)(xl >> 16); + data[i + 3] = (byte)(xl >> 24); + data[i + 4] = (byte)xr; + data[i + 5] = (byte)(xr >> 8); + data[i + 6] = (byte)(xr >> 16); + data[i + 7] = (byte)(xr >> 24); + } + } + public void encrypt(byte[] data) { for (int i = 0; i + 8 <= data.Length; i += 8) { uint xl = (uint)((data[i] << 24) | (data[i + 1] << 16) | (data[i + 2] << 8) | data[i + 3]); @@ -347,6 +363,22 @@ namespace de4dot.code.deobfuscators { rxl = xr ^ P[17]; } + public void decrypt_LE(byte[] data) { + for (int i = 0; i + 8 <= data.Length; i += 8) { + uint xl = BitConverter.ToUInt32(data, i); + uint xr = BitConverter.ToUInt32(data, i + 4); + decrypt(ref xl, ref xr); + data[i] = (byte)xl; + data[i + 1] = (byte)(xl >> 8); + data[i + 2] = (byte)(xl >> 16); + data[i + 3] = (byte)(xl >> 24); + data[i + 4] = (byte)xr; + data[i + 5] = (byte)(xr >> 8); + data[i + 6] = (byte)(xr >> 16); + data[i + 7] = (byte)(xr >> 24); + } + } + public void decrypt(byte[] data) { for (int i = 0; i + 8 <= data.Length; i += 8) { uint xl = (uint)((data[i] << 24) | (data[i + 1] << 16) | (data[i + 2] << 8) | data[i + 3]); From 1eaa9f8c518476c90a108ce73de4f1c94b5813f4 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 20 Jul 2012 14:48:19 +0200 Subject: [PATCH 08/15] Add verify methods --- de4dot.code/deobfuscators/MethodBodyParser.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/de4dot.code/deobfuscators/MethodBodyParser.cs b/de4dot.code/deobfuscators/MethodBodyParser.cs index 204c77ae..2163e4a1 100644 --- a/de4dot.code/deobfuscators/MethodBodyParser.cs +++ b/de4dot.code/deobfuscators/MethodBodyParser.cs @@ -48,6 +48,25 @@ namespace de4dot.code.deobfuscators { } } + public static bool verify(byte[] data) { + return verify(new BinaryReader(new MemoryStream(data))); + } + + public static bool verify(Stream data) { + return verify(new BinaryReader(data)); + } + + public static bool verify(BinaryReader reader) { + try { + byte[] code, extraSections; + parseMethodBody(reader, out code, out extraSections); + return true; + } + catch (InvalidMethodBody) { + return false; + } + } + static MethodBodyHeader parseMethodBody2(BinaryReader reader, out byte[] code, out byte[] extraSections) { var mbHeader = new MethodBodyHeader(); From 8b82f8b47dfbd25114737a21ab24028afb4d8155 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 20 Jul 2012 14:49:47 +0200 Subject: [PATCH 09/15] Support the latest MC versions --- de4dot.code/de4dot.code.csproj | 2 + .../deobfuscators/MaxtoCode/CryptDecrypter.cs | 375 ++++++++++++++++++ .../deobfuscators/MaxtoCode/Decrypter6.cs | 123 ++++++ .../deobfuscators/MaxtoCode/FileDecrypter.cs | 192 ++++++--- 4 files changed, 640 insertions(+), 52 deletions(-) create mode 100644 de4dot.code/deobfuscators/MaxtoCode/CryptDecrypter.cs create mode 100644 de4dot.code/deobfuscators/MaxtoCode/Decrypter6.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 5fdbf208..195abef7 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -68,6 +68,8 @@ + + diff --git a/de4dot.code/deobfuscators/MaxtoCode/CryptDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/CryptDecrypter.cs new file mode 100644 index 00000000..7cdfa911 --- /dev/null +++ b/de4dot.code/deobfuscators/MaxtoCode/CryptDecrypter.cs @@ -0,0 +1,375 @@ +/* + 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; + +namespace de4dot.code.deobfuscators.MaxtoCode { + class CryptDecrypter { + byte[] key; + + static readonly byte[] sbox = new byte[8 * 8 * 8] { + 14, 4, 13, 1, 2, 15, 11, 8, + 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, + 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, + 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, + 5, 11, 3, 14, 10, 0, 6, 13, + 15, 1, 8, 14, 6, 11, 3, 4, + 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, + 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, + 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, + 11, 6, 7, 12, 0, 5, 14, 9, + 10, 0, 9, 14, 6, 3, 15, 5, + 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, + 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, + 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, + 4, 15, 14, 3, 11, 5, 2, 12, + 7, 13, 14, 3, 0, 6, 9, 10, + 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, + 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, + 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, + 9, 4, 5, 11, 12, 7, 2, 14, + 2, 12, 4, 1, 7, 10, 11, 6, + 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, + 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, + 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, + 6, 15, 0, 9, 10, 4, 5, 3, + 12, 1, 10, 15, 9, 2, 6, 8, + 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, + 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, + 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, + 11, 14, 1, 7, 6, 0, 8, 13, + 4, 11, 2, 14, 15, 0, 8, 13, + 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, + 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, + 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, + 9, 5, 0, 15, 14, 2, 3, 12, + 13, 2, 8, 4, 6, 15, 11, 1, + 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, + 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, + 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, + 15, 12, 9, 0, 3, 5, 6, 11, + }; + static readonly byte[] perm = new byte[32] { + 16, 7, 20, 21, 29, 12, 28, 17, + 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, + 19, 13, 30, 6, 22, 11, 4, 25, + }; + static readonly byte[] esel = new byte[48] { + 32, 1, 2, 3, 4, 5, 4, 5, + 6, 7, 8, 9, 8, 9, 10, 11, + 12, 13, 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, 20, 21, + 22, 23, 24, 25, 24, 25, 26, 27, + 28, 29, 28, 29, 30, 31, 32, 1, + }; + static readonly byte[] ip = new byte[64] { + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7, + }; + static readonly byte[] final = new byte[64] { + 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25, + }; + static readonly byte[] pc1 = new byte[56] { + 57, 49, 41, 33, 25, 17, 9, 1, + 58, 50, 42, 34, 26, 18, 10, 2, + 59, 51, 43, 35, 27, 19, 11, 3, + 60, 52, 44, 36, 63, 55, 47, 39, + 31, 23, 15, 7, 62, 54, 46, 38, + 30, 22, 14, 6, 61, 53, 45, 37, + 29, 21, 13, 5, 28, 20, 12, 4, + }; + static readonly byte[] pc2 = new byte[48] { + 14, 17, 11, 24, 1, 5, 3, 28, + 15, 6, 21, 10, 23, 19, 12, 4, + 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, + 51, 45, 33, 48, 44, 49, 39, 56, + 34, 53, 46, 42, 50, 36, 29, 32, + }; + static readonly byte[] rots = new byte[16] { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, + }; + + struct Bits { + readonly byte[] byteBits; + + public static Bits fromBytes(byte[] bytes) { + return fromBytes(bytes, 0, bytes.Length * 8); + } + + public static Bits fromBytes(byte[] bytes, int index, int numBits) { + return new Bits(bytes, index, numBits); + } + + public static Bits fromByteBits(byte[] byteBits1, byte[] byteBits2) { + return new Bits(byteBits1, byteBits2); + } + + public static Bits fromByteBits(byte[] byteBits) { + return fromByteBits(byteBits, 0, byteBits.Length); + } + + public static Bits fromByteBits(byte[] byteBits, int index, int numBits) { + var bits = new Bits(numBits); + for (int i = 0; i < numBits; i++) + bits.byteBits[i] = byteBits[index + i]; + return bits; + } + + public byte this[int index] { + get { return byteBits[index]; } + } + + public byte[] ByteBits { + get { return byteBits; } + } + + Bits(int numBits) { + this.byteBits = new byte[numBits]; + } + + Bits(byte[] bytes1, byte[] bytes2) { + this.byteBits = concat(bytes1, bytes2); + } + + Bits(byte[] bytes, int index, int numBits) { + this.byteBits = toByteBits(bytes, index, numBits); + } + + static byte[] toByteBits(byte[] bytes, int index, int numBits) { + var byteBits = new byte[numBits]; + for (int i = 0; i < numBits; i++) { + int j = i / 8; + int k = i & 7; + byteBits[i] = (byte)(((bytes[index + j] >> k) & 1) != 0 ? 1 : 0); + } + return byteBits; + } + + static byte[] concat(byte[] bytes1, byte[] bytes2) { + var bytes = new byte[bytes1.Length + bytes2.Length]; + Array.Copy(bytes1, 0, bytes, 0, bytes1.Length); + Array.Copy(bytes2, 0, bytes, bytes1.Length, bytes2.Length); + return bytes; + } + + public Bits transpose(byte[] bits) { + var result = new Bits(bits.Length); + for (int i = 0; i < bits.Length; i++) + result.byteBits[i] = byteBits[bits[i] - 1]; + return result; + } + + public void rol() { + if (byteBits.Length == 0) + return; + var first = byteBits[0]; + for (int i = 1; i < byteBits.Length; i++) + byteBits[i - 1] = byteBits[i]; + byteBits[byteBits.Length - 1] = first; + } + + public void rol(int num) { + for (int i = 0; i < num; i++) + rol(); + } + + public Bits extract(int index, int numBits) { + return fromByteBits(byteBits, index, numBits); + } + + public void toBits(byte[] dest, int index) { + var bits = toBits(); + Array.Copy(bits, 0, dest, index, bits.Length); + } + + public byte[] toBits() { + var bits = new byte[(byteBits.Length + 7) / 8]; + for (int i = 0; i < bits.Length; i++) { + byte val = 0; + for (int j = i * 8, k = 1; j < byteBits.Length; j++, k <<= 1) { + if (byteBits[j] != 0) + val |= (byte)k; + } + bits[i] = val; + } + return bits; + } + + public Bits clone() { + return fromByteBits(byteBits, 0, byteBits.Length); + } + + public void set(int destIndex, Bits other) { + for (int i = 0; i < other.byteBits.Length; i++) + byteBits[destIndex + i] = other.byteBits[i]; + } + + public void xor(Bits other) { + if (byteBits.Length != other.byteBits.Length) + throw new ArgumentException("other"); + for (int i = 0; i < byteBits.Length; i++) + byteBits[i] ^= other.byteBits[i]; + } + + public void copyTo(byte[] dest, int index) { + for (int i = 0; i < byteBits.Length; i++) + dest[index + i] = byteBits[i]; + } + } + + public CryptDecrypter(byte[] key) { + if (key.Length <= 8) + throw new ArgumentException("Invalid size", "key"); + this.key = key; + } + + public static byte[] decrypt(byte[] key, byte[] encrypted) { + return new CryptDecrypter(key).decrypt(encrypted); + } + + byte[] decrypt(byte[] encrypted) { + if (encrypted.Length % 8 != 0) + throw new ArgumentException("encrypted"); + var key1 = createKey(key, 0); + var key2 = createKey(key, 8); + + var decrypted = new byte[encrypted.Length]; + int count = encrypted.Length / 8; + for (int i = 0; i < count; i++) { + var buf = new byte[8]; + Array.Copy(encrypted, i * 8, buf, 0, buf.Length); + buf = decrypt(buf, key1, true); + buf = decrypt(buf, key2, false); + buf = decrypt(buf, key1, true); + Array.Copy(buf, 0, decrypted, i * 8, buf.Length); + } + + return decrypted; + } + + byte[] decrypt(byte[] data, Bits key, bool flag) { + var bits = Bits.fromBytes(data).transpose(ip); + + if (flag) { + for (int i = 0, ki = key.ByteBits.Length - 48; i < 16; i++, ki -= 48) { + var oldBits = bits.extract(0, 32); + var tmp = decrypt(oldBits.clone(), key.extract(ki, 48)); + tmp.xor(bits.extract(32, 32)); + bits.set(32, oldBits); + bits.set(0, tmp); + } + } + else { + for (int i = 0, ki = 0; i < 16; i++, ki += 48) { + var oldBits = bits.extract(32, 32); + var tmp = decrypt(oldBits.clone(), key.extract(ki, 48)); + tmp.xor(bits.extract(0, 32)); + bits.set(0, oldBits); + bits.set(32, tmp); + } + } + + bits = bits.transpose(final); + return bits.toBits(); + } + + Bits decrypt(Bits data, Bits key) { + var newData = data.clone().transpose(esel); + newData.xor(key); + return Bits.fromByteBits(getSbox(newData)).transpose(perm); + } + + byte[] getSbox(Bits data) { + var sboxByteBits = new byte[32]; + + for (int i = 0; i < 8; i++) { + int di = i * 6; + int index = (data[di + 0] << 5) + (data[di + 5] << 4) + (data[di + 1] << 3) + + (data[di + 2] << 2) + (data[di + 3] << 1) + data[di + 4] + i * 64; + Bits.fromBytes(sbox, index, 4).copyTo(sboxByteBits, i * 4); + } + + return sboxByteBits; + } + + static Bits createKey(byte[] data, int index) { + Bits key1, key2; + createKeys(data, index, out key1, out key2); + byte[] newKey = new byte[16 * 6]; + byte[] tmpData = new byte[28 * 2]; + for (int i = 0; i < 16; i++) { + int rolCount = rots[i]; + key1.rol(rolCount); + key2.rol(rolCount); + Bits.fromByteBits(key1.ByteBits, key2.ByteBits).transpose(pc2).toBits(newKey, i * 6); + } + return Bits.fromBytes(newKey); + } + + static void createKeys(byte[] data, int index, out Bits key1, out Bits key2) { + var tmpKey = new byte[8]; + int len = Math.Min(tmpKey.Length, data.Length - index); + if (len == 0) + throw new ArgumentException("data"); + Array.Copy(data, index, tmpKey, 0, len); + var bits = Bits.fromBytes(tmpKey).transpose(pc1); + key1 = Bits.fromByteBits(bits.ByteBits, 0, 28); + key2 = Bits.fromByteBits(bits.ByteBits, 28, 28); + } + } +} diff --git a/de4dot.code/deobfuscators/MaxtoCode/Decrypter6.cs b/de4dot.code/deobfuscators/MaxtoCode/Decrypter6.cs new file mode 100644 index 00000000..59479bee --- /dev/null +++ b/de4dot.code/deobfuscators/MaxtoCode/Decrypter6.cs @@ -0,0 +1,123 @@ +/* + 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; + +namespace de4dot.code.deobfuscators.MaxtoCode { + class Decrypter6 { + readonly uint[] key; + readonly byte[] gen1 = new byte[0x100]; + readonly byte[] gen2 = new byte[0x100]; + readonly byte[] gen3 = new byte[0x100]; + readonly byte[] gen4 = new byte[0x100]; + static readonly byte[] d1 = new byte[16] { 14, 4, 13, 21, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 }; + static readonly byte[] d2 = new byte[16] { 15, 1, 8, 14, 6, 11, 3, 4, 30, 7, 2, 13, 12, 0, 5, 10 }; + static readonly byte[] d3 = new byte[16] { 10, 0, 9, 14, 6, 3, 15, 5, 23, 13, 12, 7, 11, 4, 2, 8 }; + static readonly byte[] d4 = new byte[16] { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 }; + static readonly byte[] d5 = new byte[16] { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 }; + static readonly byte[] d6 = new byte[16] { 12, 1, 10, 15, 9, 2, 6, 8, 2, 13, 3, 4, 14, 7, 5, 11 }; + static readonly byte[] d7 = new byte[16] { 4, 11, 12, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 }; + static readonly byte[] d8 = new byte[16] { 13, 2, 8, 14, 6, 7, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 }; + + public static byte[] decrypt(byte[] key, byte[] encrypted) { + return new Decrypter6(key).decrypt(encrypted); + } + + Decrypter6(byte[] key) { + if (key.Length != 32) + throw new ArgumentException("Invalid key size", "key"); + this.key = new uint[8]; + Buffer.BlockCopy(key, 0, this.key, 0, key.Length); + initialize(); + } + + byte[] decrypt(byte[] encrypted) { + if ((encrypted.Length & 7) != 0) + throw new ArgumentException("Invalid data length", "encrypted"); + var decrypted = new byte[encrypted.Length]; + + int count = decrypted.Length / 8; + for (int i = 0; i < count; i++) { + uint x, y; + decrypt(BitConverter.ToUInt32(encrypted, i * 8), BitConverter.ToUInt32(encrypted, i * 8 + 4), out x, out y); + for (int j = 1; j < 100; j++) + decrypt(x, y, out x, out y); + writeUInt32(decrypted, i * 8, x); + writeUInt32(decrypted, i * 8 + 4, y); + } + + return decrypted; + } + + static void writeUInt32(byte[] data, int index, uint value) { + data[index] = (byte)value; + data[index + 1] = (byte)(value >> 8); + data[index + 2] = (byte)(value >> 16); + data[index + 3] = (byte)(value >> 24); + } + + void initialize() { + for (int i = 0; i < 0x100; i++) { + gen1[i] = (byte)((d1[i / 16] << 4) | d2[i & 0x0F]); + gen2[i] = (byte)((d3[i / 16] << 4) | d4[i & 0x0F]); + gen3[i] = (byte)((d5[i / 16] << 4) | d6[i & 0x0F]); + gen4[i] = (byte)((d7[i / 16] << 4) | d8[i & 0x0F]); + } + } + + void decrypt(uint i0, uint i1, out uint o0, out uint o1) { + uint x = i0; + uint y = decrypt(x + key[0]); + y ^= i1; + x ^= decrypt(y + key[1]); + y ^= decrypt(x + key[2]); + x ^= decrypt(y + key[3]); + y ^= decrypt(x + key[4]); + x ^= decrypt(y + key[5]); + y ^= decrypt(x + key[6]); + x ^= decrypt(y + key[7]); + + for (int i = 0; i < 3; i++) { + y ^= decrypt(x + key[7]); + x ^= decrypt(y + key[6]); + y ^= decrypt(x + key[5]); + x ^= decrypt(y + key[4]); + y ^= decrypt(x + key[3]); + x ^= decrypt(y + key[2]); + y ^= decrypt(x + key[1]); + x ^= decrypt(y + key[0]); + } + + o0 = y; + o1 = x; + } + + uint decrypt(uint val) { + uint x = (uint)((gen1[(byte)(val >> 24)] << 24) | + (gen2[(byte)(val >> 16)] << 16) | + (gen3[(byte)(val >> 8)] << 8) | + gen4[(byte)val]); + return ror(x, 21); + } + + static uint ror(uint val, int n) { + return (val << (32 - n)) + (val >> n); + } + } +} diff --git a/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs index 738af998..8e8959e4 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs @@ -111,7 +111,18 @@ namespace de4dot.code.deobfuscators.MaxtoCode { public McKey(PeImage peImage, PeHeader peHeader) { this.peHeader = peHeader; - this.data = peImage.readBytes(peHeader.getMcKeyRva(), 0x2000); + try { + this.data = peImage.readBytes(peHeader.getMcKeyRva(), 0x2000); + } + catch (IOException) { + this.data = peImage.readBytes(peHeader.getMcKeyRva(), 0x1000); + } + } + + public byte[] readBytes(int offset, int len) { + byte[] bytes = new byte[len]; + Array.Copy(data, offset, bytes, 0, len); + return bytes; } public byte readByte(int offset) { @@ -253,9 +264,17 @@ namespace de4dot.code.deobfuscators.MaxtoCode { uint encryptedDataOffset; uint xorKey; Dictionary infos = new Dictionary(); - IDecrypter decrypter; + List decrypters = new List(); const int ENCRYPTED_DATA_INFO_SIZE = 0x13; + delegate byte[] Decrypt(byte[] encrypted); + readonly Decrypt[] decryptHandlersV1; + readonly Decrypt[] decryptHandlersV2; + readonly Decrypt[] decryptHandlersV3; + readonly Decrypt[] decryptHandlersV4; + readonly Decrypt[] decryptHandlersV5a; + readonly Decrypt[] decryptHandlersV5b; + public class DecryptedMethodInfo { public uint bodyRva; public byte[] body; @@ -272,6 +291,13 @@ namespace de4dot.code.deobfuscators.MaxtoCode { this.peHeader = peHeader; this.mcKey = mcKey; + decryptHandlersV1 = new Decrypt[] { decrypt1a, decrypt4a, decrypt2a, decrypt3a, decrypt5, decrypt6, decrypt7 }; + decryptHandlersV2 = new Decrypt[] { decrypt3a, decrypt2a, decrypt1a, decrypt4a, decrypt5, decrypt6, decrypt7 }; + decryptHandlersV3 = new Decrypt[] { decrypt1a, decrypt2a, decrypt3a, decrypt4a, decrypt5, decrypt6, decrypt7 }; + decryptHandlersV4 = new Decrypt[] { decrypt2a, decrypt1a, decrypt3a, decrypt4a, decrypt5, decrypt6, decrypt7 }; + decryptHandlersV5a = new Decrypt[] { decrypt4a, decrypt2a, decrypt3a, decrypt1a, decrypt5, decrypt6, decrypt7 }; + decryptHandlersV5b = new Decrypt[] { decrypt4b, decrypt2b, decrypt3b, decrypt1b, decrypt6, decrypt7, decrypt5 }; + structSize = getStructSize(mcKey); uint methodInfosRva = peHeader.getRva2(0x0FF8, mcKey.readUInt32(0x005A)); @@ -346,42 +372,29 @@ namespace de4dot.code.deobfuscators.MaxtoCode { } class Decrypter : IDecrypter { - MethodInfos methodInfos; - int[] typeToMethod; + Decrypt[] decrypterHandlers; - public Decrypter(MethodInfos methodInfos, int[] typeToMethod) { - this.methodInfos = methodInfos; - this.typeToMethod = typeToMethod; + public Decrypter(Decrypt[] decrypterHandlers) { + this.decrypterHandlers = decrypterHandlers; } public byte[] decrypt(int type, byte[] encrypted) { - if (0 <= type && type < typeToMethod.Length) { - switch (typeToMethod[type]) { - case 1: return methodInfos.decrypt1(encrypted); - case 2: return methodInfos.decrypt2(encrypted); - case 3: return methodInfos.decrypt3(encrypted); - case 4: return methodInfos.decrypt4(encrypted); - case 5: return methodInfos.decrypt5(encrypted); - case 6: return methodInfos.decrypt6(encrypted); - case 7: return methodInfos.decrypt7(encrypted); - } - } + if (1 <= type && type <= decrypterHandlers.Length) + return decrypterHandlers[type - 1](encrypted); throw new ApplicationException(string.Format("Invalid encryption type: {0:X2}", type)); } } - static readonly int[] typeToTypesV1 = new int[] { -1, 1, 4, 2, 3, 5, 6, 7 }; - static readonly int[] typeToTypesV2 = new int[] { -1, 3, 2, 1, 4, 5, 6, 7 }; - static readonly int[] typeToTypesV3 = new int[] { -1, 1, 2, 3, 4, 5, 6, 7 }; - static readonly int[] typeToTypesV4 = new int[] { -1, 2, 1, 3, 4, 5, 6, 7 }; - static readonly int[] typeToTypesV5 = new int[] { -1, 4, 2, 3, 1, 5, 6, 7 }; void initializeDecrypter() { switch (getVersion()) { - case EncryptionVersion.V1: decrypter = new Decrypter(this, typeToTypesV1); break; - case EncryptionVersion.V2: decrypter = new Decrypter(this, typeToTypesV2); break; - case EncryptionVersion.V3: decrypter = new Decrypter(this, typeToTypesV3); break; - case EncryptionVersion.V4: decrypter = new Decrypter(this, typeToTypesV4); break; - case EncryptionVersion.V5: decrypter = new Decrypter(this, typeToTypesV5); break; + case EncryptionVersion.V1: decrypters.Add(new Decrypter(decryptHandlersV1)); break; + case EncryptionVersion.V2: decrypters.Add(new Decrypter(decryptHandlersV2)); break; + case EncryptionVersion.V3: decrypters.Add(new Decrypter(decryptHandlersV3)); break; + case EncryptionVersion.V4: decrypters.Add(new Decrypter(decryptHandlersV4)); break; + case EncryptionVersion.V5: + decrypters.Add(new Decrypter(decryptHandlersV5a)); + decrypters.Add(new Decrypter(decryptHandlersV5b)); + break; case EncryptionVersion.Unknown: default: @@ -391,7 +404,23 @@ namespace de4dot.code.deobfuscators.MaxtoCode { public void initializeInfos() { initializeDecrypter(); + if (!initializeInfos2()) + throw new ApplicationException("Could not decrypt methods"); + } + bool initializeInfos2() { + foreach (var decrypter in decrypters) { + try { + if (initializeInfos2(decrypter)) + return true; + } + catch { + } + } + return false; + } + + bool initializeInfos2(IDecrypter decrypter) { int numMethods = readInt32(0) ^ readInt32(4); if (numMethods < 0) throw new ApplicationException("Invalid number of encrypted methods"); @@ -406,8 +435,6 @@ namespace de4dot.code.deobfuscators.MaxtoCode { uint totalSize = readEncryptedUInt32(offset + 4); uint methodInstructionRva = readEncryptedUInt32(offset + 8); - var decryptedData = new byte[totalSize]; - // Read the method body header and method body (instrs + exception handlers). // The method body header is always in the first one. The instrs + ex handlers // are always in the last 4, and evenly divided (each byte[] is totalLen / 4). @@ -425,18 +452,24 @@ namespace de4dot.code.deobfuscators.MaxtoCode { if (j == 1 && exOffset == 0) encryptedDataInfos[j] = null; else - encryptedDataInfos[j] = decrypt(encryptionType, dataOffset, encryptedSize, realSize); + encryptedDataInfos[j] = decrypt(decrypter, encryptionType, dataOffset, encryptedSize, realSize); } + var decryptedData = new byte[totalSize]; int copyOffset = 0; copyOffset = copyData(decryptedData, encryptedDataInfos[0], copyOffset); for (int j = 2; j < encryptedDataInfos.Length; j++) copyOffset = copyData(decryptedData, encryptedDataInfos[j], copyOffset); copyData(decryptedData, encryptedDataInfos[1], exOffset); // Exceptions or padding + if (!MethodBodyParser.verify(decryptedData)) + throw new InvalidMethodBody(); + var info = new DecryptedMethodInfo(methodBodyRva, decryptedData); infos[info.bodyRva] = info; } + + return true; } static int copyData(byte[] dest, byte[] source, int offset) { @@ -450,7 +483,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return peImage.offsetReadBytes(encryptedDataOffset + offset, size); } - byte[] decrypt(int type, uint dataOffset, uint encryptedSize, uint realSize) { + byte[] decrypt(IDecrypter decrypter, int type, uint dataOffset, uint encryptedSize, uint realSize) { if (realSize == 0) return null; if (realSize > encryptedSize) @@ -464,17 +497,35 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return decrypted; } - byte[] decrypt1(byte[] encrypted) { + byte[] decrypt1a(byte[] encrypted) { + return decrypt1(encrypted, 0, 0x2000); + } + + byte[] decrypt1b(byte[] encrypted) { + return decrypt1(encrypted, 6, 0x500); + } + + byte[] decrypt1(byte[] encrypted, int keyStart, int keyEnd) { var decrypted = new byte[encrypted.Length]; - for (int i = 0; i < decrypted.Length; i++) - decrypted[i] = (byte)(encrypted[i] ^ mcKey.readByte(i % 0x2000)); + for (int i = 0, ki = keyStart; i < decrypted.Length; i++) { + decrypted[i] = (byte)(encrypted[i] ^ mcKey.readByte(ki)); + if (++ki == keyEnd) + ki = keyStart; + } return decrypted; } - byte[] decrypt2(byte[] encrypted) { + byte[] decrypt2a(byte[] encrypted) { + return decrypt2(encrypted, 0x00FA); + } + + byte[] decrypt2b(byte[] encrypted) { + return decrypt2(encrypted, 0x00FA + 9); + } + + byte[] decrypt2(byte[] encrypted, int offset) { if ((encrypted.Length & 7) != 0) throw new ApplicationException("Invalid encryption #2 length"); - const int offset = 0x00FA; uint key4 = mcKey.readUInt32(offset + 4 * 4); uint key5 = mcKey.readUInt32(offset + 5 * 4); @@ -495,11 +546,18 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return decrypted; } - static byte[] decrypt3Shifts = new byte[16] { 5, 11, 14, 21, 6, 20, 17, 29, 4, 10, 3, 2, 7, 1, 26, 18 }; - byte[] decrypt3(byte[] encrypted) { + byte[] decrypt3a(byte[] encrypted) { + return decrypt3(encrypted, 0x015E); + } + + byte[] decrypt3b(byte[] encrypted) { + return decrypt3(encrypted, 0x015E + 0xE5); + } + + static readonly byte[] decrypt3Shifts = new byte[16] { 5, 11, 14, 21, 6, 20, 17, 29, 4, 10, 3, 2, 7, 1, 26, 18 }; + byte[] decrypt3(byte[] encrypted, int offset) { if ((encrypted.Length & 7) != 0) throw new ApplicationException("Invalid encryption #3 length"); - const int offset = 0x015E; uint key0 = mcKey.readUInt32(offset + 0 * 4); uint key3 = mcKey.readUInt32(offset + 3 * 4); @@ -525,37 +583,67 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return decrypted; } - byte[] decrypt4(byte[] encrypted) { + byte[] decrypt4a(byte[] encrypted) { + return decrypt4(encrypted, 0, 0x2000); + } + + byte[] decrypt4b(byte[] encrypted) { + return decrypt4(encrypted, 0x14, 0x1000); + } + + byte[] decrypt4(byte[] encrypted, int keyStart, int keyEnd) { var decrypted = new byte[encrypted.Length / 3 * 2 + 1]; int count = encrypted.Length / 3; - int i = 0, j = 0, k = 0; + int i = 0, ki = keyStart, j = 0; while (count-- > 0) { - byte k1 = mcKey.readByte(j + 1); - byte k2 = mcKey.readByte(j + 2); - byte k3 = mcKey.readByte(j + 3); - decrypted[k++] = (byte)(((encrypted[i + 1] ^ k2) >> 4) | ((encrypted[i] ^ k1) & 0xF0)); - decrypted[k++] = (byte)(((encrypted[i + 1] ^ k2) << 4) + ((encrypted[i + 2] ^ k3) & 0x0F)); + byte k1 = mcKey.readByte(ki + 1); + byte k2 = mcKey.readByte(ki + 2); + byte k3 = mcKey.readByte(ki + 3); + decrypted[j++] = (byte)(((encrypted[i + 1] ^ k2) >> 4) | ((encrypted[i] ^ k1) & 0xF0)); + decrypted[j++] = (byte)(((encrypted[i + 1] ^ k2) << 4) + ((encrypted[i + 2] ^ k3) & 0x0F)); i += 3; - j = (j + 4) % 0x2000; + ki += 4; + if (ki == keyEnd) + ki = keyStart; } if ((encrypted.Length % 3) != 0) - decrypted[k] = (byte)(encrypted[i] ^ mcKey.readByte(j)); + decrypted[j] = (byte)(encrypted[i] ^ mcKey.readByte(ki)); return decrypted; } byte[] decrypt5(byte[] encrypted) { - throw new NotImplementedException("Encryption type #5 not implemented yet"); + return CryptDecrypter.decrypt(mcKey.readBytes(0x0032, 15), encrypted); } byte[] decrypt6(byte[] encrypted) { - throw new NotImplementedException("Encryption type #6 not implemented yet"); + return Decrypter6.decrypt(mcKey.readBytes(0x0096, 32), encrypted); } byte[] decrypt7(byte[] encrypted) { - throw new NotImplementedException("Encryption type #7 not implemented yet"); + var decrypted = (byte[])encrypted.Clone(); + new Blowfish(getBlowfishKey()).decrypt_LE(decrypted); + return decrypted; + } + + byte[] blowfishKey; + byte[] getBlowfishKey() { + if (blowfishKey != null) + return blowfishKey; + var key = new byte[100]; + int i; + for (i = 0; i < key.Length; i++) { + byte b = mcKey.readByte(i); + if (b == 0) + break; + key[i] = b; + } + for (; i < key.Length; i++) + key[i] = 0; + key[key.Length - 1] = 0; + return blowfishKey = key; } } From 9b48632354ee5a903474691c13bb1a8822e57f47 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 20 Jul 2012 18:15:40 +0200 Subject: [PATCH 10/15] Refactor --- de4dot.code/de4dot.code.csproj | 4 + .../deobfuscators/MaxtoCode/DecrypterInfo.cs | 38 +++ .../deobfuscators/MaxtoCode/Deobfuscator.cs | 9 +- .../MaxtoCode/EncryptionInfos.cs | 133 +++++++++ .../deobfuscators/MaxtoCode/FileDecrypter.cs | 255 ++---------------- de4dot.code/deobfuscators/MaxtoCode/McKey.cs | 57 ++++ .../deobfuscators/MaxtoCode/PeHeader.cs | 100 +++++++ 7 files changed, 356 insertions(+), 240 deletions(-) create mode 100644 de4dot.code/deobfuscators/MaxtoCode/DecrypterInfo.cs create mode 100644 de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs create mode 100644 de4dot.code/deobfuscators/MaxtoCode/McKey.cs create mode 100644 de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 195abef7..5952a72e 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -70,6 +70,10 @@ + + + + diff --git a/de4dot.code/deobfuscators/MaxtoCode/DecrypterInfo.cs b/de4dot.code/deobfuscators/MaxtoCode/DecrypterInfo.cs new file mode 100644 index 00000000..d15c2509 --- /dev/null +++ b/de4dot.code/deobfuscators/MaxtoCode/DecrypterInfo.cs @@ -0,0 +1,38 @@ +/* + 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 de4dot.PE; + +namespace de4dot.code.deobfuscators.MaxtoCode { + class DecrypterInfo { + public readonly MainType mainType; + public readonly PeImage peImage; + public readonly PeHeader peHeader; + public readonly McKey mcKey; + public readonly byte[] fileData; + + public DecrypterInfo(MainType mainType, byte[] fileData) { + this.mainType = mainType; + this.peImage = new PeImage(fileData); + this.peHeader = new PeHeader(mainType, peImage); + this.mcKey = new McKey(peImage, peHeader); + this.fileData = fileData; + } + } +} diff --git a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs index 3fd51171..14e5949f 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs @@ -49,6 +49,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode { class Deobfuscator : DeobfuscatorBase { Options options; MainType mainType; + DecrypterInfo decrypterInfo; internal class Options : OptionsBase { } @@ -88,10 +89,11 @@ namespace de4dot.code.deobfuscators.MaxtoCode { if (!mainType.Detected) return false; - var fileDecrypter = new FileDecrypter(mainType); - var fileData = DeobUtils.readModule(module); - if (!fileDecrypter.decrypt(fileData, ref dumpedMethods)) + decrypterInfo = new DecrypterInfo(mainType, fileData); + var fileDecrypter = new FileDecrypter(decrypterInfo); + + if (!fileDecrypter.decrypt(ref dumpedMethods)) return false; newFileData = fileData; @@ -102,6 +104,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode { var newOne = new Deobfuscator(options); newOne.setModule(module); newOne.mainType = new MainType(module, mainType); + newOne.decrypterInfo = decrypterInfo; return newOne; } diff --git a/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs b/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs new file mode 100644 index 00000000..e69b87a4 --- /dev/null +++ b/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs @@ -0,0 +1,133 @@ +/* + 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 . +*/ + +namespace de4dot.code.deobfuscators.MaxtoCode { + class EncryptionInfo { + public uint MagicLo { get; set; } + public uint MagicHi { get; set; } + public EncryptionVersion Version { get; set; } + } + + static class EncryptionInfos { + public static readonly EncryptionInfo[] Rva900h = new EncryptionInfo[] { + // PE header timestamp + // 462FA2D2 = Wed, 25 Apr 2007 18:49:54 (3.20) + new EncryptionInfo { + MagicLo = 0xA098B387, + MagicHi = 0x1E8EBCA3, + Version = EncryptionVersion.V1, + }, + // 482384FB = Thu, 08 May 2008 22:55:55 (3.36) + new EncryptionInfo { + MagicLo = 0xAA98B387, + MagicHi = 0x1E8EECA3, + Version = EncryptionVersion.V2, + }, + // 4A5EEC64 = Thu, 16 Jul 2009 09:01:24 + // 4C6220EC = Wed, 11 Aug 2010 04:02:52 + // 4C622357 = Wed, 11 Aug 2010 04:13:11 + new EncryptionInfo { + MagicLo = 0xAA98B387, + MagicHi = 0x128EECA3, + Version = EncryptionVersion.V2, + }, + // 4C6E4605 = Fri, 20 Aug 2010 09:08:21 + // 4D0E220D = Sun, 19 Dec 2010 15:17:33 + // 4DC2FC75 = Thu, 05 May 2011 19:37:25 + // 4DFA3D5D = Thu, 16 Jun 2011 17:29:01 + new EncryptionInfo { + MagicLo = 0xAA98B387, + MagicHi = 0xF28EECA3, + Version = EncryptionVersion.V2, + }, + // 4DC2FE0C = Thu, 05 May 2011 19:44:12 + new EncryptionInfo { + MagicLo = 0xAA98B387, + MagicHi = 0xF28EEAA3, + Version = EncryptionVersion.V2, + }, + // 4ED76740 = Thu, 01 Dec 2011 11:38:40 + // 4EE1FAD1 = Fri, 09 Dec 2011 12:10:57 + new EncryptionInfo { + MagicLo = 0xAA983B87, + MagicHi = 0xF28EECA3, + Version = EncryptionVersion.V3, + }, + // 4F832868 = Mon, Apr 09 2012 20:20:24 + new EncryptionInfo { + MagicLo = 0xAA913B87, + MagicHi = 0xF28EE0A3, + Version = EncryptionVersion.V4, + }, + // 4F8E262C = Wed, 18 Apr 2012 02:25:48 + new EncryptionInfo { + MagicLo = 0xBA983B87, + MagicHi = 0xF28EDDA3, + Version = EncryptionVersion.V5, + }, + }; + + public static readonly EncryptionInfo[] McKey8C0h = new EncryptionInfo[] { + // 462FA2D2 = Wed, 25 Apr 2007 18:49:54 (3.20) + new EncryptionInfo { + MagicLo = 0x6AA13B13, + MagicHi = 0xD72B991F, + Version = EncryptionVersion.V1, + }, + // 482384FB = Thu, 08 May 2008 22:55:55 (3.36) + new EncryptionInfo { + MagicLo = 0x6A713B13, + MagicHi = 0xD72B891F, + Version = EncryptionVersion.V2, + }, + // 4A5EEC64 = Thu, 16 Jul 2009 09:01:24 + // 4C6220EC = Wed, 11 Aug 2010 04:02:52 + // 4C622357 = Wed, 11 Aug 2010 04:13:11 + // 4C6E4605 = Fri, 20 Aug 2010 09:08:21 + // 4D0E220D = Sun, 19 Dec 2010 15:17:33 + // 4DC2FC75 = Thu, 05 May 2011 19:37:25 + // 4DC2FE0C = Thu, 05 May 2011 19:44:12 + // 4DFA3D5D = Thu, 16 Jun 2011 17:29:01 + new EncryptionInfo { + MagicLo = 0x6A713B13, + MagicHi = 0xD72B891F, + Version = EncryptionVersion.V2, + }, + // 4ED76740 = Thu, 01 Dec 2011 11:38:40 + // 4EE1FAD1 = Fri, 09 Dec 2011 12:10:57 + new EncryptionInfo { + MagicLo = 0x6A731B13, + MagicHi = 0xD72B891F, + Version = EncryptionVersion.V3, + }, + // 4F832868 = Mon, Apr 09 2012 20:20:24 + new EncryptionInfo { + MagicLo = 0x6AD31B13, + MagicHi = 0xD72B8A1F, + Version = EncryptionVersion.V4, + }, + // 4F8E262C = Wed, 18 Apr 2012 02:25:48 + new EncryptionInfo { + MagicLo = 0xAA731B13, + MagicHi = 0xD723891F, + Version = EncryptionVersion.V5, + }, + }; + } +} diff --git a/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs index 8e8959e4..02f3fa25 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs @@ -27,232 +27,7 @@ using de4dot.PE; namespace de4dot.code.deobfuscators.MaxtoCode { // Decrypts methods and resources class FileDecrypter { - MainType mainType; - PeImage peImage; - PeHeader peHeader; - McKey mcKey; - byte[] fileData; - - class PeHeader { - const int XOR_KEY = 0x7ABF931; - - EncryptionVersion version; - byte[] headerData; - - public EncryptionVersion EncryptionVersion { - get { return version; } - } - - public PeHeader(MainType mainType, PeImage peImage) { - uint headerOffset; - version = getHeaderOffsetAndVersion(peImage, out headerOffset); - headerData = peImage.offsetReadBytes(headerOffset, 0x1000); - } - - public uint getMcKeyRva() { - return getRva2(0x0FFC, XOR_KEY); - } - - public uint getRva1(int offset, uint xorKey) { - return (readUInt32(offset) ^ xorKey); - } - - public uint getRva2(int offset, uint xorKey) { - return (readUInt32(offset) ^ xorKey); - } - - public uint readUInt32(int offset) { - return BitConverter.ToUInt32(headerData, offset); - } - - static EncryptionVersion getHeaderOffsetAndVersion(PeImage peImage, out uint headerOffset) { - headerOffset = 0; - - var version = getVersion(peImage, headerOffset); - if (version != EncryptionVersion.Unknown) - return version; - - var section = peImage.findSection(".rsrc"); - if (section == null) - return EncryptionVersion.Unknown; - - headerOffset = section.pointerToRawData; - uint end = section.pointerToRawData + section.sizeOfRawData - 0x1000 + 1; - while (headerOffset < end) { - version = getVersion(peImage, headerOffset); - if (version != EncryptionVersion.Unknown) - return version; - headerOffset++; - } - - return EncryptionVersion.Unknown; - } - - static EncryptionVersion getVersion(PeImage peImage, uint headerOffset) { - uint m1lo = peImage.offsetReadUInt32(headerOffset + 0x900); - uint m1hi = peImage.offsetReadUInt32(headerOffset + 0x904); - - foreach (var info in encryptionInfos_Rva900h) { - if (info.MagicLo == m1lo && info.MagicHi == m1hi) - return info.Version; - } - - return EncryptionVersion.Unknown; - } - } - - class McKey { - PeHeader peHeader; - byte[] data; - - public byte this[int index] { - get { return data[index]; } - } - - public McKey(PeImage peImage, PeHeader peHeader) { - this.peHeader = peHeader; - try { - this.data = peImage.readBytes(peHeader.getMcKeyRva(), 0x2000); - } - catch (IOException) { - this.data = peImage.readBytes(peHeader.getMcKeyRva(), 0x1000); - } - } - - public byte[] readBytes(int offset, int len) { - byte[] bytes = new byte[len]; - Array.Copy(data, offset, bytes, 0, len); - return bytes; - } - - public byte readByte(int offset) { - return data[offset]; - } - - public uint readUInt32(int offset) { - return BitConverter.ToUInt32(data, offset); - } - } - - enum EncryptionVersion { - Unknown, - V1, - V2, - V3, - V4, - V5, - } - - class EncryptionInfo { - public uint MagicLo { get; set; } - public uint MagicHi { get; set; } - public EncryptionVersion Version { get; set; } - } - - static readonly EncryptionInfo[] encryptionInfos_Rva900h = new EncryptionInfo[] { - // PE header timestamp - // 462FA2D2 = Wed, 25 Apr 2007 18:49:54 (3.20) - new EncryptionInfo { - MagicLo = 0xA098B387, - MagicHi = 0x1E8EBCA3, - Version = EncryptionVersion.V1, - }, - // 482384FB = Thu, 08 May 2008 22:55:55 (3.36) - new EncryptionInfo { - MagicLo = 0xAA98B387, - MagicHi = 0x1E8EECA3, - Version = EncryptionVersion.V2, - }, - // 4A5EEC64 = Thu, 16 Jul 2009 09:01:24 - // 4C6220EC = Wed, 11 Aug 2010 04:02:52 - // 4C622357 = Wed, 11 Aug 2010 04:13:11 - new EncryptionInfo { - MagicLo = 0xAA98B387, - MagicHi = 0x128EECA3, - Version = EncryptionVersion.V2, - }, - // 4C6E4605 = Fri, 20 Aug 2010 09:08:21 - // 4D0E220D = Sun, 19 Dec 2010 15:17:33 - // 4DC2FC75 = Thu, 05 May 2011 19:37:25 - // 4DFA3D5D = Thu, 16 Jun 2011 17:29:01 - new EncryptionInfo { - MagicLo = 0xAA98B387, - MagicHi = 0xF28EECA3, - Version = EncryptionVersion.V2, - }, - // 4DC2FE0C = Thu, 05 May 2011 19:44:12 - new EncryptionInfo { - MagicLo = 0xAA98B387, - MagicHi = 0xF28EEAA3, - Version = EncryptionVersion.V2, - }, - // 4ED76740 = Thu, 01 Dec 2011 11:38:40 - // 4EE1FAD1 = Fri, 09 Dec 2011 12:10:57 - new EncryptionInfo { - MagicLo = 0xAA983B87, - MagicHi = 0xF28EECA3, - Version = EncryptionVersion.V3, - }, - // 4F832868 = Mon, Apr 09 2012 20:20:24 - new EncryptionInfo { - MagicLo = 0xAA913B87, - MagicHi = 0xF28EE0A3, - Version = EncryptionVersion.V4, - }, - // 4F8E262C = Wed, 18 Apr 2012 02:25:48 - new EncryptionInfo { - MagicLo = 0xBA983B87, - MagicHi = 0xF28EDDA3, - Version = EncryptionVersion.V5, - }, - }; - - static readonly EncryptionInfo[] encryptionInfos_McKey8C0h = new EncryptionInfo[] { - // 462FA2D2 = Wed, 25 Apr 2007 18:49:54 (3.20) - new EncryptionInfo { - MagicLo = 0x6AA13B13, - MagicHi = 0xD72B991F, - Version = EncryptionVersion.V1, - }, - // 482384FB = Thu, 08 May 2008 22:55:55 (3.36) - new EncryptionInfo { - MagicLo = 0x6A713B13, - MagicHi = 0xD72B891F, - Version = EncryptionVersion.V2, - }, - // 4A5EEC64 = Thu, 16 Jul 2009 09:01:24 - // 4C6220EC = Wed, 11 Aug 2010 04:02:52 - // 4C622357 = Wed, 11 Aug 2010 04:13:11 - // 4C6E4605 = Fri, 20 Aug 2010 09:08:21 - // 4D0E220D = Sun, 19 Dec 2010 15:17:33 - // 4DC2FC75 = Thu, 05 May 2011 19:37:25 - // 4DC2FE0C = Thu, 05 May 2011 19:44:12 - // 4DFA3D5D = Thu, 16 Jun 2011 17:29:01 - new EncryptionInfo { - MagicLo = 0x6A713B13, - MagicHi = 0xD72B891F, - Version = EncryptionVersion.V2, - }, - // 4ED76740 = Thu, 01 Dec 2011 11:38:40 - // 4EE1FAD1 = Fri, 09 Dec 2011 12:10:57 - new EncryptionInfo { - MagicLo = 0x6A731B13, - MagicHi = 0xD72B891F, - Version = EncryptionVersion.V3, - }, - // 4F832868 = Mon, Apr 09 2012 20:20:24 - new EncryptionInfo { - MagicLo = 0x6AD31B13, - MagicHi = 0xD72B8A1F, - Version = EncryptionVersion.V4, - }, - // 4F8E262C = Wed, 18 Apr 2012 02:25:48 - new EncryptionInfo { - MagicLo = 0xAA731B13, - MagicHi = 0xD723891F, - Version = EncryptionVersion.V5, - }, - }; + DecrypterInfo decrypterInfo; class MethodInfos { MainType mainType; @@ -310,7 +85,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode { static uint getStructSize(McKey mcKey) { uint magicLo = mcKey.readUInt32(0x8C0); uint magicHi = mcKey.readUInt32(0x8C4); - foreach (var info in encryptionInfos_McKey8C0h) { + foreach (var info in EncryptionInfos.McKey8C0h) { if (magicLo == info.MagicLo && magicHi == info.MagicHi) return 0xC + 6 * ENCRYPTED_DATA_INFO_SIZE; } @@ -324,7 +99,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode { uint m2lo = mcKey.readUInt32(0x8C0); uint m2hi = mcKey.readUInt32(0x8C4); - foreach (var info in encryptionInfos_McKey8C0h) { + foreach (var info in EncryptionInfos.McKey8C0h) { if (info.MagicLo == m2lo && info.MagicHi == m2hi) return info.Version; } @@ -647,16 +422,11 @@ namespace de4dot.code.deobfuscators.MaxtoCode { } } - public FileDecrypter(MainType mainType) { - this.mainType = mainType; + public FileDecrypter(DecrypterInfo decrypterInfo) { + this.decrypterInfo = decrypterInfo; } - public bool decrypt(byte[] fileData, ref DumpedMethods dumpedMethods) { - peImage = new PeImage(fileData); - peHeader = new PeHeader(mainType, peImage); - mcKey = new McKey(peImage, peHeader); - this.fileData = fileData; - + public bool decrypt(ref DumpedMethods dumpedMethods) { dumpedMethods = decryptMethods(); if (dumpedMethods == null) return false; @@ -670,7 +440,8 @@ namespace de4dot.code.deobfuscators.MaxtoCode { DumpedMethods decryptMethods() { var dumpedMethods = new DumpedMethods(); - var methodInfos = new MethodInfos(mainType, peImage, peHeader, mcKey); + var peImage = decrypterInfo.peImage; + var methodInfos = new MethodInfos(decrypterInfo.mainType, peImage, decrypterInfo.peHeader, decrypterInfo.mcKey); methodInfos.initializeInfos(); var metadataTables = peImage.Cor20Header.createMetadataTables(); @@ -729,6 +500,11 @@ namespace de4dot.code.deobfuscators.MaxtoCode { } void decryptResources() { + var peHeader = decrypterInfo.peHeader; + var mcKey = decrypterInfo.mcKey; + var peImage = decrypterInfo.peImage; + var fileData = decrypterInfo.fileData; + uint resourceRva = peHeader.getRva1(0x0E10, mcKey.readUInt32(0x00A0)); uint resourceSize = peHeader.readUInt32(0x0E14) ^ mcKey.readUInt32(0x00AA); if (resourceRva == 0 || resourceSize == 0) @@ -746,6 +522,11 @@ namespace de4dot.code.deobfuscators.MaxtoCode { } void decryptStrings() { + var peHeader = decrypterInfo.peHeader; + var mcKey = decrypterInfo.mcKey; + var peImage = decrypterInfo.peImage; + var fileData = decrypterInfo.fileData; + uint usHeapRva = peHeader.getRva1(0x0E00, mcKey.readUInt32(0x0078)); uint usHeapSize = peHeader.readUInt32(0x0E04) ^ mcKey.readUInt32(0x0082); if (usHeapRva == 0 || usHeapSize == 0) diff --git a/de4dot.code/deobfuscators/MaxtoCode/McKey.cs b/de4dot.code/deobfuscators/MaxtoCode/McKey.cs new file mode 100644 index 00000000..302e0139 --- /dev/null +++ b/de4dot.code/deobfuscators/MaxtoCode/McKey.cs @@ -0,0 +1,57 @@ +/* + 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.IO; +using de4dot.PE; + +namespace de4dot.code.deobfuscators.MaxtoCode { + class McKey { + PeHeader peHeader; + byte[] data; + + public byte this[int index] { + get { return data[index]; } + } + + public McKey(PeImage peImage, PeHeader peHeader) { + this.peHeader = peHeader; + try { + this.data = peImage.readBytes(peHeader.getMcKeyRva(), 0x2000); + } + catch (IOException) { + this.data = peImage.readBytes(peHeader.getMcKeyRva(), 0x1000); + } + } + + public byte[] readBytes(int offset, int len) { + byte[] bytes = new byte[len]; + Array.Copy(data, offset, bytes, 0, len); + return bytes; + } + + public byte readByte(int offset) { + return data[offset]; + } + + public uint readUInt32(int offset) { + return BitConverter.ToUInt32(data, offset); + } + } +} diff --git a/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs b/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs new file mode 100644 index 00000000..a23fb20a --- /dev/null +++ b/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs @@ -0,0 +1,100 @@ +/* + 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 de4dot.PE; + +namespace de4dot.code.deobfuscators.MaxtoCode { + enum EncryptionVersion { + Unknown, + V1, + V2, + V3, + V4, + V5, + } + + class PeHeader { + const int XOR_KEY = 0x7ABF931; + + EncryptionVersion version; + byte[] headerData; + + public EncryptionVersion EncryptionVersion { + get { return version; } + } + + public PeHeader(MainType mainType, PeImage peImage) { + uint headerOffset; + version = getHeaderOffsetAndVersion(peImage, out headerOffset); + headerData = peImage.offsetReadBytes(headerOffset, 0x1000); + } + + public uint getMcKeyRva() { + return getRva2(0x0FFC, XOR_KEY); + } + + public uint getRva1(int offset, uint xorKey) { + return (readUInt32(offset) ^ xorKey); + } + + public uint getRva2(int offset, uint xorKey) { + return (readUInt32(offset) ^ xorKey); + } + + public uint readUInt32(int offset) { + return BitConverter.ToUInt32(headerData, offset); + } + + static EncryptionVersion getHeaderOffsetAndVersion(PeImage peImage, out uint headerOffset) { + headerOffset = 0; + + var version = getVersion(peImage, headerOffset); + if (version != EncryptionVersion.Unknown) + return version; + + var section = peImage.findSection(".rsrc"); + if (section == null) + return EncryptionVersion.Unknown; + + headerOffset = section.pointerToRawData; + uint end = section.pointerToRawData + section.sizeOfRawData - 0x1000 + 1; + while (headerOffset < end) { + version = getVersion(peImage, headerOffset); + if (version != EncryptionVersion.Unknown) + return version; + headerOffset++; + } + + return EncryptionVersion.Unknown; + } + + static EncryptionVersion getVersion(PeImage peImage, uint headerOffset) { + uint m1lo = peImage.offsetReadUInt32(headerOffset + 0x900); + uint m1hi = peImage.offsetReadUInt32(headerOffset + 0x904); + + foreach (var info in EncryptionInfos.Rva900h) { + if (info.MagicLo == m1lo && info.MagicHi == m1hi) + return info.Version; + } + + return EncryptionVersion.Unknown; + } + } +} From dfafc4a94b1f5aa23603571ef0834074214458c6 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 20 Jul 2012 18:32:49 +0200 Subject: [PATCH 11/15] Remove useless method --- de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs | 8 ++++---- de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs | 8 ++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs index 02f3fa25..6c180e39 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs @@ -75,8 +75,8 @@ namespace de4dot.code.deobfuscators.MaxtoCode { structSize = getStructSize(mcKey); - uint methodInfosRva = peHeader.getRva2(0x0FF8, mcKey.readUInt32(0x005A)); - uint encryptedDataRva = peHeader.getRva2(0x0FF0, mcKey.readUInt32(0x0046)); + uint methodInfosRva = peHeader.getRva(0x0FF8, mcKey.readUInt32(0x005A)); + uint encryptedDataRva = peHeader.getRva(0x0FF0, mcKey.readUInt32(0x0046)); methodInfosOffset = peImage.rvaToOffset(methodInfosRva); encryptedDataOffset = peImage.rvaToOffset(encryptedDataRva); @@ -505,7 +505,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode { var peImage = decrypterInfo.peImage; var fileData = decrypterInfo.fileData; - uint resourceRva = peHeader.getRva1(0x0E10, mcKey.readUInt32(0x00A0)); + uint resourceRva = peHeader.getRva(0x0E10, mcKey.readUInt32(0x00A0)); uint resourceSize = peHeader.readUInt32(0x0E14) ^ mcKey.readUInt32(0x00AA); if (resourceRva == 0 || resourceSize == 0) return; @@ -527,7 +527,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode { var peImage = decrypterInfo.peImage; var fileData = decrypterInfo.fileData; - uint usHeapRva = peHeader.getRva1(0x0E00, mcKey.readUInt32(0x0078)); + uint usHeapRva = peHeader.getRva(0x0E00, mcKey.readUInt32(0x0078)); uint usHeapSize = peHeader.readUInt32(0x0E04) ^ mcKey.readUInt32(0x0082); if (usHeapRva == 0 || usHeapSize == 0) return; diff --git a/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs b/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs index a23fb20a..da3a2442 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs @@ -47,14 +47,10 @@ namespace de4dot.code.deobfuscators.MaxtoCode { } public uint getMcKeyRva() { - return getRva2(0x0FFC, XOR_KEY); + return getRva(0x0FFC, XOR_KEY); } - public uint getRva1(int offset, uint xorKey) { - return (readUInt32(offset) ^ xorKey); - } - - public uint getRva2(int offset, uint xorKey) { + public uint getRva(int offset, uint xorKey) { return (readUInt32(offset) ^ xorKey); } From e05bfc9c8ae3cd07ff27533afc0f45bdaca73318 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 20 Jul 2012 18:59:10 +0200 Subject: [PATCH 12/15] Decrypt strings --- de4dot.code/de4dot.code.csproj | 1 + .../deobfuscators/MaxtoCode/Deobfuscator.cs | 37 ++++- .../MaxtoCode/StringDecrypter.cs | 128 ++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 de4dot.code/deobfuscators/MaxtoCode/StringDecrypter.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 5952a72e..1873bfe0 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -74,6 +74,7 @@ + diff --git a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs index 14e5949f..38e643fe 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs @@ -17,7 +17,9 @@ along with de4dot. If not, see . */ +using System; using System.Collections.Generic; +using System.Text; using Mono.Cecil; using Mono.MyStuff; @@ -26,8 +28,11 @@ namespace de4dot.code.deobfuscators.MaxtoCode { public const string THE_NAME = "MaxtoCode"; public const string THE_TYPE = "mc"; const string DEFAULT_REGEX = @"!^[oO01l]+$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX; + IntOption stringCodePage; + public DeobfuscatorInfo() : base(DEFAULT_REGEX) { + stringCodePage = new IntOption(null, makeArgName("cp"), "String code page", 936); } public override string Name { @@ -42,16 +47,25 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return new Deobfuscator(new Deobfuscator.Options { RenameResourcesInCode = false, ValidNameRegex = validNameRegex.get(), + StringCodePage = stringCodePage.get(), }); } + + protected override IEnumerable