From 6505a2490ae91fe25c7cc4534c231400adfb91c3 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 26 Jan 2012 22:04:26 +0100 Subject: [PATCH 01/17] Add updated cecil submodule --- cecil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cecil b/cecil index 766ef4e5..979417e1 160000 --- a/cecil +++ b/cecil @@ -1 +1 @@ -Subproject commit 766ef4e529f0c8a4584a708711a33a1752e658c6 +Subproject commit 979417e1e65a69afab1b9ba6c6e63bf960cfb3fd From ec459746050a719f47cc1affe0e6580157d6b01b Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 26 Jan 2012 22:22:24 +0100 Subject: [PATCH 02/17] Speed up getUnknownValue() method. 16-17% execution time -> ~6% --- blocks/cflow/InstructionEmulator.cs | 120 ++++++++++++++-------------- 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/blocks/cflow/InstructionEmulator.cs b/blocks/cflow/InstructionEmulator.cs index 866a441a..283a770f 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/blocks/cflow/InstructionEmulator.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.Cecil.Metadata; namespace de4dot.blocks.cflow { public class InstructionEmulator { @@ -69,23 +70,21 @@ namespace de4dot.blocks.cflow { return new UnknownValue(); if (!typeReference.IsValueType) return NullValue.Instance; - else if (DotNetUtils.isAssembly(typeReference.Scope, "mscorlib")) { - switch (typeReference.FullName) { - case "System.Boolean": - case "System.SByte": - case "System.Byte": - case "System.Int16": - case "System.UInt16": - case "System.Int32": - case "System.UInt32": - return new Int32Value(0); - case "System.Int64": - case "System.UInt64": - return new Int64Value(0); - case "System.Single": - case "System.Double": - return new Real8Value(0); - } + switch (typeReference.EType) { + case ElementType.Boolean: + case ElementType.I1: + case ElementType.U1: + case ElementType.I2: + case ElementType.U2: + case ElementType.I4: + case ElementType.U4: + return new Int32Value(0); + case ElementType.I8: + case ElementType.U8: + return new Int64Value(0); + case ElementType.R4: + case ElementType.R8: + return new Real8Value(0); } return new UnknownValue(); } @@ -93,18 +92,16 @@ namespace de4dot.blocks.cflow { static Value getUnknownValue(TypeReference typeReference) { if (typeReference == null) return new UnknownValue(); - if (DotNetUtils.isAssembly(typeReference.Scope, "mscorlib")) { - switch (typeReference.FullName) { - case "System.Boolean": return Int32Value.createUnknownBool(); - case "System.SByte": return Int32Value.createUnknown(); - case "System.Byte": return Int32Value.createUnknownUInt8(); - case "System.Int16": return Int32Value.createUnknown(); - case "System.UInt16": return Int32Value.createUnknownUInt16(); - case "System.Int32": return Int32Value.createUnknown(); - case "System.UInt32": return Int32Value.createUnknown(); - case "System.Int64": return Int64Value.createUnknown(); - case "System.UInt64": return Int64Value.createUnknown(); - } + switch (typeReference.EType) { + case ElementType.Boolean: return Int32Value.createUnknownBool(); + case ElementType.I1: return Int32Value.createUnknown(); + case ElementType.U1: return Int32Value.createUnknownUInt8(); + case ElementType.I2: return Int32Value.createUnknown(); + case ElementType.U2: return Int32Value.createUnknownUInt16(); + case ElementType.I4: return Int32Value.createUnknown(); + case ElementType.U4: return Int32Value.createUnknown(); + case ElementType.I8: return Int64Value.createUnknown(); + case ElementType.U8: return Int64Value.createUnknown(); } return new UnknownValue(); } @@ -112,45 +109,44 @@ namespace de4dot.blocks.cflow { static Value truncateValue(Value value, TypeReference typeReference) { if (typeReference == null) return value; - if (DotNetUtils.isAssembly(typeReference.Scope, "mscorlib")) { - switch (typeReference.FullName) { - case "System.Boolean": - if (value.isInt32()) - return ((Int32Value)value).toBoolean(); - return Int32Value.createUnknownBool(); - case "System.SByte": - if (value.isInt32()) - return ((Int32Value)value).toInt8(); - return Int32Value.createUnknown(); + switch (typeReference.EType) { + case ElementType.Boolean: + if (value.isInt32()) + return ((Int32Value)value).toBoolean(); + return Int32Value.createUnknownBool(); - case "System.Byte": - if (value.isInt32()) - return ((Int32Value)value).toUInt8(); - return Int32Value.createUnknownUInt8(); + case ElementType.I1: + if (value.isInt32()) + return ((Int32Value)value).toInt8(); + return Int32Value.createUnknown(); - case "System.Int16": - if (value.isInt32()) - return ((Int32Value)value).toInt16(); - return Int32Value.createUnknown(); + case ElementType.U1: + if (value.isInt32()) + return ((Int32Value)value).toUInt8(); + return Int32Value.createUnknownUInt8(); - case "System.UInt16": - if (value.isInt32()) - return ((Int32Value)value).toUInt16(); - return Int32Value.createUnknownUInt16(); + case ElementType.I2: + if (value.isInt32()) + return ((Int32Value)value).toInt16(); + return Int32Value.createUnknown(); - case "System.Int32": - case "System.UInt32": - if (value.isInt32()) - return value; - return Int32Value.createUnknown(); + case ElementType.U2: + if (value.isInt32()) + return ((Int32Value)value).toUInt16(); + return Int32Value.createUnknownUInt16(); - case "System.Int64": - case "System.UInt64": - if (value.isInt64()) - return value; - return Int64Value.createUnknown(); - } + case ElementType.I4: + case ElementType.U4: + if (value.isInt32()) + return value; + return Int32Value.createUnknown(); + + case ElementType.I8: + case ElementType.U8: + if (value.isInt64()) + return value; + return Int64Value.createUnknown(); } return value; } From 247cb2be20cbafe077953925a02fa3d8ac1f38f4 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 26 Jan 2012 22:40:19 +0100 Subject: [PATCH 03/17] Compare ElementType instead of calling verifyType for speed --- blocks/DotNetUtils.cs | 3 ++- blocks/MemberReferenceHelper.cs | 3 ++- de4dot.code/deobfuscators/TypesRestorer.cs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/blocks/DotNetUtils.cs b/blocks/DotNetUtils.cs index 1a6372f8..78c9608c 100644 --- a/blocks/DotNetUtils.cs +++ b/blocks/DotNetUtils.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.Cecil.Metadata; namespace de4dot.blocks { class TypeCache { @@ -653,7 +654,7 @@ namespace de4dot.blocks { } public static bool hasReturnValue(IMethodSignature method) { - return !MemberReferenceHelper.verifyType(method.MethodReturnType.ReturnType, "mscorlib", "System.Void"); + return method.MethodReturnType.ReturnType.EType != ElementType.Void; } public static void updateStack(Instruction instr, ref int stack, bool methodHasReturnValue) { diff --git a/blocks/MemberReferenceHelper.cs b/blocks/MemberReferenceHelper.cs index 7781d459..794a41c7 100644 --- a/blocks/MemberReferenceHelper.cs +++ b/blocks/MemberReferenceHelper.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using Mono.Cecil; +using Mono.Cecil.Metadata; namespace de4dot.blocks { public enum CecilType { @@ -821,7 +822,7 @@ namespace de4dot.blocks { } public static bool isSystemObject(TypeReference typeReference) { - return verifyType(typeReference, "mscorlib", "System.Object"); + return typeReference != null && typeReference.EType == ElementType.Object; } public static string getCanonicalizedTypeRefName(TypeReference typeRef) { diff --git a/de4dot.code/deobfuscators/TypesRestorer.cs b/de4dot.code/deobfuscators/TypesRestorer.cs index 4049fac3..7328913d 100644 --- a/de4dot.code/deobfuscators/TypesRestorer.cs +++ b/de4dot.code/deobfuscators/TypesRestorer.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.Cecil.Metadata; using de4dot.blocks; namespace de4dot.code.deobfuscators { @@ -705,7 +706,7 @@ namespace de4dot.code.deobfuscators { return false; if (MemberReferenceHelper.isSystemObject(type)) return false; - if (MemberReferenceHelper.verifyType(type, "mscorlib", "System.Void")) + if (type.EType == ElementType.Void) return false; while (type != null) { From 91559b95e4297892b7bf4730814bc2d436320635 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 26 Jan 2012 23:09:50 +0100 Subject: [PATCH 04/17] Add updated cecil submodule --- cecil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cecil b/cecil index 979417e1..3fcf3ed4 160000 --- a/cecil +++ b/cecil @@ -1 +1 @@ -Subproject commit 979417e1e65a69afab1b9ba6c6e63bf960cfb3fd +Subproject commit 3fcf3ed412215646ff93203df4634006c1db44b4 From 91cc161a9d0441b4e79dc59e0179fbf915efd9c0 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 26 Jan 2012 23:57:37 +0100 Subject: [PATCH 05/17] Create instances of common constants --- blocks/cflow/InstructionEmulator.cs | 10 +++++----- blocks/cflow/Int32Value.cs | 21 ++++++++++++--------- blocks/cflow/Int64Value.cs | 21 ++++++++++++--------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/blocks/cflow/InstructionEmulator.cs b/blocks/cflow/InstructionEmulator.cs index 283a770f..20f823e7 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/blocks/cflow/InstructionEmulator.cs @@ -78,10 +78,10 @@ namespace de4dot.blocks.cflow { case ElementType.U2: case ElementType.I4: case ElementType.U4: - return new Int32Value(0); + return Int32Value.zero; case ElementType.I8: case ElementType.U8: - return new Int64Value(0); + return Int64Value.zero; case ElementType.R4: case ElementType.R8: return new Real8Value(0); @@ -278,8 +278,8 @@ namespace de4dot.blocks.cflow { case Code.Ldc_I8: valueStack.push(new Int64Value((long)instr.Operand)); break; case Code.Ldc_R4: valueStack.push(new Real8Value((float)instr.Operand)); break; case Code.Ldc_R8: valueStack.push(new Real8Value((double)instr.Operand)); break; - case Code.Ldc_I4_0: valueStack.push(new Int32Value(0)); break; - case Code.Ldc_I4_1: valueStack.push(new Int32Value(1)); break; + case Code.Ldc_I4_0: valueStack.push(Int32Value.zero); break; + case Code.Ldc_I4_1: valueStack.push(Int32Value.one); break; case Code.Ldc_I4_2: valueStack.push(new Int32Value(2)); break; case Code.Ldc_I4_3: valueStack.push(new Int32Value(3)); break; case Code.Ldc_I4_4: valueStack.push(new Int32Value(4)); break; @@ -775,7 +775,7 @@ namespace de4dot.blocks.cflow { else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Ceq((Int64Value)val1, (Int64Value)val2)); else if (val1.isNull() && val2.isNull()) - valueStack.push(new Int32Value(1)); + valueStack.push(Int32Value.one); else valueStack.push(Int32Value.createUnknownBool()); } diff --git a/blocks/cflow/Int32Value.cs b/blocks/cflow/Int32Value.cs index 68563d31..e4cd7def 100644 --- a/blocks/cflow/Int32Value.cs +++ b/blocks/cflow/Int32Value.cs @@ -21,6 +21,9 @@ using System; namespace de4dot.blocks.cflow { public class Int32Value : Value { + public static readonly Int32Value zero = new Int32Value(0); + public static readonly Int32Value one = new Int32Value(1); + const uint NO_UNKNOWN_BITS = uint.MaxValue; public readonly int value; public readonly uint validMask; @@ -223,7 +226,7 @@ namespace de4dot.blocks.cflow { if (a.allBitsValid() && b.allBitsValid()) return new Int32Value(a.value - b.value); if (ReferenceEquals(a, b)) - return new Int32Value(0); + return zero; return createUnknown(); } @@ -231,7 +234,7 @@ namespace de4dot.blocks.cflow { if (a.allBitsValid() && b.allBitsValid()) return new Int32Value(a.value * b.value); if (a.isZero() || b.isZero()) - return new Int32Value(0); + return zero; if (a.hasValue(1)) return b; if (b.hasValue(1)) @@ -249,7 +252,7 @@ namespace de4dot.blocks.cflow { } } if (ReferenceEquals(a, b) && a.isNonZero()) - return new Int32Value(1); + return one; if (b.hasValue(1)) return a; return createUnknown(); @@ -265,7 +268,7 @@ namespace de4dot.blocks.cflow { } } if (ReferenceEquals(a, b) && a.isNonZero()) - return new Int32Value(1); + return one; if (b.hasValue(1)) return a; return createUnknown(); @@ -281,7 +284,7 @@ namespace de4dot.blocks.cflow { } } if ((ReferenceEquals(a, b) && a.isNonZero()) || b.hasValue(1)) - return new Int32Value(0); + return zero; return createUnknown(); } @@ -295,7 +298,7 @@ namespace de4dot.blocks.cflow { } } if ((ReferenceEquals(a, b) && a.isNonZero()) || b.hasValue(1)) - return new Int32Value(0); + return zero; return createUnknown(); } @@ -319,7 +322,7 @@ namespace de4dot.blocks.cflow { public static Int32Value Xor(Int32Value a, Int32Value b) { if (ReferenceEquals(a, b)) - return new Int32Value(0); + return zero; int av = a.value, bv = b.value; uint am = a.validMask, bm = b.validMask; return new Int32Value(av ^ bv, (uint)(am & bm)); @@ -369,8 +372,8 @@ namespace de4dot.blocks.cflow { static Int32Value create(Bool3 b) { switch (b) { - case Bool3.False: return new Int32Value(0); - case Bool3.True: return new Int32Value(1); + case Bool3.False: return zero; + case Bool3.True: return one; default: return createUnknownBool(); } } diff --git a/blocks/cflow/Int64Value.cs b/blocks/cflow/Int64Value.cs index 248fbe04..ff4dc1f7 100644 --- a/blocks/cflow/Int64Value.cs +++ b/blocks/cflow/Int64Value.cs @@ -21,6 +21,9 @@ using System; namespace de4dot.blocks.cflow { public class Int64Value : Value { + public static readonly Int64Value zero = new Int64Value(0); + public static readonly Int64Value one = new Int64Value(1); + const ulong NO_UNKNOWN_BITS = ulong.MaxValue; public readonly long value; public readonly ulong validMask; @@ -117,7 +120,7 @@ namespace de4dot.blocks.cflow { if (a.allBitsValid() && b.allBitsValid()) return new Int64Value(a.value - b.value); if (ReferenceEquals(a, b)) - return new Int64Value(0); + return zero; return createUnknown(); } @@ -125,7 +128,7 @@ namespace de4dot.blocks.cflow { if (a.allBitsValid() && b.allBitsValid()) return new Int64Value(a.value * b.value); if (a.isZero() || b.isZero()) - return new Int64Value(0); + return zero; if (a.hasValue(1)) return b; if (b.hasValue(1)) @@ -143,7 +146,7 @@ namespace de4dot.blocks.cflow { } } if (ReferenceEquals(a, b) && a.isNonZero()) - return new Int64Value(1); + return one; if (b.hasValue(1)) return a; return createUnknown(); @@ -159,7 +162,7 @@ namespace de4dot.blocks.cflow { } } if (ReferenceEquals(a, b) && a.isNonZero()) - return new Int64Value(1); + return one; if (b.hasValue(1)) return a; return createUnknown(); @@ -175,7 +178,7 @@ namespace de4dot.blocks.cflow { } } if ((ReferenceEquals(a, b) && a.isNonZero()) || b.hasValue(1)) - return new Int64Value(0); + return zero; return createUnknown(); } @@ -189,7 +192,7 @@ namespace de4dot.blocks.cflow { } } if ((ReferenceEquals(a, b) && a.isNonZero()) || b.hasValue(1)) - return new Int64Value(0); + return zero; return createUnknown(); } @@ -213,7 +216,7 @@ namespace de4dot.blocks.cflow { public static Int64Value Xor(Int64Value a, Int64Value b) { if (ReferenceEquals(a, b)) - return new Int64Value(0); + return zero; long av = a.value, bv = b.value; ulong am = a.validMask, bm = b.validMask; return new Int64Value(av ^ bv, am & bm); @@ -263,8 +266,8 @@ namespace de4dot.blocks.cflow { static Int32Value create(Bool3 b) { switch (b) { - case Bool3.False: return new Int32Value(0); - case Bool3.True: return new Int32Value(1); + case Bool3.False: return Int32Value.zero; + case Bool3.True: return Int32Value.one; default: return Int32Value.createUnknownBool(); } } From cac39b8a0166278df6791cb124af29582f8ed044 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 27 Jan 2012 00:16:23 +0100 Subject: [PATCH 06/17] Don't use cecil coll iterator for speed --- blocks/cflow/InstructionEmulator.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/blocks/cflow/InstructionEmulator.cs b/blocks/cflow/InstructionEmulator.cs index 20f823e7..bff9ba1c 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/blocks/cflow/InstructionEmulator.cs @@ -50,18 +50,18 @@ namespace de4dot.blocks.cflow { argBase = 1; args.Add(new UnknownValue()); } - foreach (var arg in parameterDefinitions) - args.Add(getUnknownValue(arg.ParameterType)); + for (int i = 0; i < parameterDefinitions.Count; i++) + args.Add(getUnknownValue(parameterDefinitions[i].ParameterType)); if (initLocals) { locals.Clear(); - foreach (var local in variableDefinitions) - locals.Add(getDefaultValue(local.VariableType)); + for (int i = 0; i < variableDefinitions.Count; i++) + locals.Add(getDefaultValue(variableDefinitions[i].VariableType)); } else { locals.Clear(); - foreach (var local in variableDefinitions) - locals.Add(getUnknownValue(local.VariableType)); + for (int i = 0; i < variableDefinitions.Count; i++) + locals.Add(getUnknownValue(variableDefinitions[i].VariableType)); } } From cd46fb9793b2e1737733e54869559fd6a0a87c47 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 27 Jan 2012 00:30:53 +0100 Subject: [PATCH 07/17] Don't use iterator for speed --- blocks/Block.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/blocks/Block.cs b/blocks/Block.cs index a8116e9d..1f10cf1e 100644 --- a/blocks/Block.cs +++ b/blocks/Block.cs @@ -265,9 +265,10 @@ namespace de4dot.blocks { updateSources(); } - void addInstructions(IList dest, IEnumerable instrs) { - foreach (var instr in instrs) { - if (!instr.isNop()) + void addInstructions(IList dest, IList instrs) { + for (int i = 0; i < instrs.Count; i++) { + var instr = instrs[i]; + if (instr.OpCode != OpCodes.Nop) dest.Add(instr); } } From 887ee7c9e80c61bdc7683acdc24bcbe663b5753e Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 27 Jan 2012 01:02:17 +0100 Subject: [PATCH 08/17] Fix method signature --- blocks/cflow/BlockCflowDeobfuscator.cs | 2 +- blocks/cflow/ConstantsFolder.cs | 2 +- blocks/cflow/InstructionEmulator.cs | 18 ++++++++++++------ blocks/cflow/SwitchCflowDeobfuscator.cs | 10 +++++----- de4dot.code/deobfuscators/ArrayFinder.cs | 2 +- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/blocks/cflow/BlockCflowDeobfuscator.cs b/blocks/cflow/BlockCflowDeobfuscator.cs index 909aff92..2426d8ec 100644 --- a/blocks/cflow/BlockCflowDeobfuscator.cs +++ b/blocks/cflow/BlockCflowDeobfuscator.cs @@ -30,7 +30,7 @@ namespace de4dot.blocks.cflow { public void init(Blocks blocks, Block block) { this.blocks = blocks; this.block = block; - instructionEmulator.init(blocks.Method.HasImplicitThis, false, blocks.Method.Parameters, blocks.Locals); + instructionEmulator.init(blocks); } // Returns true if code was updated, false otherwise diff --git a/blocks/cflow/ConstantsFolder.cs b/blocks/cflow/ConstantsFolder.cs index 0a567297..e406a0b6 100644 --- a/blocks/cflow/ConstantsFolder.cs +++ b/blocks/cflow/ConstantsFolder.cs @@ -39,7 +39,7 @@ namespace de4dot.blocks.cflow { public bool deobfuscate() { bool changed = false; foreach (var block in allBlocks) { - instructionEmulator.init(blocks.Method.HasImplicitThis, false, blocks.Method.Parameters, blocks.Locals); + instructionEmulator.init(blocks); var instrs = block.Instructions; for (int i = 0; i < instrs.Count; i++) { var instr = instrs[i]; diff --git a/blocks/cflow/InstructionEmulator.cs b/blocks/cflow/InstructionEmulator.cs index bff9ba1c..998b245b 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/blocks/cflow/InstructionEmulator.cs @@ -35,18 +35,24 @@ namespace de4dot.blocks.cflow { public InstructionEmulator() { } - public InstructionEmulator(bool implicitThis, bool initLocals, IList parameterDefinitions, IList variableDefinitions) { - init(implicitThis, initLocals, parameterDefinitions, variableDefinitions); + public InstructionEmulator(MethodDefinition method) { + init(method); } - public void init(bool implicitThis, bool initLocals, IList parameterDefinitions, IList variableDefinitions) { - this.parameterDefinitions = parameterDefinitions; - this.variableDefinitions = variableDefinitions; + public void init(Blocks blocks) { + init(blocks.Method); + } + + public void init(MethodDefinition method) { + bool initLocals = false; + + this.parameterDefinitions = method.Parameters; + this.variableDefinitions = method.Body.Variables; valueStack.init(); args.Clear(); argBase = 0; - if (implicitThis) { + if (method.HasImplicitThis) { argBase = 1; args.Add(new UnknownValue()); } diff --git a/blocks/cflow/SwitchCflowDeobfuscator.cs b/blocks/cflow/SwitchCflowDeobfuscator.cs index 93c45a32..166a4fbb 100644 --- a/blocks/cflow/SwitchCflowDeobfuscator.cs +++ b/blocks/cflow/SwitchCflowDeobfuscator.cs @@ -144,7 +144,7 @@ namespace de4dot.blocks.cflow { foreach (var source in new List(block.Sources)) { if (!isBranchBlock(source)) continue; - instructionEmulator.init(blocks.Method.HasImplicitThis, false, blocks.Method.Parameters, blocks.Locals); + instructionEmulator.init(blocks); instructionEmulator.emulate(source.Instructions); var target = getSwitchTarget(switchTargets, switchFallThrough, source, instructionEmulator.pop()); @@ -170,7 +170,7 @@ namespace de4dot.blocks.cflow { foreach (var source in new List(block.Sources)) { if (!isBranchBlock(source)) continue; - instructionEmulator.init(blocks.Method.HasImplicitThis, false, blocks.Method.Parameters, blocks.Locals); + instructionEmulator.init(blocks); instructionEmulator.emulate(source.Instructions); var target = getSwitchTarget(switchTargets, switchFallThrough, source, instructionEmulator.getLocal(switchVariable)); @@ -193,7 +193,7 @@ namespace de4dot.blocks.cflow { foreach (var source in new List(block.Sources)) { if (!isBranchBlock(source)) continue; - instructionEmulator.init(blocks.Method.HasImplicitThis, false, blocks.Method.Parameters, blocks.Locals); + instructionEmulator.init(blocks); instructionEmulator.emulate(source.Instructions); var target = getSwitchTarget(switchTargets, switchFallThrough, source, instructionEmulator.pop()); @@ -264,7 +264,7 @@ namespace de4dot.blocks.cflow { } bool emulateGetTarget(Block switchBlock, out Block target) { - instructionEmulator.init(blocks.Method.HasImplicitThis, false, blocks.Method.Parameters, blocks.Locals); + instructionEmulator.init(blocks); try { instructionEmulator.emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1); } @@ -278,7 +278,7 @@ namespace de4dot.blocks.cflow { } bool willHaveKnownTarget(Block switchBlock, Block source) { - instructionEmulator.init(blocks.Method.HasImplicitThis, false, blocks.Method.Parameters, blocks.Locals); + instructionEmulator.init(blocks); try { instructionEmulator.emulate(source.Instructions); instructionEmulator.emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1); diff --git a/de4dot.code/deobfuscators/ArrayFinder.cs b/de4dot.code/deobfuscators/ArrayFinder.cs index 085ec39e..85d905a4 100644 --- a/de4dot.code/deobfuscators/ArrayFinder.cs +++ b/de4dot.code/deobfuscators/ArrayFinder.cs @@ -126,7 +126,7 @@ namespace de4dot.code.deobfuscators { public static Value[] getInitializedArray(int arraySize, MethodDefinition method, ref int newarrIndex, Code stelemOpCode) { var resultValueArray = new Value[arraySize]; - var emulator = new InstructionEmulator(method.HasImplicitThis, false, method.Parameters, method.Body.Variables); + var emulator = new InstructionEmulator(method); var theArray = new UnknownValue(); emulator.push(theArray); From dce16e9f12c138c12a9e6448d606cbd83d94e353 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 27 Jan 2012 01:03:41 +0100 Subject: [PATCH 09/17] Remove useless code --- blocks/cflow/InstructionEmulator.cs | 39 +++-------------------------- 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/blocks/cflow/InstructionEmulator.cs b/blocks/cflow/InstructionEmulator.cs index 998b245b..8dd346fd 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/blocks/cflow/InstructionEmulator.cs @@ -44,8 +44,6 @@ namespace de4dot.blocks.cflow { } public void init(MethodDefinition method) { - bool initLocals = false; - this.parameterDefinitions = method.Parameters; this.variableDefinitions = method.Body.Variables; valueStack.init(); @@ -59,40 +57,9 @@ namespace de4dot.blocks.cflow { for (int i = 0; i < parameterDefinitions.Count; i++) args.Add(getUnknownValue(parameterDefinitions[i].ParameterType)); - if (initLocals) { - locals.Clear(); - for (int i = 0; i < variableDefinitions.Count; i++) - locals.Add(getDefaultValue(variableDefinitions[i].VariableType)); - } - else { - locals.Clear(); - for (int i = 0; i < variableDefinitions.Count; i++) - locals.Add(getUnknownValue(variableDefinitions[i].VariableType)); - } - } - - static Value getDefaultValue(TypeReference typeReference) { - if (typeReference == null) - return new UnknownValue(); - if (!typeReference.IsValueType) - return NullValue.Instance; - switch (typeReference.EType) { - case ElementType.Boolean: - case ElementType.I1: - case ElementType.U1: - case ElementType.I2: - case ElementType.U2: - case ElementType.I4: - case ElementType.U4: - return Int32Value.zero; - case ElementType.I8: - case ElementType.U8: - return Int64Value.zero; - case ElementType.R4: - case ElementType.R8: - return new Real8Value(0); - } - return new UnknownValue(); + locals.Clear(); + for (int i = 0; i < variableDefinitions.Count; i++) + locals.Add(getUnknownValue(variableDefinitions[i].VariableType)); } static Value getUnknownValue(TypeReference typeReference) { From 96c13fb05e8cc516e692cfa377b4d8bff4716b74 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 27 Jan 2012 01:19:18 +0100 Subject: [PATCH 10/17] Cache values to get 30% speed up with some files (DNR) --- blocks/cflow/InstructionEmulator.cs | 35 ++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/blocks/cflow/InstructionEmulator.cs b/blocks/cflow/InstructionEmulator.cs index 8dd346fd..786f5b09 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/blocks/cflow/InstructionEmulator.cs @@ -32,6 +32,11 @@ namespace de4dot.blocks.cflow { List locals = new List(); int argBase; + MethodDefinition prev_method; + List cached_args = new List(); + List cached_locals = new List(); + int cached_argBase; + public InstructionEmulator() { } @@ -48,18 +53,28 @@ namespace de4dot.blocks.cflow { this.variableDefinitions = method.Body.Variables; valueStack.init(); - args.Clear(); - argBase = 0; - if (method.HasImplicitThis) { - argBase = 1; - args.Add(new UnknownValue()); - } - for (int i = 0; i < parameterDefinitions.Count; i++) - args.Add(getUnknownValue(parameterDefinitions[i].ParameterType)); + if (method != prev_method) { + prev_method = method; + cached_args.Clear(); + cached_argBase = 0; + if (method.HasImplicitThis) { + cached_argBase = 1; + cached_args.Add(new UnknownValue()); + } + for (int i = 0; i < parameterDefinitions.Count; i++) + cached_args.Add(getUnknownValue(parameterDefinitions[i].ParameterType)); + + cached_locals.Clear(); + for (int i = 0; i < variableDefinitions.Count; i++) + cached_locals.Add(getUnknownValue(variableDefinitions[i].VariableType)); + } + + argBase = cached_argBase; + args.Clear(); + args.AddRange(cached_args); locals.Clear(); - for (int i = 0; i < variableDefinitions.Count; i++) - locals.Add(getUnknownValue(variableDefinitions[i].VariableType)); + locals.AddRange(cached_locals); } static Value getUnknownValue(TypeReference typeReference) { From 50e7d28ddfc47380271c97573a1322f1bc5c6b64 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 27 Jan 2012 05:39:25 +0100 Subject: [PATCH 11/17] Speed up method param renaming code --- de4dot.code/renamer/TypeInfo.cs | 4 +- de4dot.code/renamer/TypeNames.cs | 4 +- de4dot.code/renamer/VariableNameState.cs | 48 ++++++++++++++++++------ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/de4dot.code/renamer/TypeInfo.cs b/de4dot.code/renamer/TypeInfo.cs index b961e6c8..4040e438 100644 --- a/de4dot.code/renamer/TypeInfo.cs +++ b/de4dot.code/renamer/TypeInfo.cs @@ -29,7 +29,7 @@ namespace de4dot.code.renamer { class TypeInfo : MemberInfo { public string oldNamespace; public string newNamespace; - public VariableNameState variableNameState = new VariableNameState(); + public VariableNameState variableNameState = VariableNameState.create(); public TypeDef type; MemberInfos memberInfos; @@ -306,7 +306,7 @@ namespace de4dot.code.renamer { info.newName = "e"; } else { - var newVariableNameState = variableNameState.clone(); + var newVariableNameState = variableNameState.cloneParamsOnly(); var checker = NameChecker; foreach (var paramDef in methodDef.ParamDefs) { var info = param(paramDef); diff --git a/de4dot.code/renamer/TypeNames.cs b/de4dot.code/renamer/TypeNames.cs index cafb67c6..600b24f9 100644 --- a/de4dot.code/renamer/TypeNames.cs +++ b/de4dot.code/renamer/TypeNames.cs @@ -96,7 +96,9 @@ namespace de4dot.code.renamer { } class VariableNameCreator : TypeNames { - public VariableNameCreator() { + public VariableNameCreator(bool init = true) { + if (!init) + return; initTypeName("System.Boolean", "bool"); initTypeName("System.Byte", "byte"); initTypeName("System.Char", "char"); diff --git a/de4dot.code/renamer/VariableNameState.cs b/de4dot.code/renamer/VariableNameState.cs index 41dfd5f5..41c9b24e 100644 --- a/de4dot.code/renamer/VariableNameState.cs +++ b/de4dot.code/renamer/VariableNameState.cs @@ -21,19 +21,43 @@ using Mono.Cecil; namespace de4dot.code.renamer { class VariableNameState { - ExistingNames existingVariableNames = new ExistingNames(); - ExistingNames existingMethodNames = new ExistingNames(); - ExistingNames existingPropertyNames = new ExistingNames(); - ExistingNames existingEventNames = new ExistingNames(); - TypeNames variableNameCreator = new VariableNameCreator(); // For fields and method args - TypeNames propertyNameCreator = new PropertyNameCreator(); - NameCreator eventNameCreator = new NameCreator("Event_"); - NameCreator genericPropertyNameCreator = new NameCreator("Prop_"); - public NameCreator staticMethodNameCreator = new NameCreator("smethod_"); - public NameCreator instanceMethodNameCreator = new NameCreator("method_"); + ExistingNames existingVariableNames; + ExistingNames existingMethodNames; + ExistingNames existingPropertyNames; + ExistingNames existingEventNames; + TypeNames variableNameCreator; // For fields and method args + TypeNames propertyNameCreator; + NameCreator eventNameCreator; + NameCreator genericPropertyNameCreator; + public NameCreator staticMethodNameCreator; + public NameCreator instanceMethodNameCreator; - public VariableNameState clone() { - return new VariableNameState().merge(this); + public static VariableNameState create() { + var vns = new VariableNameState(); + vns.existingVariableNames = new ExistingNames(); + vns.existingMethodNames = new ExistingNames(); + vns.existingPropertyNames = new ExistingNames(); + vns.existingEventNames = new ExistingNames(); + vns.variableNameCreator = new VariableNameCreator(); + vns.propertyNameCreator = new PropertyNameCreator(); + vns.eventNameCreator = new NameCreator("Event_"); + vns.genericPropertyNameCreator = new NameCreator("Prop_"); + vns.staticMethodNameCreator = new NameCreator("smethod_"); + vns.instanceMethodNameCreator = new NameCreator("method_"); + return vns; + } + + VariableNameState() { + } + + // Cloning only params will speed up the method param renaming code + public VariableNameState cloneParamsOnly() { + var vns = new VariableNameState(); + vns.existingVariableNames = new ExistingNames(); + vns.variableNameCreator = new VariableNameCreator(false); + vns.existingVariableNames.merge(existingVariableNames); + vns.variableNameCreator.merge(variableNameCreator); + return vns; } public VariableNameState merge(VariableNameState other) { From 9e1412a6ae5fcd5d7d948787078a0417cf1fc0cb Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 27 Jan 2012 05:54:23 +0100 Subject: [PATCH 12/17] Use TryGetValue to speed it up a little --- de4dot.code/renamer/TypeNames.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/de4dot.code/renamer/TypeNames.cs b/de4dot.code/renamer/TypeNames.cs index 600b24f9..1a3b5a69 100644 --- a/de4dot.code/renamer/TypeNames.cs +++ b/de4dot.code/renamer/TypeNames.cs @@ -81,8 +81,9 @@ namespace de4dot.code.renamer { public virtual TypeNames merge(TypeNames other) { foreach (var pair in other.typeNames) { - if (typeNames.ContainsKey(pair.Key)) - typeNames[pair.Key].merge(pair.Value); + NameCreator nc; + if (typeNames.TryGetValue(pair.Key, out nc)) + nc.merge(pair.Value); else typeNames[pair.Key] = pair.Value.clone(); } From e9f6e2930d272751c633985c1324dbee19ae3de1 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 27 Jan 2012 16:14:54 +0100 Subject: [PATCH 13/17] Add updated cecil submodule --- cecil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cecil b/cecil index 3fcf3ed4..b12e7577 160000 --- a/cecil +++ b/cecil @@ -1 +1 @@ -Subproject commit 3fcf3ed412215646ff93203df4634006c1db44b4 +Subproject commit b12e75770c5ab14fbdfe141fd6dc1668adc71436 From 257456fd8b2b1be9a3dc54288b99ebf883df8d0f Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 27 Jan 2012 22:01:20 +0100 Subject: [PATCH 14/17] Speed up renaming by storing less names in the typeNames dict. merge() was pretty slow but is much faster now. --- de4dot.code/renamer/TypeNames.cs | 113 ++++++++++++++--------- de4dot.code/renamer/VariableNameState.cs | 2 +- 2 files changed, 70 insertions(+), 45 deletions(-) diff --git a/de4dot.code/renamer/TypeNames.cs b/de4dot.code/renamer/TypeNames.cs index 1a3b5a69..91a4d94f 100644 --- a/de4dot.code/renamer/TypeNames.cs +++ b/de4dot.code/renamer/TypeNames.cs @@ -25,6 +25,8 @@ namespace de4dot.code.renamer { abstract class TypeNames { protected Dictionary typeNames = new Dictionary(StringComparer.Ordinal); protected NameCreator genericParamNameCreator = new NameCreator("gparam_"); + protected Dictionary fullNameToShortName; + protected Dictionary fullNameToShortNamePrefix; public string create(TypeReference typeRef) { if (typeRef.IsGenericInstance) { @@ -46,21 +48,28 @@ namespace de4dot.code.renamer { if (typeNames.TryGetValue(typeFullName, out nc)) return nc.create(); - var name = elementType.FullName; - var parts = name.Replace('/', '.').Split(new char[] { '.' }); - var newName = parts[parts.Length - 1]; - int tickIndex = newName.LastIndexOf('`'); - if (tickIndex > 0) - newName = newName.Substring(0, tickIndex); + var fullName = elementType.FullName; + string shortName; + var dict = prefix == "" ? fullNameToShortName : fullNameToShortNamePrefix; + if (!dict.TryGetValue(fullName, out shortName)) { + fullName = fullName.Replace('/', '.'); + int index = fullName.LastIndexOf('.'); + shortName = index > 0 ? fullName.Substring(index + 1) : fullName; - return addTypeName(typeFullName, newName, prefix).create(); + index = shortName.LastIndexOf('`'); + if (index > 0) + shortName = shortName.Substring(0, index); + } + + return addTypeName(typeFullName, shortName, prefix).create(); } static string getPrefix(TypeReference typeRef) { string prefix = ""; - while (typeRef is PointerType) { - typeRef = ((PointerType)typeRef).ElementType; - prefix += "p"; + while (typeRef is TypeSpecification) { + if (typeRef.IsPointer) + prefix += "p"; + typeRef = ((TypeSpecification)typeRef).ElementType; } return prefix; } @@ -97,42 +106,50 @@ namespace de4dot.code.renamer { } class VariableNameCreator : TypeNames { - public VariableNameCreator(bool init = true) { - if (!init) - return; - initTypeName("System.Boolean", "bool"); - initTypeName("System.Byte", "byte"); - initTypeName("System.Char", "char"); - initTypeName("System.Double", "double"); - initTypeName("System.Int16", "short"); - initTypeName("System.Int32", "int"); - initTypeName("System.Int64", "long"); - initTypeName("System.IntPtr", "intptr", "IntPtr"); - initTypeName("System.SByte", "sbyte", "SByte"); - initTypeName("System.Single", "float"); - initTypeName("System.String", "string"); - initTypeName("System.UInt16", "ushort", "UShort"); - initTypeName("System.UInt32", "uint", "UInt"); - initTypeName("System.UInt64", "ulong", "ULong"); - initTypeName("System.UIntPtr", "uintptr", "UIntPtr"); - initTypeName("System.Decimal", "decimal"); + static Dictionary ourFullNameToShortName; + static Dictionary ourFullNameToShortNamePrefix; + static VariableNameCreator() { + ourFullNameToShortName = new Dictionary(StringComparer.Ordinal) { + { "System.Boolean", "bool" }, + { "System.Byte", "byte" }, + { "System.Char", "char" }, + { "System.Double", "double" }, + { "System.Int16", "short" }, + { "System.Int32", "int" }, + { "System.Int64", "long" }, + { "System.IntPtr", "intptr" }, + { "System.SByte", "sbyte" }, + { "System.Single", "float" }, + { "System.String", "string" }, + { "System.UInt16", "ushort" }, + { "System.UInt32", "uint" }, + { "System.UInt64", "ulong" }, + { "System.UIntPtr", "uintptr" }, + { "System.Decimal", "decimal" }, + }; + ourFullNameToShortNamePrefix = new Dictionary(StringComparer.Ordinal) { + { "System.Boolean", "Bool" }, + { "System.Byte", "Byte" }, + { "System.Char", "Char" }, + { "System.Double", "Double" }, + { "System.Int16", "Short" }, + { "System.Int32", "Int" }, + { "System.Int64", "Long" }, + { "System.IntPtr", "IntPtr" }, + { "System.SByte", "SByte" }, + { "System.Single", "Float" }, + { "System.String", "String" }, + { "System.UInt16", "UShort" }, + { "System.UInt32", "UInt" }, + { "System.UInt64", "ULong" }, + { "System.UIntPtr", "UIntPtr" }, + { "System.Decimal", "Decimal" }, + }; } - void initTypeName(string fullName, string newName, string ptrName = null) { - if (ptrName == null) - ptrName = upperFirst(newName); - initTypeName2(fullName, "", newName); - initTypeName2(fullName + "[]", "", newName); - initTypeName2(fullName + "[][]", "", newName); - initTypeName2(fullName + "[][][]", "", newName); - initTypeName2(fullName + "[0...,0...]", "", newName); - initTypeName2(fullName + "*", "p", ptrName); - initTypeName2(fullName + "**", "pp", ptrName); - } - - void initTypeName2(string fullName, string prefix, string newName) { - addTypeName(fullName, newName, prefix); - addTypeName(fullName + "&", newName, prefix); + public VariableNameCreator() { + fullNameToShortName = ourFullNameToShortName; + fullNameToShortNamePrefix = ourFullNameToShortNamePrefix; } static string lowerLeadingChars(string name) { @@ -155,6 +172,14 @@ namespace de4dot.code.renamer { } class PropertyNameCreator : TypeNames { + static Dictionary ourFullNameToShortName = new Dictionary(StringComparer.Ordinal); + static Dictionary ourFullNameToShortNamePrefix = new Dictionary(StringComparer.Ordinal); + + public PropertyNameCreator() { + fullNameToShortName = ourFullNameToShortName; + fullNameToShortNamePrefix = ourFullNameToShortNamePrefix; + } + protected override string fixName(string prefix, string name) { return prefix.ToUpperInvariant() + upperFirst(name); } diff --git a/de4dot.code/renamer/VariableNameState.cs b/de4dot.code/renamer/VariableNameState.cs index 41c9b24e..67dcd0d4 100644 --- a/de4dot.code/renamer/VariableNameState.cs +++ b/de4dot.code/renamer/VariableNameState.cs @@ -54,7 +54,7 @@ namespace de4dot.code.renamer { public VariableNameState cloneParamsOnly() { var vns = new VariableNameState(); vns.existingVariableNames = new ExistingNames(); - vns.variableNameCreator = new VariableNameCreator(false); + vns.variableNameCreator = new VariableNameCreator(); vns.existingVariableNames.merge(existingVariableNames); vns.variableNameCreator.merge(variableNameCreator); return vns; From f75075ab15422fefc9542193fb24a5a050187ddc Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 28 Jan 2012 00:32:27 +0100 Subject: [PATCH 15/17] Add XNA assembly search paths --- de4dot.code/AssemblyResolver.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/de4dot.code/AssemblyResolver.cs b/de4dot.code/AssemblyResolver.cs index a02614aa..33b96c92 100644 --- a/de4dot.code/AssemblyResolver.cs +++ b/de4dot.code/AssemblyResolver.cs @@ -64,6 +64,12 @@ namespace de4dot.code { addIfExists(path, @"Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies"); addIfExists(path, @"Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies"); addIfExists(path, @"Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies"); + addIfExists(path, @"Microsoft XNA\XNA Game Studio\v2.0\References\Windows\x86"); + addIfExists(path, @"Microsoft XNA\XNA Game Studio\v2.0\References\Xbox360"); + addIfExists(path, @"Microsoft XNA\XNA Game Studio\v3.0\References\Windows\x86"); + addIfExists(path, @"Microsoft XNA\XNA Game Studio\v3.0\References\Xbox360"); + addIfExists(path, @"Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86"); + addIfExists(path, @"Microsoft XNA\XNA Game Studio\v4.0\References\Xbox360"); } // basePath is eg. "C:\Program Files (x86)\Microsoft Silverlight" From 915018c2fcff4d657499c5f2b24f9353bd0c464e Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 28 Jan 2012 02:54:12 +0100 Subject: [PATCH 16/17] Use a better method dictionary --- de4dot.code/deobfuscators/ExceptionLoggerRemover.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/de4dot.code/deobfuscators/ExceptionLoggerRemover.cs b/de4dot.code/deobfuscators/ExceptionLoggerRemover.cs index aa48f8eb..d3b204c9 100644 --- a/de4dot.code/deobfuscators/ExceptionLoggerRemover.cs +++ b/de4dot.code/deobfuscators/ExceptionLoggerRemover.cs @@ -24,12 +24,12 @@ using de4dot.blocks; namespace de4dot.code.deobfuscators { class ExceptionLoggerRemover { - Dictionary exceptionLoggerMethods = new Dictionary(); + MethodDefinitionAndDeclaringTypeDict exceptionLoggerMethods = new MethodDefinitionAndDeclaringTypeDict(); public int NumRemovedExceptionLoggers { get; set; } public void add(MethodDefinition exceptionLogger) { - exceptionLoggerMethods[exceptionLogger] = true; + exceptionLoggerMethods.add(exceptionLogger, true); } bool find(Blocks blocks, out TryBlock tryBlock) { @@ -87,7 +87,7 @@ namespace de4dot.code.deobfuscators { } protected virtual bool isExceptionLogger(MethodReference method) { - return exceptionLoggerMethods.ContainsKey(method); + return exceptionLoggerMethods.find(method); } protected virtual bool HasExceptionLoggers { From 1141a451ac291128336b1e2e9f1001f303e664c2 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 28 Jan 2012 18:37:02 +0100 Subject: [PATCH 17/17] Update resource renamer code. - Faster code - Renames resource even if it doesn't end in '.resources' --- de4dot.code/de4dot.code.csproj | 1 + de4dot.code/renamer/Renamer.cs | 102 +--------------- de4dot.code/renamer/ResourceRenamer.cs | 163 +++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 101 deletions(-) create mode 100644 de4dot.code/renamer/ResourceRenamer.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index c0ad749b..cd498c8e 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -182,6 +182,7 @@ + diff --git a/de4dot.code/renamer/Renamer.cs b/de4dot.code/renamer/Renamer.cs index aa47eee2..6c70465b 100644 --- a/de4dot.code/renamer/Renamer.cs +++ b/de4dot.code/renamer/Renamer.cs @@ -331,107 +331,7 @@ namespace de4dot.code.renamer { if (renamedTypes.Count == 0) return; - // Rename the longest names first. Otherwise eg. b.g.resources could be renamed - // Class0.g.resources instead of Class1.resources when b.g was renamed Class1. - renamedTypes.Sort((a, b) => Utils.compareInt32(b.oldFullName.Length, a.oldFullName.Length)); - - renameResourceNamesInCode(module, renamedTypes); - renameResources(module, renamedTypes); - } - - void renameResourceNamesInCode(Module module, IEnumerable renamedTypes) { - // This is needed to speed up this method - var oldToNewTypeName = new Dictionary(StringComparer.Ordinal); - foreach (var info in renamedTypes) - oldToNewTypeName[info.oldFullName] = info.type.TypeDefinition.FullName; - - List validResourceNames = new List(); - if (module.ModuleDefinition.Resources != null) { - foreach (var resource in module.ModuleDefinition.Resources) { - var name = resource.Name; - if (name.EndsWith(".resources", StringComparison.Ordinal)) - validResourceNames.Add(name); - } - } - - foreach (var method in module.getAllMethods()) { - if (!method.HasBody) - continue; - foreach (var instr in method.Body.Instructions) { - if (instr.OpCode != OpCodes.Ldstr) - continue; - var s = (string)instr.Operand; - if (string.IsNullOrEmpty(s)) - continue; // Ignore emtpy strings since we'll get lots of false warnings - - string newName = null; - string oldName = null; - if (oldToNewTypeName.ContainsKey(s)) { - oldName = s; - newName = oldToNewTypeName[s]; - } - else if (s.EndsWith(".resources", StringComparison.Ordinal)) { - // This should rarely, if ever, execute... - foreach (var info in renamedTypes) { // Slow loop - var newName2 = renameResourceString(s, info.oldFullName, info.type.TypeDefinition.FullName); - if (newName2 != s) { - newName = newName2; - oldName = info.oldFullName; - break; - } - } - } - if (newName == null || string.IsNullOrEmpty(oldName)) - continue; - - bool isValid = false; - foreach (var validName in validResourceNames) { - if (Utils.StartsWith(validName, oldName, StringComparison.Ordinal)) { - isValid = true; - break; - } - } - if (!isValid) - continue; - - if (s == "" || !module.ObfuscatedFile.RenameResourcesInCode) - Log.v("Possible resource name in code: '{0}' => '{1}' in method {2}", Utils.removeNewlines(s), newName, Utils.removeNewlines(method)); - else { - instr.Operand = newName; - Log.v("Renamed resource string in code: '{0}' => '{1}' ({2})", Utils.removeNewlines(s), newName, Utils.removeNewlines(method)); - break; - } - } - } - } - - void renameResources(Module module, IEnumerable renamedTypes) { - if (module.ModuleDefinition.Resources == null) - return; - foreach (var resource in module.ModuleDefinition.Resources) { - var s = resource.Name; - foreach (var info in renamedTypes) { - var newName = renameResourceString(s, info.oldFullName, info.type.TypeDefinition.FullName); - if (newName != s) { - resource.Name = newName; - Log.v("Renamed resource in resources: {0} => {1}", Utils.removeNewlines(s), newName); - break; - } - } - } - } - - static string renameResourceString(string s, string oldTypeName, string newTypeName) { - if (!Utils.StartsWith(s, oldTypeName, StringComparison.Ordinal)) - return s; - if (s.Length == oldTypeName.Length) - return newTypeName; - // s.Length > oldTypeName.Length - if (s[oldTypeName.Length] != '.') - return s; - if (!s.EndsWith(".resources", StringComparison.Ordinal)) - return s; - return newTypeName + s.Substring(oldTypeName.Length); + new ResourceRenamer(module).rename(renamedTypes); } // Make sure the renamed types are using valid CLS names. That means renaming all diff --git a/de4dot.code/renamer/ResourceRenamer.cs b/de4dot.code/renamer/ResourceRenamer.cs new file mode 100644 index 00000000..cb92a9c5 --- /dev/null +++ b/de4dot.code/renamer/ResourceRenamer.cs @@ -0,0 +1,163 @@ +/* + Copyright (C) 2011-2012 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; +using de4dot.code.renamer.asmmodules; + +namespace de4dot.code.renamer { + class ResourceRenamer { + Module module; + Dictionary nameToResource; + + public ResourceRenamer(Module module) { + this.module = module; + } + + public void rename(List renamedTypes) { + // Rename the longest names first. Otherwise eg. b.g.resources could be renamed + // Class0.g.resources instead of Class1.resources when b.g was renamed Class1. + renamedTypes.Sort((a, b) => Utils.compareInt32(b.oldFullName.Length, a.oldFullName.Length)); + + nameToResource = new Dictionary(module.ModuleDefinition.Resources.Count * 3, StringComparer.Ordinal); + foreach (var resource in module.ModuleDefinition.Resources) { + var name = resource.Name; + nameToResource[name] = resource; + if (name.EndsWith(".g.resources")) + nameToResource[name.Substring(0, name.Length - 12)] = resource; + int index = name.LastIndexOf('.'); + if (index > 0) + nameToResource[name.Substring(0, index)] = resource; + } + + renameResourceNamesInCode(renamedTypes); + renameResources(renamedTypes); + } + + void renameResourceNamesInCode(List renamedTypes) { + var oldNameToTypeInfo = new Dictionary(StringComparer.Ordinal); + foreach (var info in renamedTypes) + oldNameToTypeInfo[info.oldFullName] = info; + + foreach (var method in module.getAllMethods()) { + if (!method.HasBody) + continue; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count; i++) { + var instr = instrs[i]; + if (instr.OpCode != OpCodes.Ldstr) + continue; + var codeString = (string)instr.Operand; + if (string.IsNullOrEmpty(codeString)) + continue; + + Resource resource; + if (!nameToResource.TryGetValue(codeString, out resource)) + continue; + + TypeInfo typeInfo; + if (!oldNameToTypeInfo.TryGetValue(codeString, out typeInfo)) + continue; + var newName = typeInfo.type.TypeDefinition.FullName; + + bool renameCodeString = module.ObfuscatedFile.RenameResourcesInCode || + isCallingResourceManagerCtor(instrs, i, typeInfo); + if (!renameCodeString) + Log.v("Possible resource name in code: '{0}' => '{1}' in method {2}", Utils.removeNewlines(codeString), newName, Utils.removeNewlines(method)); + else { + instr.Operand = newName; + Log.v("Renamed resource string in code: '{0}' => '{1}' ({2})", Utils.removeNewlines(codeString), newName, Utils.removeNewlines(method)); + } + } + } + } + + static bool isCallingResourceManagerCtor(IList instrs, int ldstrIndex, TypeInfo typeInfo) { + try { + int index = ldstrIndex + 1; + + var ldtoken = instrs[index++]; + if (ldtoken.OpCode.Code != Code.Ldtoken) + return false; + if (!MemberReferenceHelper.compareTypes(typeInfo.type.TypeDefinition, ldtoken.Operand as TypeReference)) + return false; + + if (!checkCalledMethod(instrs[index++], "System.Type", "(System.RuntimeTypeHandle)")) + return false; + if (!checkCalledMethod(instrs[index++], "System.Reflection.Assembly", "()")) + return false; + + var newobj = instrs[index++]; + if (newobj.OpCode.Code != Code.Newobj) + return false; + if (newobj.Operand.ToString() != "System.Void System.Resources.ResourceManager::.ctor(System.String,System.Reflection.Assembly)") + return false; + + return true; + } + catch (ArgumentOutOfRangeException) { + return false; + } + catch (IndexOutOfRangeException) { + return false; + } + } + + static bool checkCalledMethod(Instruction instr, string returnType, string parameters) { + if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt) + return false; + return DotNetUtils.isMethod(instr.Operand as MethodReference, returnType, parameters); + } + + class RenameInfo { + public Resource resource; + public TypeInfo typeInfo; + public string newResourceName; + public RenameInfo(Resource resource, TypeInfo typeInfo, string newResourceName) { + this.resource = resource; + this.typeInfo = typeInfo; + this.newResourceName = newResourceName; + } + public override string ToString() { + return string.Format("{0} => {1}", resource.Name, newResourceName); + } + } + + void renameResources(List renamedTypes) { + var newNames = new Dictionary(); + foreach (var info in renamedTypes) { + var oldFullName = info.oldFullName; + Resource resource; + if (!nameToResource.TryGetValue(oldFullName, out resource)) + continue; + if (newNames.ContainsKey(resource)) + continue; + var newTypeName = info.type.TypeDefinition.FullName; + var newName = newTypeName + resource.Name.Substring(oldFullName.Length); + newNames[resource] = new RenameInfo(resource, info, newName); + + Log.v("Renamed resource in resources: {0} => {1}", Utils.removeNewlines(resource.Name), newName); + resource.Name = newName; + } + } + } +}