Merge branch 'master' into newcode

This commit is contained in:
de4dot 2012-01-28 18:38:09 +01:00
commit 0f9184e9be
20 changed files with 407 additions and 291 deletions

View File

@ -265,9 +265,10 @@ namespace de4dot.blocks {
updateSources();
}
void addInstructions(IList<Instr> dest, IEnumerable<Instr> instrs) {
foreach (var instr in instrs) {
if (!instr.isNop())
void addInstructions(IList<Instr> dest, IList<Instr> instrs) {
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode != OpCodes.Nop)
dest.Add(instr);
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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];

View File

@ -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 {
@ -31,80 +32,64 @@ namespace de4dot.blocks.cflow {
List<Value> locals = new List<Value>();
int argBase;
MethodDefinition prev_method;
List<Value> cached_args = new List<Value>();
List<Value> cached_locals = new List<Value>();
int cached_argBase;
public InstructionEmulator() {
}
public InstructionEmulator(bool implicitThis, bool initLocals, IList<ParameterDefinition> parameterDefinitions, IList<VariableDefinition> variableDefinitions) {
init(implicitThis, initLocals, parameterDefinitions, variableDefinitions);
public InstructionEmulator(MethodDefinition method) {
init(method);
}
public void init(bool implicitThis, bool initLocals, IList<ParameterDefinition> parameterDefinitions, IList<VariableDefinition> variableDefinitions) {
this.parameterDefinitions = parameterDefinitions;
this.variableDefinitions = variableDefinitions;
public void init(Blocks blocks) {
init(blocks.Method);
}
public void init(MethodDefinition method) {
this.parameterDefinitions = method.Parameters;
this.variableDefinitions = method.Body.Variables;
valueStack.init();
args.Clear();
argBase = 0;
if (implicitThis) {
argBase = 1;
args.Add(new UnknownValue());
}
foreach (var arg in parameterDefinitions)
args.Add(getUnknownValue(arg.ParameterType));
if (method != prev_method) {
prev_method = method;
if (initLocals) {
locals.Clear();
foreach (var local in variableDefinitions)
locals.Add(getDefaultValue(local.VariableType));
}
else {
locals.Clear();
foreach (var local in variableDefinitions)
locals.Add(getUnknownValue(local.VariableType));
}
}
static Value getDefaultValue(TypeReference typeReference) {
if (typeReference == null)
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);
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));
}
return new UnknownValue();
argBase = cached_argBase;
args.Clear();
args.AddRange(cached_args);
locals.Clear();
locals.AddRange(cached_locals);
}
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 +97,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;
}
@ -282,8 +266,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;
@ -779,7 +763,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());
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -144,7 +144,7 @@ namespace de4dot.blocks.cflow {
foreach (var source in new List<Block>(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>(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>(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);

2
cecil

@ -1 +1 @@
Subproject commit 766ef4e529f0c8a4584a708711a33a1752e658c6
Subproject commit b12e75770c5ab14fbdfe141fd6dc1668adc71436

View File

@ -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"

View File

@ -217,6 +217,7 @@
<Compile Include="renamer\MemberInfos.cs" />
<Compile Include="renamer\NameCreators.cs" />
<Compile Include="renamer\Renamer.cs" />
<Compile Include="renamer\ResourceRenamer.cs" />
<Compile Include="renamer\TypeInfo.cs" />
<Compile Include="renamer\TypeNames.cs" />
<Compile Include="renamer\TypeRenamerState.cs" />

View File

@ -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);

View File

@ -24,12 +24,12 @@ using de4dot.blocks;
namespace de4dot.code.deobfuscators {
class ExceptionLoggerRemover {
Dictionary<MethodReference, bool> exceptionLoggerMethods = new Dictionary<MethodReference, bool>();
MethodDefinitionAndDeclaringTypeDict<bool> exceptionLoggerMethods = new MethodDefinitionAndDeclaringTypeDict<bool>();
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 {

View File

@ -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) {

View File

@ -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<TypeInfo> renamedTypes) {
// This is needed to speed up this method
var oldToNewTypeName = new Dictionary<string, string>(StringComparer.Ordinal);
foreach (var info in renamedTypes)
oldToNewTypeName[info.oldFullName] = info.type.TypeDefinition.FullName;
List<string> validResourceNames = new List<string>();
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<TypeInfo> 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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<string, Resource> nameToResource;
public ResourceRenamer(Module module) {
this.module = module;
}
public void rename(List<TypeInfo> 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<string, Resource>(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<TypeInfo> renamedTypes) {
var oldNameToTypeInfo = new Dictionary<string, TypeInfo>(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<Instruction> 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<TypeInfo> renamedTypes) {
var newNames = new Dictionary<Resource, RenameInfo>();
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;
}
}
}
}

View File

@ -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);

View File

@ -25,6 +25,8 @@ namespace de4dot.code.renamer {
abstract class TypeNames {
protected Dictionary<string, NameCreator> typeNames = new Dictionary<string, NameCreator>(StringComparer.Ordinal);
protected NameCreator genericParamNameCreator = new NameCreator("gparam_");
protected Dictionary<string, string> fullNameToShortName;
protected Dictionary<string, string> 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;
}
@ -81,8 +90,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();
}
@ -96,40 +106,50 @@ namespace de4dot.code.renamer {
}
class VariableNameCreator : TypeNames {
static Dictionary<string, string> ourFullNameToShortName;
static Dictionary<string, string> ourFullNameToShortNamePrefix;
static VariableNameCreator() {
ourFullNameToShortName = new Dictionary<string, string>(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<string, string>(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" },
};
}
public VariableNameCreator() {
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");
}
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);
fullNameToShortName = ourFullNameToShortName;
fullNameToShortNamePrefix = ourFullNameToShortNamePrefix;
}
static string lowerLeadingChars(string name) {
@ -152,6 +172,14 @@ namespace de4dot.code.renamer {
}
class PropertyNameCreator : TypeNames {
static Dictionary<string, string> ourFullNameToShortName = new Dictionary<string, string>(StringComparer.Ordinal);
static Dictionary<string, string> ourFullNameToShortNamePrefix = new Dictionary<string, string>(StringComparer.Ordinal);
public PropertyNameCreator() {
fullNameToShortName = ourFullNameToShortName;
fullNameToShortNamePrefix = ourFullNameToShortNamePrefix;
}
protected override string fixName(string prefix, string name) {
return prefix.ToUpperInvariant() + upperFirst(name);
}

View File

@ -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();
vns.existingVariableNames.merge(existingVariableNames);
vns.variableNameCreator.merge(variableNameCreator);
return vns;
}
public VariableNameState merge(VariableNameState other) {