Merge branch 'ds'

This commit is contained in:
de4dot 2012-05-12 21:40:01 +02:00
commit 654ebf652e
52 changed files with 3331 additions and 820 deletions

View File

@ -200,6 +200,21 @@ namespace de4dot.blocks {
}
}
// Returns true if it's one of the ldarg instructions
public static bool isLdarg(Instruction instr) {
switch (instr.OpCode.Code) {
case Code.Ldarg:
case Code.Ldarg_S:
case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
return true;
default:
return false;
}
}
// Return true if it's one of the stloc instructions
public static bool isStloc(Instruction instr) {
switch (instr.OpCode.Code) {
@ -577,6 +592,25 @@ namespace de4dot.blocks {
return null;
}
// Copies most things but not everything
public static MethodDefinition clone(MethodDefinition method) {
var newMethod = new MethodDefinition(method.Name, method.Attributes, method.MethodReturnType.ReturnType);
newMethod.MetadataToken = method.MetadataToken;
newMethod.Attributes = method.Attributes;
newMethod.ImplAttributes = method.ImplAttributes;
newMethod.HasThis = method.HasThis;
newMethod.ExplicitThis = method.ExplicitThis;
newMethod.CallingConvention = method.CallingConvention;
newMethod.SemanticsAttributes = method.SemanticsAttributes;
newMethod.DeclaringType = method.DeclaringType;
foreach (var arg in method.Parameters)
newMethod.Parameters.Add(new ParameterDefinition(arg.Name, arg.Attributes, arg.ParameterType));
foreach (var gp in method.GenericParameters)
newMethod.GenericParameters.Add(new GenericParameter(gp.Name, newMethod) { Attributes = gp.Attributes });
copyBodyFromTo(method, newMethod);
return newMethod;
}
public static Instruction clone(Instruction instr) {
return new Instruction {
Offset = instr.Offset,
@ -1174,5 +1208,50 @@ namespace de4dot.blocks {
return false;
}
public static IList<Instruction> getArgPushes(IList<Instruction> instrs, int index) {
return getArgPushes(instrs, ref index);
}
public static IList<Instruction> getArgPushes(IList<Instruction> instrs, ref int index) {
if (index < 0 || index >= instrs.Count)
return null;
var startInstr = instrs[index];
int pushes, pops;
calculateStackUsage(startInstr, false, out pushes, out pops);
index--;
int numArgs = pops;
var args = new List<Instruction>(numArgs);
int stackSize = numArgs;
while (index >= 0 && args.Count != numArgs) {
var instr = instrs[index--];
calculateStackUsage(instr, false, out pushes, out pops);
if (instr.OpCode.Code == Code.Dup) {
args.Add(instr);
stackSize--;
}
else {
if (pushes == 1)
args.Add(instr);
else if (pushes > 1)
throw new NotImplementedException();
stackSize -= pushes;
if (pops != 0) {
index++;
if (getArgPushes(instrs, ref index) == null)
return null;
}
}
if (stackSize < 0)
return null;
}
if (args.Count != numArgs)
return null;
args.Reverse();
return args;
}
}
}

View File

@ -111,6 +111,10 @@ namespace de4dot.blocks {
return DotNetUtils.getLdcI4Value(instruction);
}
public bool isLdarg() {
return DotNetUtils.isLdarg(instruction);
}
public bool isStloc() {
return DotNetUtils.isStloc(instruction);
}

View File

@ -39,19 +39,20 @@
<Compile Include="Blocks.cs" />
<Compile Include="BlocksSorter.cs" />
<Compile Include="cflow\BlockCflowDeobfuscator.cs" />
<Compile Include="cflow\BlockDeobfuscator.cs" />
<Compile Include="cflow\CachedCflowDeobfuscator.cs" />
<Compile Include="cflow\CflowDeobfuscator.cs" />
<Compile Include="cflow\CflowUtils.cs" />
<Compile Include="cflow\ConstantsFolder.cs" />
<Compile Include="cflow\DeadCodeRemover.cs" />
<Compile Include="cflow\DeadStoreRemover.cs" />
<Compile Include="cflow\IBlocksDeobfuscator.cs" />
<Compile Include="cflow\ICflowDeobfuscator.cs" />
<Compile Include="cflow\IMethodCallInliner.cs" />
<Compile Include="cflow\InstructionEmulator.cs" />
<Compile Include="cflow\Int32Value.cs" />
<Compile Include="cflow\Int64Value.cs" />
<Compile Include="cflow\MethodCallInliner.cs" />
<Compile Include="cflow\MethodCallInlinerBase.cs" />
<Compile Include="cflow\NoMethodInliner.cs" />
<Compile Include="cflow\Real8Value.cs" />
<Compile Include="cflow\StLdlocFixer.cs" />
<Compile Include="cflow\SwitchCflowDeobfuscator.cs" />

View File

@ -17,24 +17,22 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace de4dot.blocks.cflow {
class BlockCflowDeobfuscator {
Blocks blocks;
class BlockCflowDeobfuscator : BlockDeobfuscator {
Block block;
InstructionEmulator instructionEmulator = new InstructionEmulator();
public void init(Blocks blocks, Block block) {
this.blocks = blocks;
protected override bool deobfuscate(Block block) {
this.block = block;
if (!DotNetUtils.isConditionalBranch(block.LastInstr.OpCode.Code) && block.LastInstr.OpCode.Code != Code.Switch)
return false;
instructionEmulator.init(blocks);
}
// Returns true if code was updated, false otherwise
public bool deobfuscate() {
var instructions = block.Instructions;
if (instructions.Count == 0)
return false;
@ -44,7 +42,7 @@ namespace de4dot.blocks.cflow {
instructionEmulator.emulate(instr);
}
}
catch (System.NullReferenceException) {
catch (NullReferenceException) {
// Here if eg. invalid metadata token in a call instruction (operand is null)
return false;
}

View File

@ -0,0 +1,55 @@
/*
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;
namespace de4dot.blocks.cflow {
public abstract class BlockDeobfuscator : IBlocksDeobfuscator {
protected List<Block> allBlocks;
protected Blocks blocks;
public bool ExecuteOnNoChange { get; set; }
public virtual void deobfuscateBegin(Blocks blocks) {
this.blocks = blocks;
}
public bool deobfuscate(List<Block> allBlocks) {
init(allBlocks);
bool changed = false;
foreach (var block in allBlocks) {
try {
changed |= deobfuscate(block);
}
catch (NullReferenceException) {
// Here if eg. invalid metadata token in a call instruction (operand is null)
}
}
return changed;
}
protected virtual void init(List<Block> allBlocks) {
this.allBlocks = allBlocks;
}
protected abstract bool deobfuscate(Block block);
}
}

View File

@ -23,16 +23,38 @@ using Mono.Cecil.Cil;
namespace de4dot.blocks.cflow {
public class BlocksCflowDeobfuscator {
BlockCflowDeobfuscator blockCflowDeobfuscator = new BlockCflowDeobfuscator();
Blocks blocks;
List<Block> allBlocks = new List<Block>();
SwitchCflowDeobfuscator switchCflowDeobfuscator = new SwitchCflowDeobfuscator();
DeadCodeRemover deadCodeRemover = new DeadCodeRemover();
DeadStoreRemover deadStoreRemover = new DeadStoreRemover();
StLdlocFixer stLdlocFixer = new StLdlocFixer();
ConstantsFolder constantsFolder = new ConstantsFolder();
List<IBlocksDeobfuscator> userBlocksDeobfuscators = new List<IBlocksDeobfuscator>();
List<IBlocksDeobfuscator> ourBlocksDeobfuscators = new List<IBlocksDeobfuscator>();
public IMethodCallInliner MethodCallInliner { get; set; }
public BlocksCflowDeobfuscator() {
init();
}
public BlocksCflowDeobfuscator(IEnumerable<IBlocksDeobfuscator> blocksDeobfuscator) {
init();
add(blocksDeobfuscator);
}
void init() {
ourBlocksDeobfuscators.Add(new BlockCflowDeobfuscator { ExecuteOnNoChange = false });
ourBlocksDeobfuscators.Add(new SwitchCflowDeobfuscator { ExecuteOnNoChange = false });
ourBlocksDeobfuscators.Add(new DeadStoreRemover { ExecuteOnNoChange = false });
ourBlocksDeobfuscators.Add(new DeadCodeRemover { ExecuteOnNoChange = false });
ourBlocksDeobfuscators.Add(new ConstantsFolder { ExecuteOnNoChange = true });
ourBlocksDeobfuscators.Add(new StLdlocFixer { ExecuteOnNoChange = true });
}
public void add(IEnumerable<IBlocksDeobfuscator> blocksDeobfuscators) {
foreach (var bd in blocksDeobfuscators)
add(bd);
}
public void add(IBlocksDeobfuscator blocksDeobfuscator) {
if (blocksDeobfuscator != null)
userBlocksDeobfuscators.Add(blocksDeobfuscator);
}
public void init(Blocks blocks) {
this.blocks = blocks;
@ -41,6 +63,10 @@ namespace de4dot.blocks.cflow {
public void deobfuscate() {
bool changed;
int iterations = -1;
deobfuscateBegin(userBlocksDeobfuscators);
deobfuscateBegin(ourBlocksDeobfuscators);
do {
iterations++;
changed = false;
@ -52,40 +78,39 @@ namespace de4dot.blocks.cflow {
if (iterations == 0)
changed |= fixDotfuscatorLoop();
foreach (var block in allBlocks) {
MethodCallInliner.init(blocks, block);
changed |= MethodCallInliner.deobfuscate();
}
foreach (var block in allBlocks) {
var lastInstr = block.LastInstr;
if (!DotNetUtils.isConditionalBranch(lastInstr.OpCode.Code) && lastInstr.OpCode.Code != Code.Switch)
continue;
blockCflowDeobfuscator.init(blocks, block);
changed |= blockCflowDeobfuscator.deobfuscate();
}
switchCflowDeobfuscator.init(blocks, allBlocks);
changed |= switchCflowDeobfuscator.deobfuscate();
deadStoreRemover.init(blocks, allBlocks);
changed |= deadStoreRemover.remove();
deadCodeRemover.init(allBlocks);
changed |= deadCodeRemover.remove();
if (!changed) {
constantsFolder.init(blocks, allBlocks);
changed |= constantsFolder.deobfuscate();
}
if (!changed) {
stLdlocFixer.init(allBlocks, blocks.Locals);
changed |= stLdlocFixer.fix();
}
changed |= deobfuscate(userBlocksDeobfuscators, allBlocks);
changed |= deobfuscate(ourBlocksDeobfuscators, allBlocks);
changed |= deobfuscateNoChange(changed, userBlocksDeobfuscators, allBlocks);
changed |= deobfuscateNoChange(changed, ourBlocksDeobfuscators, allBlocks);
} while (changed);
}
void deobfuscateBegin(IEnumerable<IBlocksDeobfuscator> bds) {
foreach (var bd in bds)
bd.deobfuscateBegin(blocks);
}
bool deobfuscate(IEnumerable<IBlocksDeobfuscator> bds, List<Block> allBlocks) {
bool changed = false;
foreach (var bd in bds) {
if (bd.ExecuteOnNoChange)
continue;
changed |= bd.deobfuscate(allBlocks);
}
return changed;
}
bool deobfuscateNoChange(bool changed, IEnumerable<IBlocksDeobfuscator> bds, List<Block> allBlocks) {
foreach (var bd in bds) {
if (changed)
break;
if (!bd.ExecuteOnNoChange)
continue;
changed |= bd.deobfuscate(allBlocks);
}
return changed;
}
// Hack for old Dotfuscator
bool fixDotfuscatorLoop() {
/*

View File

@ -0,0 +1,74 @@
/*
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.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace de4dot.blocks.cflow {
// Only deobfuscates a method once. A copy of the method (now deobfuscated) is returned.
public class CachedCflowDeobfuscator {
BlocksCflowDeobfuscator cflowDeobfuscator = new BlocksCflowDeobfuscator();
Dictionary<MethodDefinition, MethodDefinition> deobfuscated = new Dictionary<MethodDefinition, MethodDefinition>();
public CachedCflowDeobfuscator() {
}
public CachedCflowDeobfuscator(IEnumerable<IBlocksDeobfuscator> blocksDeobfuscators) {
add(blocksDeobfuscators);
}
public void add(IEnumerable<IBlocksDeobfuscator> blocksDeobfuscators) {
foreach (var bd in blocksDeobfuscators)
cflowDeobfuscator.add(bd);
}
public void add(IBlocksDeobfuscator blocksDeobfuscator) {
cflowDeobfuscator.add(blocksDeobfuscator);
}
public MethodDefinition deobfuscate(MethodDefinition method) {
MethodDefinition deobfuscatedMethod;
if (deobfuscated.TryGetValue(method, out deobfuscatedMethod))
return deobfuscatedMethod;
if (method.Body == null || method.Body.Instructions.Count == 0) {
deobfuscated[method] = method;
return method;
}
deobfuscatedMethod = DotNetUtils.clone(method);
deobfuscated[method] = deobfuscatedMethod;
var blocks = new Blocks(deobfuscatedMethod);
deobfuscate(blocks);
IList<Instruction> allInstructions;
IList<ExceptionHandler> allExceptionHandlers;
blocks.getCode(out allInstructions, out allExceptionHandlers);
DotNetUtils.restoreBody(deobfuscatedMethod, allInstructions, allExceptionHandlers);
return deobfuscatedMethod;
}
void deobfuscate(Blocks blocks) {
cflowDeobfuscator.init(blocks);
cflowDeobfuscator.deobfuscate();
}
}
}

View File

@ -25,8 +25,11 @@ namespace de4dot.blocks.cflow {
public class CflowDeobfuscator : ICflowDeobfuscator {
BlocksCflowDeobfuscator cflowDeobfuscator = new BlocksCflowDeobfuscator();
public CflowDeobfuscator(IMethodCallInliner methodCallInliner) {
cflowDeobfuscator.MethodCallInliner = methodCallInliner;
public CflowDeobfuscator() {
}
public CflowDeobfuscator(IBlocksDeobfuscator blocksDeobfuscator) {
cflowDeobfuscator.add(blocksDeobfuscator);
}
public void deobfuscate(MethodDefinition method) {

View File

@ -17,6 +17,7 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
@ -24,65 +25,62 @@ using de4dot.blocks;
namespace de4dot.blocks.cflow {
// Very simple constants folder which is all that's needed at the moment
class ConstantsFolder {
List<Block> allBlocks;
Blocks blocks;
class ConstantsFolder : BlockDeobfuscator {
InstructionEmulator instructionEmulator = new InstructionEmulator();
List<ParameterDefinition> args;
public void init(Blocks blocks, List<Block> allBlocks) {
this.blocks = blocks;
this.allBlocks = allBlocks;
protected override void init(List<Block> allBlocks) {
base.init(allBlocks);
args = DotNetUtils.getParameters(blocks.Method);
}
public bool deobfuscate() {
protected override bool deobfuscate(Block block) {
bool changed = false;
foreach (var block in allBlocks) {
instructionEmulator.init(blocks);
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
switch (instr.OpCode.Code) {
case Code.Ldarg:
case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
case Code.Ldarg_S:
changed |= fixLoadInstruction(block, i, instructionEmulator.getArg(DotNetUtils.getParameter(args, instr.Instruction)));
break;
instructionEmulator.init(blocks);
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
case Code.Ldloc:
case Code.Ldloc_0:
case Code.Ldloc_1:
case Code.Ldloc_2:
case Code.Ldloc_3:
case Code.Ldloc_S:
changed |= fixLoadInstruction(block, i, instructionEmulator.getLocal(DotNetUtils.getLocalVar(blocks.Locals, instr.Instruction)));
break;
switch (instr.OpCode.Code) {
case Code.Ldarg:
case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
case Code.Ldarg_S:
changed |= fixLoadInstruction(block, i, instructionEmulator.getArg(DotNetUtils.getParameter(args, instr.Instruction)));
break;
case Code.Ldarga:
case Code.Ldarga_S:
instructionEmulator.makeArgUnknown((ParameterDefinition)instr.Operand);
break;
case Code.Ldloc:
case Code.Ldloc_0:
case Code.Ldloc_1:
case Code.Ldloc_2:
case Code.Ldloc_3:
case Code.Ldloc_S:
changed |= fixLoadInstruction(block, i, instructionEmulator.getLocal(DotNetUtils.getLocalVar(blocks.Locals, instr.Instruction)));
break;
case Code.Ldloca:
case Code.Ldloca_S:
instructionEmulator.makeLocalUnknown((VariableDefinition)instr.Operand);
break;
}
case Code.Ldarga:
case Code.Ldarga_S:
instructionEmulator.makeArgUnknown((ParameterDefinition)instr.Operand);
break;
try {
instructionEmulator.emulate(instr.Instruction);
}
catch (System.NullReferenceException) {
// Here if eg. invalid metadata token in a call instruction (operand is null)
break;
}
case Code.Ldloca:
case Code.Ldloca_S:
instructionEmulator.makeLocalUnknown((VariableDefinition)instr.Operand);
break;
}
try {
instructionEmulator.emulate(instr.Instruction);
}
catch (NullReferenceException) {
// Here if eg. invalid metadata token in a call instruction (operand is null)
break;
}
}
return changed;
}

View File

@ -23,31 +23,11 @@ using Mono.Cecil.Cil;
namespace de4dot.blocks.cflow {
// Removes dead code that is the result of one of our optimizations, or created by the
// obfuscator.
class DeadCodeRemover {
List<Block> allBlocks;
class DeadCodeRemover : BlockDeobfuscator {
List<int> allDeadInstructions = new List<int>();
InstructionExpressionFinder instructionExpressionFinder = new InstructionExpressionFinder();
public void init(List<Block> allBlocks) {
this.allBlocks = allBlocks;
}
public bool remove() {
bool changed = false;
foreach (var block in allBlocks) {
try {
changed |= remove(block);
}
catch (System.NullReferenceException) {
// Here if eg. invalid metadata token in a call instruction (operand is null)
}
}
return changed;
}
bool remove(Block block) {
protected override bool deobfuscate(Block block) {
allDeadInstructions.Clear();
bool changed = false;

View File

@ -26,17 +26,12 @@ namespace de4dot.blocks.cflow {
// dead code and remove it.
// I've only seen Xenocode generate this kind of code, so the code below is a special case of
// the more general case.
class DeadStoreRemover {
class DeadStoreRemover : IBlocksDeobfuscator {
Blocks blocks;
List<Block> allBlocks = new List<Block>();
List<Block> allBlocks;
List<AccessFlags> localFlags = new List<AccessFlags>();
List<bool> deadLocals = new List<bool>();
public void init(Blocks blocks, List<Block> allBlocks) {
this.blocks = blocks;
this.allBlocks = allBlocks;
}
[Flags]
enum AccessFlags {
None = 0,
@ -44,7 +39,18 @@ namespace de4dot.blocks.cflow {
Write = 2,
}
public bool remove() {
public bool ExecuteOnNoChange { get; set; }
public void deobfuscateBegin(Blocks blocks) {
this.blocks = blocks;
}
public bool deobfuscate(List<Block> allBlocks) {
this.allBlocks = allBlocks;
return remove();
}
bool remove() {
if (blocks.Locals.Count == 0)
return false;

View File

@ -17,11 +17,15 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
namespace de4dot.blocks.cflow {
public interface IMethodCallInliner {
void init(Blocks blocks, Block block);
using System.Collections.Generic;
// Returns true if something was inlined
bool deobfuscate();
namespace de4dot.blocks.cflow {
public interface IBlocksDeobfuscator {
bool ExecuteOnNoChange { get; }
void deobfuscateBegin(Blocks blocks);
// Returns true if something was updated
bool deobfuscate(List<Block> allBlocks);
}
}

View File

@ -26,6 +26,7 @@ using Mono.Cecil.Metadata;
namespace de4dot.blocks.cflow {
public class InstructionEmulator {
ValueStack valueStack = new ValueStack();
Dictionary<Value, bool> protectedStackValues = new Dictionary<Value, bool>();
IList<ParameterDefinition> parameterDefinitions;
IList<VariableDefinition> variableDefinitions;
List<Value> args = new List<Value>();
@ -52,6 +53,7 @@ namespace de4dot.blocks.cflow {
this.parameterDefinitions = method.Parameters;
this.variableDefinitions = method.Body.Variables;
valueStack.init();
protectedStackValues.Clear();
if (method != prev_method) {
prev_method = method;
@ -77,6 +79,10 @@ namespace de4dot.blocks.cflow {
locals.AddRange(cached_locals);
}
public void setProtected(Value value) {
protectedStackValues[value] = true;
}
static Value getUnknownValue(TypeReference typeReference) {
if (typeReference == null)
return new UnknownValue();
@ -94,9 +100,11 @@ namespace de4dot.blocks.cflow {
return new UnknownValue();
}
static Value truncateValue(Value value, TypeReference typeReference) {
Value truncateValue(Value value, TypeReference typeReference) {
if (typeReference == null)
return value;
if (protectedStackValues.ContainsKey(value))
return value;
switch (typeReference.EType) {
case ElementType.Boolean:
@ -208,6 +216,10 @@ namespace de4dot.blocks.cflow {
return new UnknownValue();
}
public int stackSize() {
return valueStack.Size;
}
public void push(Value value) {
valueStack.push(value);
}

View File

@ -17,11 +17,12 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace de4dot.blocks.cflow {
public abstract class MethodCallInlinerBase : IMethodCallInliner {
public abstract class MethodCallInlinerBase : IBlocksDeobfuscator {
// We can't catch all infinite loops, so inline methods at most this many times
const int MAX_ITERATIONS = 10;
@ -29,24 +30,46 @@ namespace de4dot.blocks.cflow {
protected Block block;
int iteration;
public void init(Blocks blocks, Block block) {
public bool ExecuteOnNoChange { get; set; }
public void deobfuscateBegin(Blocks blocks) {
this.blocks = blocks;
this.block = block;
this.iteration = 0;
iteration = 0;
}
public bool deobfuscate() {
public bool deobfuscate(List<Block> allBlocks) {
if (iteration++ >= MAX_ITERATIONS)
return false;
return deobfuscateInternal();
bool changed = false;
foreach (var block in allBlocks) {
this.block = block;
changed |= deobfuscateInternal();
}
return changed;
}
protected abstract bool deobfuscateInternal();
protected class InstructionPatcher {
readonly int patchIndex;
public readonly int afterIndex;
public readonly Instruction lastInstr;
readonly Instr clonedInstr;
public InstructionPatcher(int patchIndex, int afterIndex, Instruction lastInstr) {
this.patchIndex = patchIndex;
this.afterIndex = afterIndex;
this.lastInstr = lastInstr;
this.clonedInstr = new Instr(DotNetUtils.clone(lastInstr));
}
public void patch(Block block) {
block.Instructions[patchIndex] = clonedInstr;
}
}
protected bool inlineLoadMethod(int patchIndex, MethodDefinition methodToInline, Instruction loadInstr, int instrIndex) {
var instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
if (instr == null || instr.OpCode.Code != Code.Ret)
if (!isReturn(methodToInline, instrIndex))
return false;
int methodArgsCount = DotNetUtils.getArgsCount(methodToInline);
@ -58,6 +81,21 @@ namespace de4dot.blocks.cflow {
}
protected bool inlineOtherMethod(int patchIndex, MethodDefinition methodToInline, Instruction instr, int instrIndex, int popLastArgs = 0) {
return patchMethod(methodToInline, tryInlineOtherMethod(patchIndex, methodToInline, instr, instrIndex, popLastArgs));
}
protected bool patchMethod(MethodDefinition methodToInline, InstructionPatcher patcher) {
if (patcher == null)
return false;
if (!isReturn(methodToInline, patcher.afterIndex))
return false;
patcher.patch(block);
return true;
}
protected InstructionPatcher tryInlineOtherMethod(int patchIndex, MethodDefinition methodToInline, Instruction instr, int instrIndex, int popLastArgs = 0) {
int loadIndex = 0;
int methodArgsCount = DotNetUtils.getArgsCount(methodToInline);
bool foundLdarga = false;
@ -83,75 +121,66 @@ namespace de4dot.blocks.cflow {
break;
if (DotNetUtils.getArgIndex(instr) != loadIndex)
return false;
return null;
loadIndex++;
instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
}
if (instr == null || loadIndex != methodArgsCount - popLastArgs)
return false;
return null;
if (instr.OpCode.Code == Code.Call || instr.OpCode.Code == Code.Callvirt) {
if (foundLdarga)
return false;
return null;
var callInstr = instr;
var calledMethod = callInstr.Operand as MethodReference;
if (calledMethod == null)
return false;
return null;
if (!isCompatibleType(-1, calledMethod.MethodReturnType.ReturnType, methodToInline.MethodReturnType.ReturnType))
return false;
return null;
if (!checkSameMethods(calledMethod, methodToInline, popLastArgs))
return false;
return null;
instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
if (instr == null || instr.OpCode.Code != Code.Ret)
return false;
block.Instructions[patchIndex] = new Instr(DotNetUtils.clone(callInstr));
return true;
return new InstructionPatcher(patchIndex, instrIndex, callInstr);
}
else if (instr.OpCode.Code == Code.Newobj) {
if (foundLdarga)
return false;
return null;
var newobjInstr = instr;
var ctor = newobjInstr.Operand as MethodReference;
if (ctor == null)
return false;
return null;
if (!isCompatibleType(-1, ctor.DeclaringType, methodToInline.MethodReturnType.ReturnType))
return false;
return null;
var methodArgs = DotNetUtils.getArgs(methodToInline);
var calledMethodArgs = DotNetUtils.getArgs(ctor);
if (methodArgs.Count + 1 - popLastArgs != calledMethodArgs.Count)
return false;
return null;
for (int i = 1; i < calledMethodArgs.Count; i++) {
if (!isCompatibleType(i, calledMethodArgs[i], methodArgs[i - 1]))
return false;
return null;
}
instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
if (instr == null || instr.OpCode.Code != Code.Ret)
return false;
block.Instructions[patchIndex] = new Instr(DotNetUtils.clone(newobjInstr));
return true;
return new InstructionPatcher(patchIndex, instrIndex, newobjInstr);
}
else if (instr.OpCode.Code == Code.Ldfld || instr.OpCode.Code == Code.Ldflda ||
instr.OpCode.Code == Code.Ldftn || instr.OpCode.Code == Code.Ldvirtftn) {
var ldInstr = instr;
instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
if (instr == null || instr.OpCode.Code != Code.Ret)
return false;
if (methodArgsCount != 1)
return false;
block.Instructions[patchIndex] = new Instr(DotNetUtils.clone(ldInstr));
return true;
return null;
return new InstructionPatcher(patchIndex, instrIndex, ldInstr);
}
return false;
return null;
}
protected virtual bool isReturn(MethodDefinition methodToInline, int instrIndex) {
var instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
return instr != null && instr.OpCode.Code == Code.Ret;
}
protected bool checkSameMethods(MethodReference method, MethodDefinition methodToInline, int ignoreLastMethodToInlineArgs = 0) {

View File

@ -22,25 +22,15 @@ using Mono.Cecil.Cil;
namespace de4dot.blocks.cflow {
// Replace stloc + ldloc with dup + stloc
class StLdlocFixer {
class StLdlocFixer : BlockDeobfuscator {
IList<VariableDefinition> locals;
List<Block> allBlocks;
public void init(List<Block> allBlocks, IList<VariableDefinition> locals) {
this.allBlocks = allBlocks;
this.locals = locals;
protected override void init(List<Block> allBlocks) {
base.init(allBlocks);
locals = blocks.Locals;
}
public bool fix() {
bool changed = false;
foreach (var block in allBlocks)
changed |= fix(block);
return changed;
}
bool fix(Block block) {
protected override bool deobfuscate(Block block) {
bool changed = false;
var instructions = block.Instructions;
for (int i = 0; i < instructions.Count; i++) {

View File

@ -17,54 +17,34 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using Mono.Cecil.Cil;
namespace de4dot.blocks.cflow {
class SwitchCflowDeobfuscator {
List<Block> allBlocks;
Blocks blocks;
class SwitchCflowDeobfuscator : BlockDeobfuscator {
InstructionEmulator instructionEmulator = new InstructionEmulator();
public void init(Blocks blocks, List<Block> allBlocks) {
this.blocks = blocks;
this.allBlocks = allBlocks;
}
protected override bool deobfuscate(Block switchBlock) {
if (switchBlock.LastInstr.OpCode.Code != Code.Switch)
return false;
public bool deobfuscate() {
bool changed = false;
if (isSwitchTopOfStack(switchBlock) && deobfuscateTos(switchBlock))
return true;
foreach (var switchBlock in allBlocks) {
if (switchBlock.LastInstr.OpCode.Code != Code.Switch)
continue;
if (isLdlocBranch(switchBlock, true) && deobfuscateLdloc(switchBlock))
return true;
if (isSwitchTopOfStack(switchBlock) && deobfuscateTos(switchBlock)) {
changed = true;
continue;
}
if (isStLdlocBranch(switchBlock, true) && deobfuscateStLdloc(switchBlock))
return true;
if (isLdlocBranch(switchBlock, true) && deobfuscateLdloc(switchBlock)) {
changed = true;
continue;
}
if (isSwitchType1(switchBlock) && deobfuscateType1(switchBlock))
return true;
if (isStLdlocBranch(switchBlock, true) && deobfuscateStLdloc(switchBlock)) {
changed = true;
continue;
}
if (switchBlock.FirstInstr.isLdloc() && fixSwitchBranch(switchBlock))
return true;
if (isSwitchType1(switchBlock) && deobfuscateType1(switchBlock)) {
changed = true;
continue;
}
if (switchBlock.FirstInstr.isLdloc() && fixSwitchBranch(switchBlock)) {
changed = true;
continue;
}
}
return changed;
return false;
}
static bool isSwitchTopOfStack(Block switchBlock) {
@ -268,7 +248,7 @@ namespace de4dot.blocks.cflow {
try {
instructionEmulator.emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1);
}
catch (System.NullReferenceException) {
catch (NullReferenceException) {
// Here if eg. invalid metadata token in a call instruction (operand is null)
target = null;
return false;
@ -283,7 +263,7 @@ namespace de4dot.blocks.cflow {
instructionEmulator.emulate(source.Instructions);
instructionEmulator.emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1);
}
catch (System.NullReferenceException) {
catch (NullReferenceException) {
// Here if eg. invalid metadata token in a call instruction (operand is null)
return false;
}

View File

@ -25,6 +25,10 @@ namespace de4dot.blocks.cflow {
class ValueStack {
List<Value> stack = new List<Value>();
public int Size {
get { return stack.Count; }
}
public void init() {
stack.Clear();
}

View File

@ -32,6 +32,7 @@ namespace de4dot.code {
INameChecker NameChecker { get; }
bool RenameResourcesInCode { get; }
bool RemoveNamespaceWithOneType { get; }
bool RenameResourceKeys { get; }
void deobfuscateBegin();
void deobfuscate();

View File

@ -146,6 +146,12 @@ namespace de4dot.code {
Blocks blocks;
VariableValues variableValues;
int errors = 0;
bool useUnknownArgs = false;
public bool UseUnknownArgs {
get { return useUnknownArgs; }
set { useUnknownArgs = value; }
}
protected class CallResult {
public Block block;
@ -200,13 +206,16 @@ namespace de4dot.code {
}
}
void getLocalVariableValue(VariableDefinition variable, out object value) {
bool getLocalVariableValue(VariableDefinition variable, out object value) {
if (variableValues == null)
variableValues = new VariableValues(blocks.Locals, allBlocks);
var val = variableValues.getValue(variable);
if (!val.isValid())
throw new ApplicationException("Could not get value of local variable");
if (!val.isValid()) {
value = null;
return false;
}
value = val.Value;
return true;
}
void findAllCallResults() {
@ -320,17 +329,29 @@ namespace de4dot.code {
getLocalVariableValue(Instr.getLocalVar(blocks.Locals, instr), out arg);
break;
case Code.Ldfld:
case Code.Ldsfld:
arg = instr.Operand;
break;
default:
Log.w("Could not find all arguments to method {0} ({1:X8}), instr: {2}",
Utils.removeNewlines(method),
method.MetadataToken.ToInt32(),
instr);
errors++;
return false;
int pushes, pops;
DotNetUtils.calculateStackUsage(instr.Instruction, false, out pushes, out pops);
if (!useUnknownArgs || pushes != 1) {
Log.w("Could not find all arguments to method {0} ({1:X8}), instr: {2}",
Utils.removeNewlines(method),
method.MetadataToken.ToInt32(),
instr);
errors++;
return false;
}
for (int i = 0; i < pops; i++) {
if (!getArg(method, block, ref arg, ref instrIndex))
return false;
}
arg = null;
break;
}
break;
}

View File

@ -120,6 +120,10 @@ namespace de4dot.code {
get { return (deob.RenamingOptions & RenamingOptions.RemoveNamespaceIfOneType) != 0; }
}
public bool RenameResourceKeys {
get { return (deob.RenamingOptions & RenamingOptions.RenameResourceKeys) != 0; }
}
public IDeobfuscator Deobfuscator {
get { return deob; }
}
@ -541,7 +545,7 @@ namespace de4dot.code {
Log.v("Deobfuscating methods");
var methodPrinter = new MethodPrinter();
var cflowDeobfuscator = new BlocksCflowDeobfuscator { MethodCallInliner = deob.MethodCallInliner };
var cflowDeobfuscator = new BlocksCflowDeobfuscator(deob.BlocksDeobfuscators);
foreach (var method in allMethods) {
Log.v("Deobfuscating {0} ({1:X8})", Utils.removeNewlines(method), method.MetadataToken.ToUInt32());
Log.indent();
@ -715,7 +719,7 @@ namespace de4dot.code {
return;
deobfuscate(method, "Deobfuscating control flow", (blocks) => {
var cflowDeobfuscator = new BlocksCflowDeobfuscator { MethodCallInliner = deob.MethodCallInliner };
var cflowDeobfuscator = new BlocksCflowDeobfuscator(deob.BlocksDeobfuscators);
cflowDeobfuscator.init(blocks);
cflowDeobfuscator.deobfuscate();
});

View File

@ -107,6 +107,7 @@
<Compile Include="deobfuscators\CodeVeil\Deobfuscator.cs" />
<Compile Include="deobfuscators\CodeVeil\ProxyDelegateFinder.cs" />
<Compile Include="deobfuscators\CodeVeil\TamperDetection.cs" />
<Compile Include="deobfuscators\ConstantsReader.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\AntiDebugger.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\AssemblyResolver.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\Deobfuscator.cs" />
@ -115,6 +116,10 @@
<Compile Include="deobfuscators\CryptoObfuscator\ResourceResolver.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\StringDecrypter.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\TamperDetection.cs" />
<Compile Include="deobfuscators\DeepSea\ArrayBlockDeobfuscator.cs" />
<Compile Include="deobfuscators\DeepSea\CastDeobfuscator.cs" />
<Compile Include="deobfuscators\DeepSea\DsConstantsReader.cs" />
<Compile Include="deobfuscators\DeepSea\DsUtils.cs" />
<Compile Include="deobfuscators\DeepSea\FieldsRestorer.cs" />
<Compile Include="deobfuscators\DeobfuscatorBase.cs" />
<Compile Include="deobfuscators\DeobfuscatorInfoBase.cs" />
@ -125,7 +130,7 @@
<Compile Include="deobfuscators\DeepSea\ResourceResolver.cs" />
<Compile Include="deobfuscators\DeepSea\StringDecrypter.cs" />
<Compile Include="deobfuscators\DeepSea\Deobfuscator.cs" />
<Compile Include="deobfuscators\DeepSea\MethodCallInliner.cs" />
<Compile Include="deobfuscators\DeepSea\DsMethodCallInliner.cs" />
<Compile Include="deobfuscators\Dotfuscator\Deobfuscator.cs" />
<Compile Include="deobfuscators\Dotfuscator\StringDecrypter.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\v3\AntiStrongName.cs" />
@ -152,7 +157,7 @@
<Compile Include="deobfuscators\dotNET_Reactor\v3\MemoryPatcher.cs" />
<Compile Include="deobfuscators\Eazfuscator_NET\AssemblyResolver.cs" />
<Compile Include="deobfuscators\Eazfuscator_NET\CodeCompilerMethodCallRestorer.cs" />
<Compile Include="deobfuscators\Eazfuscator_NET\ConstantsReader.cs" />
<Compile Include="deobfuscators\Eazfuscator_NET\EfConstantsReader.cs" />
<Compile Include="deobfuscators\Eazfuscator_NET\DecrypterType.cs" />
<Compile Include="deobfuscators\Eazfuscator_NET\Deobfuscator.cs" />
<Compile Include="deobfuscators\Eazfuscator_NET\EfUtils.cs" />
@ -253,6 +258,7 @@
<Compile Include="renamer\MemberInfos.cs" />
<Compile Include="renamer\NameCreators.cs" />
<Compile Include="renamer\Renamer.cs" />
<Compile Include="renamer\ResourceKeysRenamer.cs" />
<Compile Include="renamer\ResourceRenamer.cs" />
<Compile Include="renamer\TypeInfo.cs" />
<Compile Include="renamer\TypeNames.cs" />
@ -262,6 +268,7 @@
<Compile Include="resources\IResourceData.cs" />
<Compile Include="resources\ResourceElement.cs" />
<Compile Include="resources\ResourceElementSet.cs" />
<Compile Include="resources\ResourceReader.cs" />
<Compile Include="resources\ResourceTypeCode.cs" />
<Compile Include="resources\ResourceDataCreator.cs" />
<Compile Include="resources\ResourceWriter.cs" />

View File

@ -200,7 +200,7 @@ namespace de4dot.code.deobfuscators.CliSecure.vm {
"System.Collections.Generic.Dictionary`2<System.UInt16,System.Type>",
"System.UInt16",
};
var cflowDeobfuscator = new CflowDeobfuscator(new NoMethodInliner());
var cflowDeobfuscator = new CflowDeobfuscator();
foreach (var type in module.Types) {
var cctor = DotNetUtils.getMethod(type, ".cctor");
if (cctor == null)

View File

@ -0,0 +1,316 @@
/*
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.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Metadata;
using de4dot.blocks;
namespace de4dot.code.deobfuscators {
class ConstantsReader {
protected IInstructions instructions;
protected IList<VariableDefinition> locals;
protected Dictionary<VariableDefinition, int> localsValues = new Dictionary<VariableDefinition, int>();
public interface IInstructions {
int Count { get; }
Instruction this[int index] { get; }
}
class ListInstructions : IInstructions {
IList<Instruction> instrs;
public int Count {
get { return instrs.Count; }
}
public Instruction this[int index] {
get { return instrs[index]; }
}
public ListInstructions(IList<Instruction> instrs) {
this.instrs = instrs;
}
}
class ListInstrs : IInstructions {
IList<Instr> instrs;
public int Count {
get { return instrs.Count; }
}
public Instruction this[int index] {
get { return instrs[index].Instruction; }
}
public ListInstrs(IList<Instr> instrs) {
this.instrs = instrs;
}
}
public ConstantsReader(IList<Instruction> instrs) {
this.instructions = new ListInstructions(instrs);
}
public ConstantsReader(IList<Instr> instrs) {
this.instructions = new ListInstrs(instrs);
}
public ConstantsReader(MethodDefinition method)
: this(method.Body.Instructions) {
this.locals = method.Body.Variables;
}
public ConstantsReader(IList<Instr> instrs, IList<VariableDefinition> locals)
: this(instrs) {
this.locals = locals;
}
public bool getNextInt32(ref int index, out int val) {
for (; index < instructions.Count; index++) {
var instr = instructions[index];
if (!isLoadConstant(instr))
continue;
return getInt32(ref index, out val);
}
val = 0;
return false;
}
public bool isLoadConstant(Instruction instr) {
if (DotNetUtils.isLdcI4(instr))
return true;
if (DotNetUtils.isLdloc(instr)) {
int tmp;
return getLocalConstant(instr, out tmp);
}
if (DotNetUtils.isLdarg(instr)) {
int tmp;
return getArgConstant(instr, out tmp);
}
return false;
}
public bool getInt16(ref int index, out short val) {
int tmp;
if (!getInt32(ref index, out tmp)) {
val = 0;
return false;
}
val = (short)tmp;
return true;
}
struct ConstantInfo {
public int index;
public int constant;
public ConstantInfo(int index, int constant) {
this.index = index;
this.constant = constant;
}
}
public bool getInt32(ref int index, out int val) {
val = 0;
if (index >= instructions.Count)
return false;
var stack = new Stack<ConstantInfo>();
int op1;
ConstantInfo info1, info2;
for (; index < instructions.Count; index++) {
var instr = instructions[index];
switch (instr.OpCode.Code) {
case Code.Conv_I1:
if (stack.Count < 1)
goto done;
stack.Push(new ConstantInfo(index, (sbyte)stack.Pop().constant));
break;
case Code.Conv_U1:
if (stack.Count < 1)
goto done;
stack.Push(new ConstantInfo(index, (byte)stack.Pop().constant));
break;
case Code.Conv_I2:
if (stack.Count < 1)
goto done;
stack.Push(new ConstantInfo(index, (short)stack.Pop().constant));
break;
case Code.Conv_U2:
if (stack.Count < 1)
goto done;
stack.Push(new ConstantInfo(index, (ushort)stack.Pop().constant));
break;
case Code.Conv_I4:
case Code.Conv_U4:
stack.Push(new ConstantInfo(index, stack.Pop().constant));
break;
case Code.Not:
stack.Push(new ConstantInfo(index, ~stack.Pop().constant));
break;
case Code.Neg:
stack.Push(new ConstantInfo(index, -stack.Pop().constant));
break;
case Code.Ldloc:
case Code.Ldloc_S:
case Code.Ldloc_0:
case Code.Ldloc_1:
case Code.Ldloc_2:
case Code.Ldloc_3:
if (!getLocalConstant(instr, out op1))
goto done;
stack.Push(new ConstantInfo(index, op1));
break;
case Code.Ldarg:
case Code.Ldarg_S:
case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
if (!getArgConstant(instr, out op1))
goto done;
stack.Push(new ConstantInfo(index, op1));
break;
case Code.Ldc_I4:
case Code.Ldc_I4_S:
case Code.Ldc_I4_0:
case Code.Ldc_I4_1:
case Code.Ldc_I4_2:
case Code.Ldc_I4_3:
case Code.Ldc_I4_4:
case Code.Ldc_I4_5:
case Code.Ldc_I4_6:
case Code.Ldc_I4_7:
case Code.Ldc_I4_8:
case Code.Ldc_I4_M1:
stack.Push(new ConstantInfo(index, DotNetUtils.getLdcI4Value(instr)));
break;
case Code.Add:
if (stack.Count < 2)
goto done;
info2 = stack.Pop();
info1 = stack.Pop();
stack.Push(new ConstantInfo(index, info1.constant + info2.constant));
break;
case Code.Sub:
if (stack.Count < 2)
goto done;
info2 = stack.Pop();
info1 = stack.Pop();
stack.Push(new ConstantInfo(index, info1.constant - info2.constant));
break;
case Code.Xor:
if (stack.Count < 2)
goto done;
info2 = stack.Pop();
info1 = stack.Pop();
stack.Push(new ConstantInfo(index, info1.constant ^ info2.constant));
break;
case Code.Or:
if (stack.Count < 2)
goto done;
info2 = stack.Pop();
info1 = stack.Pop();
stack.Push(new ConstantInfo(index, info1.constant | info2.constant));
break;
case Code.And:
if (stack.Count < 2)
goto done;
info2 = stack.Pop();
info1 = stack.Pop();
stack.Push(new ConstantInfo(index, info1.constant & info2.constant));
break;
case Code.Mul:
if (stack.Count < 2)
goto done;
info2 = stack.Pop();
info1 = stack.Pop();
stack.Push(new ConstantInfo(index, info1.constant * info2.constant));
break;
case Code.Div:
if (stack.Count < 2)
goto done;
info2 = stack.Pop();
info1 = stack.Pop();
stack.Push(new ConstantInfo(index, info1.constant / info2.constant));
break;
case Code.Div_Un:
if (stack.Count < 2)
goto done;
info2 = stack.Pop();
info1 = stack.Pop();
stack.Push(new ConstantInfo(index, (int)((uint)info1.constant / (uint)info2.constant)));
break;
default:
goto done;
}
}
done:
if (stack.Count == 0)
return false;
while (stack.Count > 1)
stack.Pop();
info1 = stack.Pop();
index = info1.index + 1;
val = info1.constant;
return true;
}
protected virtual bool getLocalConstant(Instruction instr, out int value) {
value = 0;
if (locals == null)
return false;
var local = DotNetUtils.getLocalVar(locals, instr);
if (local == null)
return false;
if (local.VariableType.EType != ElementType.I4)
return false;
return localsValues.TryGetValue(local, out value);
}
protected virtual bool getArgConstant(Instruction instr, out int value) {
value = 0;
return false;
}
}
}

View File

@ -0,0 +1,326 @@
/*
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.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<FieldInfo> fieldToInfo = new FieldDefinitionAndDeclaringTypeDict<FieldInfo>();
Dictionary<VariableDefinition, FieldInfo> localToInfo = new Dictionary<VariableDefinition, FieldInfo>();
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 override void deobfuscateBegin(Blocks blocks) {
base.deobfuscateBegin(blocks);
initLocalToInfo();
}
void initLocalToInfo() {
localToInfo.Clear();
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count - 1; i++) {
var ldsfld = instrs[i];
if (ldsfld.OpCode.Code != Code.Ldsfld)
continue;
var stloc = instrs[i + 1];
if (!stloc.isStloc())
continue;
var info = fieldToInfo.find((FieldReference)ldsfld.Operand);
if (info == null)
continue;
var local = DotNetUtils.getLocalVar(blocks.Locals, stloc.Instruction);
if (local == null)
continue;
localToInfo[local] = info;
}
}
}
protected override bool deobfuscate(Block block) {
bool changed = false;
constantsReader = null;
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
bool ch = deobfuscate1(block, i);
if (ch) {
changed = true;
continue;
}
ch = deobfuscate2(block, i);
if (ch) {
changed = true;
continue;
}
ch = deobfuscate3(block, i);
if (ch) {
changed = true;
continue;
}
}
return changed;
}
bool deobfuscate1(Block block, int i) {
var instrs = block.Instructions;
if (i >= instrs.Count - 2)
return false;
var ldloc = instrs[i];
if (!ldloc.isLdloc())
return false;
var local = DotNetUtils.getLocalVar(blocks.Locals, ldloc.Instruction);
if (local == null)
return false;
FieldInfo info;
if (!localToInfo.TryGetValue(local, out info))
return false;
var ldci4 = instrs[i + 1];
if (!ldci4.isLdcI4())
return false;
var ldelem = instrs[i + 2];
if (ldelem.OpCode.Code != Code.Ldelem_U1)
return false;
block.remove(i, 3 - 1);
instrs[i] = new Instr(DotNetUtils.createLdci4(info.array[ldci4.getLdcI4Value()]));
return true;
}
bool deobfuscate2(Block block, int i) {
var instrs = block.Instructions;
if (i >= instrs.Count - 2)
return false;
var ldsfld = instrs[i];
if (ldsfld.OpCode.Code != Code.Ldsfld)
return false;
var info = fieldToInfo.find(ldsfld.Operand as FieldReference);
if (info == null)
return false;
var ldci4 = instrs[i + 1];
if (!ldci4.isLdcI4())
return false;
var ldelem = instrs[i + 2];
if (ldelem.OpCode.Code != Code.Ldelem_U1)
return false;
block.remove(i, 3 - 1);
instrs[i] = new Instr(DotNetUtils.createLdci4(info.array[ldci4.getLdcI4Value()]));
return true;
}
bool deobfuscate3(Block block, int i) {
var instrs = block.Instructions;
if (i + 1 >= instrs.Count)
return false;
int start = i;
var ldsfld = instrs[i];
if (ldsfld.OpCode.Code != Code.Ldsfld)
return false;
var info = fieldToInfo.find(ldsfld.Operand as FieldReference);
if (info == null)
return false;
if (!instrs[i + 1].isLdcI4())
return false;
var constants = getConstantsReader(block);
int value;
i += 2;
if (!constants.getInt32(ref i, out value))
return false;
if (i >= instrs.Count)
return false;
var stelem = instrs[i];
if (stelem.OpCode.Code != Code.Stelem_I1)
return false;
block.remove(start, i - start + 1);
return true;
}
DsConstantsReader getConstantsReader(Block block) {
if (constantsReader != null)
return constantsReader;
return constantsReader = new DsConstantsReader(block.Instructions);
}
public IEnumerable<FieldDefinition> cleanUp() {
var removedFields = new List<FieldDefinition>();
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<Instruction> allInstructions;
IList<ExceptionHandler> 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<FieldInfo, bool> findFieldsToKeep() {
var keep = new Dictionary<FieldInfo, bool>();
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;
}
}
}

View File

@ -29,12 +29,16 @@ namespace de4dot.code.deobfuscators.DeepSea {
class AssemblyResolver : ResolverBase {
Version version;
List<FieldInfo> fieldInfos;
MethodDefinition decryptMethod;
enum Version {
Unknown,
V3Old,
V3,
V4,
V404,
V41,
V41SL,
}
public class AssemblyInfo {
@ -67,6 +71,10 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
}
public MethodDefinition DecryptMethod {
get { return decryptMethod; }
}
public AssemblyResolver(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob)
: base(module, simpleDeobfuscator, deob) {
}
@ -89,6 +97,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
continue;
if (!DotNetUtils.isMethod(method, "System.Void", "(System.String)"))
continue;
simpleDeobfuscator.deobfuscate(method);
if (!new LocalTypes(method).all(requiredLocals_sl))
continue;
@ -102,10 +111,50 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
void updateVersion(MethodDefinition handler) {
if (isV3Old(handler))
if (isV3Old(handler)) {
version = Version.V3Old;
else
version = Version.V3;
return;
}
if (isV3SL(handler)) {
version = Version.V3; // 3.x-4.0.4
return;
}
if (isV41SL(handler)) {
version = Version.V41SL;
return;
}
}
static bool isV3SL(MethodDefinition handler) {
var instrs = handler.Body.Instructions;
for (int i = 0; i < instrs.Count - 3; i++) {
if (!DotNetUtils.isLdloc(instrs[i]))
continue;
if (instrs[i + 1].OpCode.Code != Code.Add)
continue;
if (!DotNetUtils.isLdcI4(instrs[i + 2]))
continue;
if (instrs[i + 3].OpCode.Code != Code.And)
continue;
return true;
}
return false;
}
static bool isV41SL(MethodDefinition handler) {
var instrs = handler.Body.Instructions;
for (int i = 0; i < instrs.Count; i++) {
if (!DotNetUtils.isLdcI4(instrs[i]) || DotNetUtils.getLdcI4Value(instrs[i]) != 5)
continue;
if (instrs[i + 1].OpCode.Code != Code.And)
continue;
if (!DotNetUtils.isLdcI4(instrs[i + 2]) || DotNetUtils.getLdcI4Value(instrs[i + 2]) != 0x1F)
continue;
if (instrs[i + 3].OpCode.Code != Code.And)
continue;
return true;
}
return false;
}
static bool isV3Old(MethodDefinition method) {
@ -127,10 +176,19 @@ namespace de4dot.code.deobfuscators.DeepSea {
simpleDeobfuscator.deobfuscate(handler);
List<FieldInfo> fieldInfosTmp;
if (checkHandlerV4(handler, out fieldInfosTmp) ||
checkHandlerV4_0_4(handler, out fieldInfosTmp)) {
MethodDefinition decryptMethodTmp;
if (checkHandlerV4(handler, out fieldInfosTmp, out decryptMethodTmp)) {
version = Version.V4;
fieldInfos = fieldInfosTmp;
decryptMethod = decryptMethodTmp;
return true;
}
Version versionTmp = checkHandlerV404_41(handler, out fieldInfosTmp, out decryptMethodTmp);
if (fieldInfosTmp.Count != 0) {
version = versionTmp;
fieldInfos = fieldInfosTmp;
decryptMethod = decryptMethodTmp;
return true;
}
@ -163,8 +221,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
// 4.0.1.18 .. 4.0.3
bool checkHandlerV4(MethodDefinition handler, out List<FieldInfo> fieldInfos) {
bool checkHandlerV4(MethodDefinition handler, out List<FieldInfo> fieldInfos, out MethodDefinition decryptMethod) {
fieldInfos = new List<FieldInfo>();
decryptMethod = null;
var instrs = handler.Body.Instructions;
for (int i = 0; i < instrs.Count - 3; i++) {
@ -193,21 +252,25 @@ namespace de4dot.code.deobfuscators.DeepSea {
call = instrs[index++];
if (call.OpCode.Code != Code.Call)
return false;
if (!DotNetUtils.isMethod(call.Operand as MethodReference, "System.Reflection.Assembly", "(System.RuntimeFieldHandle,System.Int32,System.Int32)"))
var decryptMethodTmp = call.Operand as MethodDefinition;
if (!DotNetUtils.isMethod(decryptMethodTmp, "System.Reflection.Assembly", "(System.RuntimeFieldHandle,System.Int32,System.Int32)"))
return false;
decryptMethod = decryptMethodTmp;
fieldInfos.Add(new FieldInfo(field, magic));
}
return fieldInfos.Count != 0;
}
// 4.0.4+
bool checkHandlerV4_0_4(MethodDefinition handler, out List<FieldInfo> fieldInfos) {
// 4.0.4, 4.1+
Version checkHandlerV404_41(MethodDefinition handler, out List<FieldInfo> fieldInfos, out MethodDefinition decryptMethod) {
Version version = Version.Unknown;
fieldInfos = new List<FieldInfo>();
decryptMethod = null;
var instrs = handler.Body.Instructions;
for (int i = 0; i < instrs.Count - 8; i++) {
for (int i = 0; i < instrs.Count - 6; i++) {
int index = i;
var ldci4_len = instrs[index++];
@ -233,26 +296,137 @@ namespace de4dot.code.deobfuscators.DeepSea {
if (!DotNetUtils.isMethod(call1.Operand as MethodReference, "System.Void", "(System.Array,System.RuntimeFieldHandle)"))
continue;
if (!DotNetUtils.isLdloc(instrs[index++]))
continue;
var ldci4_magic = instrs[index++];
if (!DotNetUtils.isLdcI4(ldci4_magic))
continue;
int magic = DotNetUtils.getLdcI4Value(ldci4_magic);
var call2 = instrs[index++];
if (call2.OpCode.Code == Code.Tail)
call2 = instrs[index++];
if (call2.OpCode.Code != Code.Call)
continue;
if (!DotNetUtils.isMethod(call2.Operand as MethodReference, "System.Reflection.Assembly", "(System.Byte[],System.Int32)"))
int callIndex = getCallDecryptMethodIndex(instrs, index);
if (callIndex < 0)
continue;
var args = DsUtils.getArgValues(instrs, callIndex);
if (args == null)
continue;
var decryptMethodTmp = instrs[callIndex].Operand as MethodDefinition;
if (decryptMethodTmp == null)
continue;
int magic;
Version versionTmp;
getMagic(decryptMethodTmp, args, out versionTmp, out magic);
version = versionTmp;
decryptMethod = decryptMethodTmp;
fieldInfos.Add(new FieldInfo(field, magic));
}
return fieldInfos.Count != 0;
return version;
}
static bool getMagic(MethodDefinition method, IList<object> args, out Version version, out int magic) {
magic = 0;
int magicIndex = getMagicIndex(method, out version);
if (magicIndex < 0 || magicIndex >= args.Count)
return false;
var val = args[magicIndex];
if (!(val is int))
return false;
magic = (int)val;
return true;
}
static int getMagicIndex(MethodDefinition method, out Version version) {
int magicIndex = getMagicIndex404(method);
if (magicIndex >= 0) {
version = Version.V404;
return magicIndex;
}
magicIndex = getMagicIndex41Trial(method);
if (magicIndex >= 0) {
version = Version.V41;
return magicIndex;
}
version = Version.Unknown;
return -1;
}
static int getMagicIndex404(MethodDefinition method) {
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count - 4; i++) {
int index = i;
if (!DotNetUtils.isLdloc(instrs[index++]))
continue;
var ldarg = instrs[index++];
if (!DotNetUtils.isLdarg(ldarg))
continue;
if (instrs[index++].OpCode.Code != Code.Add)
continue;
var ldci4 = instrs[index++];
if (!DotNetUtils.isLdcI4(ldci4))
continue;
if (DotNetUtils.getLdcI4Value(ldci4) != 0xFF)
continue;
return DotNetUtils.getArgIndex(ldarg);
}
return -1;
}
static int getMagicIndex41Trial(MethodDefinition method) {
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count - 4; i++) {
int index = i;
if (instrs[index++].OpCode.Code != Code.Div)
continue;
var ldarg = instrs[index++];
if (!DotNetUtils.isLdarg(ldarg))
continue;
if (instrs[index++].OpCode.Code != Code.Add)
continue;
var ldci4 = instrs[index++];
if (!DotNetUtils.isLdcI4(ldci4))
continue;
if (DotNetUtils.getLdcI4Value(ldci4) != 0xFF)
continue;
return DotNetUtils.getArgIndex(ldarg);
}
return -1;
}
static int getCallDecryptMethodIndex(IList<Instruction> instrs, int index) {
index = getRetIndex(instrs, index);
if (index < 0)
return -1;
for (int i = index - 1; i >= 0; i--) {
var instr = instrs[i];
if (!isCallOrNext(instr))
break;
if (instr.OpCode.Code != Code.Call)
continue;
var calledMethod = instr.Operand as MethodReference;
if (calledMethod == null || calledMethod.Parameters.Count < 2)
continue;
return i;
}
return -1;
}
static int getRetIndex(IList<Instruction> instrs, int index) {
for (int i = index; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode.Code == Code.Ret)
return i;
if (!isCallOrNext(instr))
break;
}
return -1;
}
static bool isCallOrNext(Instruction instr) {
switch (instr.OpCode.FlowControl) {
case FlowControl.Call:
case FlowControl.Next:
return true;
default:
return false;
}
}
public IEnumerable<AssemblyInfo> getAssemblyInfos() {
@ -261,16 +435,22 @@ namespace de4dot.code.deobfuscators.DeepSea {
switch (version) {
case Version.V3Old:
return getAssemblyInfos(resource => decryptResourceV3Old(resource));
case Version.V3:
return getAssemblyInfosV3();
return getAssemblyInfos(resource => decryptResourceV3(resource));
case Version.V4:
case Version.V404:
return getAssemblyInfosV4();
case Version.V41:
return getAssemblyInfosV41();
case Version.V41SL:
return getAssemblyInfos(resource => decryptResourceV41SL(resource));
default:
throw new ApplicationException("Unknown version");
}
}
IEnumerable<AssemblyInfo> getAssemblyInfosV3() {
IEnumerable<AssemblyInfo> getAssemblyInfos(Func<EmbeddedResource, byte[]> decrypter) {
var infos = new List<AssemblyInfo>();
foreach (var tmp in module.Resources) {
@ -279,7 +459,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
continue;
if (!Regex.IsMatch(resource.Name, @"^[0-9A-F]{40}$"))
continue;
var info = getAssemblyInfoV3(resource);
var info = getAssemblyInfo(resource, decrypter);
if (info == null)
continue;
infos.Add(info);
@ -288,9 +468,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
return infos;
}
AssemblyInfo getAssemblyInfoV3(EmbeddedResource resource) {
AssemblyInfo getAssemblyInfo(EmbeddedResource resource, Func<EmbeddedResource, byte[]> decrypter) {
try {
var decrypted = version == Version.V3Old ? decryptResourceV3Old(resource) : decryptResourceV3(resource);
var decrypted = decrypter(resource);
return getAssemblyInfo(decrypted, resource);
}
catch (Exception) {
@ -306,14 +486,14 @@ namespace de4dot.code.deobfuscators.DeepSea {
return new AssemblyInfo(decryptedData, fullName, simpleName, extension, resource);
}
IEnumerable<AssemblyInfo> getAssemblyInfosV4() {
IEnumerable<AssemblyInfo> getAssemblyInfos(Func<byte[], int, byte[]> decrypter) {
var infos = new List<AssemblyInfo>();
if (fieldInfos == null)
return infos;
foreach (var fieldInfo in fieldInfos) {
var decrypted = decryptResourceV4(fieldInfo.field.InitialValue, fieldInfo.magic);
var decrypted = decrypter(fieldInfo.field.InitialValue, fieldInfo.magic);
infos.Add(getAssemblyInfo(decrypted, null));
fieldInfo.field.InitialValue = new byte[1];
fieldInfo.field.FieldType = module.TypeSystem.Byte;
@ -321,5 +501,19 @@ namespace de4dot.code.deobfuscators.DeepSea {
return infos;
}
IEnumerable<AssemblyInfo> getAssemblyInfosV4() {
return getAssemblyInfos((data, magic) => decryptResourceV4(data, magic));
}
IEnumerable<AssemblyInfo> getAssemblyInfosV41() {
return getAssemblyInfos((data, magic) => inflateIfNeeded(decrypt41Trial(data, magic)));
}
static byte[] decrypt41Trial(byte[] data, int magic) {
for (int i = 0; i < data.Length; i++)
data[i] ^= (byte)(i / 3 + magic);
return data;
}
}
}

View File

@ -0,0 +1,213 @@
/*
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 de4dot.blocks;
using de4dot.blocks.cflow;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace de4dot.code.deobfuscators.DeepSea {
class CastDeobfuscator : IBlocksDeobfuscator {
Blocks blocks;
Dictionary<VariableDefinition, LocalInfo> localInfos = new Dictionary<VariableDefinition, LocalInfo>();
class LocalInfo {
public readonly VariableDefinition local;
TypeReference type;
bool isValid;
public TypeReference CastType {
get { return type; }
set {
if (!isValid)
return;
if (value == null) {
invalid();
return;
}
if (type != null && !MemberReferenceHelper.compareTypes(type, value)) {
invalid();
return;
}
type = value;
}
}
public LocalInfo(VariableDefinition local) {
this.local = local;
this.isValid = true;
}
public void invalid() {
isValid = false;
type = null;
}
public override string ToString() {
if (type == null)
return string.Format("{0} - INVALID", local);
return string.Format("{0} - {1:X8} {2}", local, type.MetadataToken.ToInt32(), type.FullName);
}
}
public bool ExecuteOnNoChange {
get { return true; }
}
public void deobfuscateBegin(Blocks blocks) {
this.blocks = blocks;
}
public bool deobfuscate(List<Block> allBlocks) {
if (!init(allBlocks))
return false;
bool changed = false;
var indexesToRemove = new List<int>();
foreach (var block in allBlocks) {
indexesToRemove.Clear();
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count - 1; i++) {
var instr = instrs[i];
if (instr.OpCode.Code == Code.Ldloca || instr.OpCode.Code == Code.Ldloca_S) {
var local = instr.Operand as VariableDefinition;
if (local == null)
continue;
localInfos[local].invalid();
}
else if (instr.isLdloc()) {
var local = DotNetUtils.getLocalVar(blocks.Locals, instr.Instruction);
if (local == null)
continue;
var localInfo = localInfos[local];
var cast = instrs[i + 1];
if (localInfo.CastType == null)
continue;
if (!isCast(cast))
throw new ApplicationException("Not a cast instr");
indexesToRemove.Add(i + 1);
}
}
if (indexesToRemove.Count > 0) {
block.remove(indexesToRemove);
changed = true;
}
}
foreach (var info in localInfos.Values) {
if (info.CastType == null)
continue;
info.local.VariableType = info.CastType;
}
if (changed) {
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count - 1; i++) {
var instr = instrs[i];
int castIndex = i + 1;
if (instr.OpCode.Code == Code.Dup) {
if (i == 0)
continue;
castIndex = i;
instr = instrs[i - 1];
}
if (instr.isLdarg())
addCast(block, castIndex, i + 1, DotNetUtils.getArgType(blocks.Method, instr.Instruction));
else if (instr.OpCode.Code == Code.Ldfld || instr.OpCode.Code == Code.Ldsfld) {
var field = instr.Operand as FieldReference;
if (field == null)
continue;
addCast(block, castIndex, i + 1, field.FieldType);
}
else if (instr.OpCode.Code == Code.Call || instr.OpCode.Code == Code.Callvirt) {
var calledMethod = instr.Operand as MethodReference;
if (calledMethod == null || !DotNetUtils.hasReturnValue(calledMethod))
continue;
addCast(block, castIndex, i + 1, calledMethod.MethodReturnType.ReturnType);
}
}
}
}
return changed;
}
bool addCast(Block block, int castIndex, int index, TypeReference type) {
if (type == null)
return false;
if (castIndex >= block.Instructions.Count || index >= block.Instructions.Count)
return false;
var stloc = block.Instructions[index];
if (!stloc.isStloc())
return false;
var local = DotNetUtils.getLocalVar(blocks.Locals, stloc.Instruction);
if (local == null)
return false;
var localInfo = localInfos[local];
if (localInfo.CastType == null)
return false;
if (!MemberReferenceHelper.compareTypes(localInfo.CastType, type))
block.insert(castIndex, new Instruction(OpCodes.Castclass, localInfo.CastType));
return true;
}
bool init(List<Block> allBlocks) {
localInfos.Clear();
foreach (var local in blocks.Locals)
localInfos[local] = new LocalInfo(local);
if (localInfos.Count == 0)
return false;
foreach (var block in allBlocks) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count - 1; i++) {
var ldloc = instrs[i];
if (!ldloc.isLdloc())
continue;
var local = DotNetUtils.getLocalVar(blocks.Locals, ldloc.Instruction);
if (local == null)
continue;
var localInfo = localInfos[local];
localInfo.CastType = getCastType(instrs[i + 1]);
}
}
return true;
}
static bool isCast(Instr instr) {
return instr.OpCode.Code == Code.Castclass || instr.OpCode.Code == Code.Isinst;
}
static TypeReference getCastType(Instr instr) {
if (!isCast(instr))
return null;
return instr.Operand as TypeReference;
}
}
}

View File

@ -31,6 +31,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
BoolOption decryptResources;
BoolOption dumpEmbeddedAssemblies;
BoolOption restoreFields;
BoolOption renameResourceKeys;
BoolOption castDeobfuscation;
public DeobfuscatorInfo()
: base() {
@ -39,6 +41,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
decryptResources = new BoolOption(null, makeArgName("rsrc"), "Decrypt resources", true);
dumpEmbeddedAssemblies = new BoolOption(null, makeArgName("embedded"), "Dump embedded assemblies", true);
restoreFields = new BoolOption(null, makeArgName("fields"), "Restore fields", true);
renameResourceKeys = new BoolOption(null, makeArgName("keys"), "Rename resource keys", true);
castDeobfuscation = new BoolOption(null, makeArgName("casts"), "Deobfuscate casts", true);
}
public override string Name {
@ -57,6 +61,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
DecryptResources = decryptResources.get(),
DumpEmbeddedAssemblies = dumpEmbeddedAssemblies.get(),
RestoreFields = restoreFields.get(),
RenameResourceKeys = renameResourceKeys.get(),
CastDeobfuscation = castDeobfuscation.get(),
});
}
@ -67,6 +73,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
decryptResources,
dumpEmbeddedAssemblies,
restoreFields,
renameResourceKeys,
castDeobfuscation,
};
}
}
@ -80,6 +88,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
ResourceResolver resourceResolver;
AssemblyResolver assemblyResolver;
FieldsRestorer fieldsRestorer;
ArrayBlockDeobfuscator arrayBlockDeobfuscator;
internal class Options : OptionsBase {
public bool InlineMethods { get; set; }
@ -87,6 +96,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
public bool DecryptResources { get; set; }
public bool DumpEmbeddedAssemblies { get; set; }
public bool RestoreFields { get; set; }
public bool RenameResourceKeys { get; set; }
public bool CastDeobfuscation { get; set; }
}
public override string Type {
@ -105,17 +116,32 @@ namespace de4dot.code.deobfuscators.DeepSea {
get { return startedDeobfuscating ? options.InlineMethods : true; }
}
public override IMethodCallInliner MethodCallInliner {
public override IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
get {
var list = new List<IBlocksDeobfuscator>(getBlocksDeobfuscators());
if (CanInlineMethods)
return new MethodCallInliner();
return new NoMethodInliner();
list.Add(new DsMethodCallInliner(new CachedCflowDeobfuscator(getBlocksDeobfuscators())));
return list;
}
}
List<IBlocksDeobfuscator> getBlocksDeobfuscators() {
var list = new List<IBlocksDeobfuscator>();
if (arrayBlockDeobfuscator.Detected)
list.Add(arrayBlockDeobfuscator);
if (!startedDeobfuscating || options.CastDeobfuscation)
list.Add(new CastDeobfuscator());
return list;
}
public Deobfuscator(Options options)
: base(options) {
this.options = options;
if (options.RenameResourceKeys)
this.RenamingOptions |= RenamingOptions.RenameResourceKeys;
else
this.RenamingOptions &= ~RenamingOptions.RenameResourceKeys;
}
protected override int detectInternal() {
@ -131,6 +157,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
protected override void scanForObfuscator() {
staticStringInliner.UseUnknownArgs = true;
arrayBlockDeobfuscator = new ArrayBlockDeobfuscator(module);
arrayBlockDeobfuscator.init();
stringDecrypter = new StringDecrypter(module);
stringDecrypter.find(DeobfuscatedFile);
resourceResolver = new ResourceResolver(module, DeobfuscatedFile, this);
@ -146,8 +175,10 @@ namespace de4dot.code.deobfuscators.DeepSea {
if (detectMethodProxyObfuscation())
return DeobfuscatorInfo.THE_NAME + " 3.5";
return DeobfuscatorInfo.THE_NAME + " 1.x-3.x";
case StringDecrypter.DecrypterVersion.V4:
return DeobfuscatorInfo.THE_NAME + " 4.x";
case StringDecrypter.DecrypterVersion.V4_0:
return DeobfuscatorInfo.THE_NAME + " 4.0";
case StringDecrypter.DecrypterVersion.V4_1:
return DeobfuscatorInfo.THE_NAME + " 4.1";
}
return DeobfuscatorInfo.THE_NAME;
@ -165,7 +196,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
continue;
if (checkedMethods++ >= 1000)
goto done;
if (!DeepSea.MethodCallInliner.canInline(method))
if (!DsMethodCallInliner.canInline(method))
continue;
foundProxies++;
}
@ -207,6 +238,7 @@ done:
addCctorInitCallToBeRemoved(resourceResolver.InitMethod);
addCallToBeRemoved(module.EntryPoint, resourceResolver.InitMethod);
addMethodToBeRemoved(resourceResolver.InitMethod, "Resource resolver init method");
addMethodToBeRemoved(resourceResolver.InitMethod2, "Resource resolver init method #2");
addMethodToBeRemoved(resourceResolver.HandlerMethod, "Resource resolver handler method");
addMethodToBeRemoved(resourceResolver.GetDataMethod, "Resource resolver 'get resource data' method");
}
@ -224,6 +256,7 @@ done:
addCallToBeRemoved(module.EntryPoint, assemblyResolver.InitMethod);
addMethodToBeRemoved(assemblyResolver.InitMethod, "Assembly resolver init method");
addMethodToBeRemoved(assemblyResolver.HandlerMethod, "Assembly resolver handler method");
addMethodToBeRemoved(assemblyResolver.DecryptMethod, "Assembly resolver decrypt method");
}
public override void deobfuscateMethodEnd(Blocks blocks) {
@ -245,13 +278,15 @@ done:
stringDecrypter.cleanup();
}
addFieldsToBeRemoved(arrayBlockDeobfuscator.cleanUp(), "Control flow obfuscation array");
base.deobfuscateEnd();
}
void removeInlinedMethods() {
if (!options.InlineMethods || !options.RemoveInlinedMethods)
return;
removeInlinedMethods(DsInlinedMethodsFinder.find(module));
removeInlinedMethods(DsInlinedMethodsFinder.find(module, staticStringInliner.Methods));
}
public override IEnumerable<int> getStringDecrypterMethods() {

View File

@ -17,13 +17,24 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
namespace de4dot.blocks.cflow {
public class NoMethodInliner : IMethodCallInliner {
public void init(Blocks blocks, Block block) {
using System.Collections.Generic;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.DeepSea {
class DsConstantsReader : ConstantsReader {
public DsConstantsReader(List<Instr> instrs)
: base(instrs) {
}
public bool deobfuscate() {
return false;
protected override bool getLocalConstant(Instruction instr, out int value) {
value = 0;
return true;
}
protected override bool getArgConstant(Instruction instr, out int value) {
value = 0;
return true;
}
}
}

View File

@ -22,12 +22,16 @@ using Mono.Cecil;
namespace de4dot.code.deobfuscators.DeepSea {
static class DsInlinedMethodsFinder {
public static List<MethodDefinition> find(ModuleDefinition module) {
public static List<MethodDefinition> find(ModuleDefinition module, IEnumerable<MethodDefinition> notInlinedMethods) {
var notInlinedMethodsDict = new Dictionary<MethodDefinition, bool>();
foreach (var method in notInlinedMethods)
notInlinedMethodsDict[method] = true;
var inlinedMethods = new List<MethodDefinition>();
foreach (var type in module.GetTypes()) {
foreach (var method in type.Methods) {
if (MethodCallInliner.canInline(method))
if (!notInlinedMethodsDict.ContainsKey(method) && DsMethodCallInliner.canInline(method))
inlinedMethods.Add(method);
}
}

View File

@ -0,0 +1,333 @@
/*
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.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 DsMethodCallInliner : MethodCallInlinerBase {
InstructionEmulator instructionEmulator = new InstructionEmulator();
List<ParameterDefinition> parameters;
ParameterDefinition arg1, arg2;
Value returnValue;
MethodDefinition methodToInline;
CachedCflowDeobfuscator cflowDeobfuscator;
public DsMethodCallInliner(CachedCflowDeobfuscator cflowDeobfuscator) {
this.cflowDeobfuscator = cflowDeobfuscator;
}
protected override bool deobfuscateInternal() {
bool changed = false;
var instructions = block.Instructions;
for (int i = 0; i < instructions.Count; i++) {
var instr = instructions[i].Instruction;
if (instr.OpCode.Code == Code.Call)
changed |= inlineMethod(instr, i);
}
return changed;
}
bool inlineMethod(Instruction callInstr, int instrIndex) {
var method = callInstr.Operand as MethodDefinition;
if (method == null)
return false;
if (!canInline(method))
return false;
if (instrIndex < 2)
return false;
var ldci4_1st = block.Instructions[instrIndex - 2];
var ldci4_2nd = block.Instructions[instrIndex - 1];
if (!ldci4_1st.isLdcI4() || !ldci4_2nd.isLdcI4())
return false;
if (!inlineMethod(method, instrIndex, ldci4_1st.getLdcI4Value(), ldci4_2nd.getLdcI4Value()))
return false;
return true;
}
bool inlineMethod(MethodDefinition methodToInline, int instrIndex, int const1, int const2) {
this.methodToInline = methodToInline = cflowDeobfuscator.deobfuscate(methodToInline);
parameters = DotNetUtils.getParameters(methodToInline);
arg1 = parameters[parameters.Count - 2];
arg2 = parameters[parameters.Count - 1];
returnValue = null;
instructionEmulator.init(methodToInline);
foreach (var arg in parameters) {
if (arg.ParameterType.EType >= ElementType.Boolean && arg.ParameterType.EType <= ElementType.U4)
instructionEmulator.setArg(arg, new Int32Value(0));
}
instructionEmulator.setArg(arg1, new Int32Value(const1));
instructionEmulator.setArg(arg2, new Int32Value(const2));
int index = 0;
if (!emulateInstructions(ref index, false))
return false;
var patcher = tryInlineOtherMethod(instrIndex, methodToInline, methodToInline.Body.Instructions[index], index + 1, 2);
if (patcher == null)
return false;
if (!emulateToReturn(patcher.afterIndex, patcher.lastInstr))
return false;
patcher.patch(block);
block.insert(instrIndex, Instruction.Create(OpCodes.Pop));
block.insert(instrIndex, Instruction.Create(OpCodes.Pop));
return true;
}
bool emulateInstructions(ref int index, bool allowUnknownArgs) {
Instruction instr;
var instrs = methodToInline.Body.Instructions;
int counter = 0;
var foundOpCodes = new Dictionary<Code, bool>();
bool checkInstrs = false;
while (true) {
if (counter++ >= 50)
return false;
if (index < 0 || index >= instrs.Count)
return false;
instr = instrs[index];
foundOpCodes[instr.OpCode.Code] = true;
switch (instr.OpCode.Code) {
case Code.Stloc:
case Code.Stloc_S:
case Code.Stloc_0:
case Code.Stloc_1:
case Code.Stloc_2:
case Code.Stloc_3:
case Code.Ldloc:
case Code.Ldloc_S:
case Code.Ldloc_0:
case Code.Ldloc_1:
case Code.Ldloc_2:
case Code.Ldloc_3:
case Code.Ldc_I4:
case Code.Ldc_I4_0:
case Code.Ldc_I4_1:
case Code.Ldc_I4_2:
case Code.Ldc_I4_3:
case Code.Ldc_I4_4:
case Code.Ldc_I4_5:
case Code.Ldc_I4_6:
case Code.Ldc_I4_7:
case Code.Ldc_I4_8:
case Code.Ldc_I4_M1:
case Code.Ldc_I4_S:
case Code.Add:
case Code.Sub:
case Code.Xor:
case Code.Or:
case Code.Nop:
case Code.Dup:
case Code.Mul:
case Code.Rem:
case Code.Div:
instructionEmulator.emulate(instr);
index++;
break;
case Code.Ldarg:
case Code.Ldarg_S:
case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
var arg = DotNetUtils.getParameter(parameters, instr);
if (arg != arg1 && arg != arg2) {
if (!allowUnknownArgs)
goto done;
checkInstrs = true;
}
instructionEmulator.emulate(instr);
index++;
break;
case Code.Call:
case Code.Callvirt:
case Code.Newobj:
goto done;
case Code.Switch:
var value = instructionEmulator.pop() as Int32Value;
if (value == null || !value.allBitsValid())
return false;
var targets = (Instruction[])instr.Operand;
if (value.value >= 0 && value.value < targets.Length)
index = instrs.IndexOf(targets[value.value]);
else
index++;
break;
case Code.Br:
case Code.Br_S:
index = instrs.IndexOf((Instruction)instr.Operand);
break;
case Code.Brtrue:
case Code.Brtrue_S:
index = emulateBrtrue(index);
break;
case Code.Brfalse:
case Code.Brfalse_S:
index = emulateBrfalse(index);
break;
case Code.Isinst:
case Code.Castclass:
if (returnValue != null && instructionEmulator.peek() == returnValue) {
// Do nothing
}
else
instructionEmulator.emulate(instr);
index++;
break;
default:
if (instr.OpCode.OpCodeType != OpCodeType.Prefix)
goto done;
index++;
break;
}
}
done:
if (checkInstrs) {
if (!foundOpCodes.ContainsKey(Code.Ldc_I4_1))
return false;
if (!foundOpCodes.ContainsKey(Code.Ldc_I4_2))
return false;
if (!foundOpCodes.ContainsKey(Code.Add))
return false;
if (!foundOpCodes.ContainsKey(Code.Dup))
return false;
if (!foundOpCodes.ContainsKey(Code.Mul))
return false;
if (!foundOpCodes.ContainsKey(Code.Rem))
return false;
if (!foundOpCodes.ContainsKey(Code.Brtrue) && !foundOpCodes.ContainsKey(Code.Brtrue_S) &&
!foundOpCodes.ContainsKey(Code.Brfalse) && !foundOpCodes.ContainsKey(Code.Brfalse_S))
return false;
}
return true;
}
int emulateBranch(int stackArgs, Bool3 cond, Instruction instrTrue, Instruction instrFalse) {
if (cond == Bool3.Unknown)
return -1;
Instruction instr = cond == Bool3.True ? instrTrue : instrFalse;
return methodToInline.Body.Instructions.IndexOf(instr);
}
int emulateBrtrue(int instrIndex) {
var val1 = instructionEmulator.pop();
var instr = methodToInline.Body.Instructions[instrIndex];
var instrTrue = (Instruction)instr.Operand;
var instrFalse = methodToInline.Body.Instructions[instrIndex + 1];
if (val1.isInt32())
return emulateBranch(1, Int32Value.compareTrue((Int32Value)val1), instrTrue, instrFalse);
return -1;
}
int emulateBrfalse(int instrIndex) {
var val1 = instructionEmulator.pop();
var instr = methodToInline.Body.Instructions[instrIndex];
var instrTrue = (Instruction)instr.Operand;
var instrFalse = methodToInline.Body.Instructions[instrIndex + 1];
if (val1.isInt32())
return emulateBranch(1, Int32Value.compareFalse((Int32Value)val1), instrTrue, instrFalse);
return -1;
}
bool emulateToReturn(int index, Instruction lastInstr) {
int pushes, pops;
DotNetUtils.calculateStackUsage(lastInstr, false, out pushes, out pops);
for (int i = 0; i < pops; i++)
instructionEmulator.pop();
returnValue = null;
if (pushes != 0) {
returnValue = new UnknownValue();
instructionEmulator.setProtected(returnValue);
instructionEmulator.push(returnValue);
}
if (!emulateInstructions(ref index, true))
return false;
if (index >= methodToInline.Body.Instructions.Count)
return false;
if (methodToInline.Body.Instructions[index].OpCode.Code != Code.Ret)
return false;
if (returnValue != null) {
if (instructionEmulator.pop() != returnValue)
return false;
}
return instructionEmulator.stackSize() == 0;
}
public static bool canInline(MethodDefinition method) {
if (method == null || method.Body == null)
return false;
if (method.Attributes != (MethodAttributes.Assembly | MethodAttributes.Static))
return false;
if (method.GenericParameters.Count > 0)
return false;
if (method.Body.ExceptionHandlers.Count > 0)
return false;
var parameters = method.Parameters;
int paramCount = parameters.Count;
if (paramCount < 2)
return false;
var param1 = parameters[paramCount - 1];
var param2 = parameters[paramCount - 2];
if (!isIntType(param1.ParameterType.EType))
return false;
if (!isIntType(param2.ParameterType.EType))
return false;
return true;
}
static bool isIntType(ElementType etype) {
return etype == ElementType.Char || etype == ElementType.I2 || etype == ElementType.I4;
}
protected override bool isReturn(MethodDefinition methodToInline, int instrIndex) {
int oldIndex = instrIndex;
if (base.isReturn(methodToInline, oldIndex))
return true;
return false;
}
}
}

View File

@ -0,0 +1,91 @@
/*
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.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.DeepSea {
static class DsUtils {
public static IList<object> getArgValues(IList<Instruction> instrs, int index) {
return getArgValues(DotNetUtils.getArgPushes(instrs, index));
}
public static IList<object> getArgValues(IList<Instruction> argInstrs) {
if (argInstrs == null)
return null;
var args = new List<object>(argInstrs.Count);
foreach (var argInstr in argInstrs) {
object arg;
getArgValue(argInstr, out arg);
args.Add(arg);
}
return args;
}
public static bool getArgValue(MethodDefinition method, int index, out object arg) {
return getArgValue(method.Body.Instructions[index], out arg);
}
public static bool getArgValue(Instruction instr, out object arg) {
switch (instr.OpCode.Code) {
case Code.Ldc_I4_S: arg = (int)(sbyte)instr.Operand; return true;
case Code.Ldc_I4_M1: arg = -1; return true;
case Code.Ldc_I4_0: arg = 0; return true;
case Code.Ldc_I4_1: arg = 1; return true;
case Code.Ldc_I4_2: arg = 2; return true;
case Code.Ldc_I4_3: arg = 3; return true;
case Code.Ldc_I4_4: arg = 4; return true;
case Code.Ldc_I4_5: arg = 5; return true;
case Code.Ldc_I4_6: arg = 6; return true;
case Code.Ldc_I4_7: arg = 7; return true;
case Code.Ldc_I4_8: arg = 8; return true;
case Code.Ldnull: arg = null; return true;
case Code.Ldstr:
case Code.Ldc_I4:
case Code.Ldc_I8:
case Code.Ldc_R4:
case Code.Ldc_R8:
arg = instr.Operand;
return true;
case Code.Ldarg:
case Code.Ldarg_S:
case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
case Code.Ldloc:
case Code.Ldloc_S:
case Code.Ldloc_0:
case Code.Ldloc_1:
case Code.Ldloc_2:
case Code.Ldloc_3:
arg = null;
return true;
default:
arg = null;
return false;
}
}
}
}

View File

@ -21,6 +21,7 @@ using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Metadata;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.DeepSea {
@ -35,7 +36,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
get {
var list = new List<TypeDefinition>(structToOwners.Count);
foreach (var structType in structToOwners.getKeys()) {
if (structType.Methods.Count != 0)
if (!hasNoMethods(structType))
continue;
list.Add(structType);
@ -44,6 +45,21 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
}
static bool hasNoMethods(TypeDefinition type) {
if (type.Methods.Count == 0)
return true;
if (type.BaseType == null)
return false;
if (type.BaseType.EType != ElementType.Object)
return false;
if (type.Methods.Count != 1)
return false;
var ctor = type.Methods[0];
if (ctor.Name != ".ctor" || ctor.Parameters.Count != 0)
return false;
return true;
}
public FieldsRestorer(ModuleDefinition module) {
this.module = module;
}
@ -78,7 +94,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
foreach (var type in module.GetTypes()) {
foreach (var field in getPossibleFields(type)) {
var fieldType = DotNetUtils.getType(module, field.FieldType);
if (fieldType == null || !fieldType.IsValueType)
if (fieldType == null)
continue;
if (!checkBaseType(fieldType))
continue;
if ((fieldType.Attributes & ~TypeAttributes.Sealed) != TypeAttributes.NestedAssembly)
continue;
@ -90,9 +108,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
continue;
if (fieldType.HasEvents || fieldType.HasProperties || fieldType.HasInterfaces)
continue;
if (hasNonStaticMethods(fieldType))
if (checkMethods(fieldType))
continue;
if (hasStaticFields(fieldType))
if (!checkFields(fieldType))
continue;
List<TypeDefinition> list;
@ -132,7 +150,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
if (field.Attributes != FieldAttributes.Private)
continue;
var fieldType = DotNetUtils.getType(module, field.FieldType);
if (fieldType == null || !fieldType.IsValueType)
if (fieldType == null)
continue;
if (!checkBaseType(fieldType))
continue;
var list = typeToFields.find(fieldType);
if (list == null)
@ -146,6 +166,12 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
}
static bool checkBaseType(TypeDefinition type) {
if (type == null || type.BaseType == null)
return false;
return type.BaseType.FullName == "System.ValueType" || type.BaseType.EType == ElementType.Object;
}
void removeType(Dictionary<TypeDefinition, List<TypeDefinition>> candidates, TypeReference type) {
var typeDef = DotNetUtils.getType(module, type);
if (typeDef == null)
@ -153,10 +179,12 @@ namespace de4dot.code.deobfuscators.DeepSea {
candidates.Remove(typeDef);
}
static bool hasNonStaticMethods(TypeDefinition type) {
static bool checkMethods(TypeDefinition type) {
foreach (var method in type.Methods) {
if (method.Name == ".cctor")
continue;
if (type.BaseType != null && type.BaseType.EType == ElementType.Object && method.Name == ".ctor" && method.Parameters.Count == 0)
continue;
if (!method.IsStatic)
return true;
if (method.GenericParameters.Count > 0)
@ -169,22 +197,31 @@ namespace de4dot.code.deobfuscators.DeepSea {
return false;
}
static bool hasStaticFields(TypeDefinition type) {
static bool checkFields(TypeDefinition type) {
if (type.Fields.Count == 0)
return false;
foreach (var field in type.Fields) {
if (field.IsStatic)
return true;
return false;
if (!field.IsAssembly)
return false;
}
return false;
return true;
}
public void deobfuscate(Blocks blocks) {
deobfuscateNormal(blocks);
fixFieldCtorCalls(blocks);
}
void deobfuscateNormal(Blocks blocks) {
var instrsToRemove = new List<int>();
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
instrsToRemove.Clear();
var instrs = block.Instructions;
for (int i = instrs.Count - 1; i >= 0; i--) {
var instr = instrs[i];
if (instr.OpCode.Code != Code.Ldflda)
if (instr.OpCode.Code != Code.Ldflda && instr.OpCode.Code != Code.Ldfld)
continue;
var structField = instr.Operand as FieldReference;
if (structField == null || !structFieldsToFix.find(structField))
@ -199,6 +236,40 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
}
void fixFieldCtorCalls(Blocks blocks) {
if (blocks.Method.Name != ".ctor")
return;
var instrsToRemove = new List<int>();
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var stfld = instrs[i];
if (stfld.OpCode.Code != Code.Stfld)
continue;
var field = stfld.Operand as FieldReference;
if (field == null)
continue;
if (!structFieldsToFix.find(field))
continue;
var instrs2 = toInstructionList(instrs);
var instrPushes = DotNetUtils.getArgPushes(instrs2, i);
if (instrPushes == null || instrPushes.Count != 2)
continue;
block.remove(i, 1);
block.remove(instrs2.IndexOf(instrPushes[1]), 1);
block.remove(instrs2.IndexOf(instrPushes[0]), 1);
i -= 3;
}
}
}
static IList<Instruction> toInstructionList(IEnumerable<Instr> instrs) {
var newInstrs = new List<Instruction>();
foreach (var instr in instrs)
newInstrs.Add(instr.Instruction);
return newInstrs;
}
FieldDefinition getNewField(FieldReference structField, FieldReference oldFieldRef) {
var fieldsDict = typeToFieldsDict.find(structField.DeclaringType);
if (fieldsDict == null)

View File

@ -1,208 +0,0 @@
/*
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.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
using de4dot.blocks.cflow;
namespace de4dot.code.deobfuscators.DeepSea {
class MethodCallInliner : MethodCallInlinerBase {
InstructionEmulator instructionEmulator = new InstructionEmulator();
protected override bool deobfuscateInternal() {
bool changed = false;
var instructions = block.Instructions;
for (int i = 0; i < instructions.Count; i++) {
var instr = instructions[i].Instruction;
if (instr.OpCode.Code == Code.Call)
changed |= inlineMethod(instr, i);
}
return changed;
}
bool inlineMethod(Instruction callInstr, int instrIndex) {
var method = callInstr.Operand as MethodDefinition;
if (method == null)
return false;
if (!canInline(method))
return false;
if (instrIndex < 2)
return false;
var ldci4_1st = block.Instructions[instrIndex - 2];
var ldci4_2nd = block.Instructions[instrIndex - 1];
if (!ldci4_1st.isLdcI4() || !ldci4_2nd.isLdcI4())
return false;
if (!inlineMethod(method, instrIndex, ldci4_1st.getLdcI4Value(), ldci4_2nd.getLdcI4Value()))
return false;
return true;
}
bool inlineMethod(MethodDefinition methodToInline, int instrIndex, int const1, int const2) {
var parameters = DotNetUtils.getParameters(methodToInline);
var arg1 = parameters[parameters.Count - 2];
var arg2 = parameters[parameters.Count - 1];
instructionEmulator.init(methodToInline);
instructionEmulator.setArg(arg1, new Int32Value(const1));
instructionEmulator.setArg(arg2, new Int32Value(const2));
Instruction instr;
var instrs = methodToInline.Body.Instructions;
int index = 0;
int counter = 0;
while (true) {
if (counter++ >= 50)
return false;
if (index >= instrs.Count)
return false;
instr = instrs[index];
switch (instr.OpCode.Code) {
case Code.Stloc:
case Code.Stloc_S:
case Code.Stloc_0:
case Code.Stloc_1:
case Code.Stloc_2:
case Code.Stloc_3:
case Code.Ldloc:
case Code.Ldloc_S:
case Code.Ldloc_0:
case Code.Ldloc_1:
case Code.Ldloc_2:
case Code.Ldloc_3:
case Code.Ldc_I4:
case Code.Ldc_I4_0:
case Code.Ldc_I4_1:
case Code.Ldc_I4_2:
case Code.Ldc_I4_3:
case Code.Ldc_I4_4:
case Code.Ldc_I4_5:
case Code.Ldc_I4_6:
case Code.Ldc_I4_7:
case Code.Ldc_I4_8:
case Code.Ldc_I4_M1:
case Code.Ldc_I4_S:
case Code.Add:
case Code.Sub:
case Code.Xor:
case Code.Or:
case Code.Nop:
instructionEmulator.emulate(instr);
index++;
break;
case Code.Ldarg:
case Code.Ldarg_S:
case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
var arg = DotNetUtils.getParameter(parameters, instr);
if (arg != arg1 && arg != arg2)
goto checkInline;
instructionEmulator.emulate(instr);
index++;
break;
case Code.Call:
case Code.Callvirt:
case Code.Newobj:
goto checkInline;
case Code.Switch:
var value = instructionEmulator.pop() as Int32Value;
if (value == null || !value.allBitsValid())
return false;
var targets = (Instruction[])instr.Operand;
if (value.value >= 0 && value.value < targets.Length)
index = instrs.IndexOf(targets[value.value]);
else
index++;
break;
case Code.Br:
case Code.Br_S:
index = instrs.IndexOf((Instruction)instr.Operand);
break;
default:
if (instr.OpCode.OpCodeType != OpCodeType.Prefix)
return false;
index++;
break;
}
}
checkInline:
if (!inlineOtherMethod(instrIndex, methodToInline, instr, index + 1, 2))
return false;
block.insert(instrIndex, Instruction.Create(OpCodes.Pop));
block.insert(instrIndex, Instruction.Create(OpCodes.Pop));
return true;
}
public static bool canInline(MethodDefinition method) {
if (method == null || method.Body == null)
return false;
if (method.GenericParameters.Count > 0)
return false;
if (method.Body.ExceptionHandlers.Count > 0)
return false;
var parameters = method.Parameters;
int paramCount = parameters.Count;
if (paramCount < 2)
return false;
if (parameters[paramCount - 1].ParameterType.FullName != "System.Int32")
return false;
if (parameters[paramCount - 2].ParameterType.FullName != "System.Int32")
return false;
if (method.Attributes != (MethodAttributes.Assembly | MethodAttributes.Static))
return false;
if (!checkInstrs(method))
return false;
return true;
}
static bool checkInstrs(MethodDefinition method) {
bool foundSwitch = false, foundXor = false;
foreach (var instr in method.Body.Instructions) {
if (foundSwitch && foundXor)
break;
switch (instr.OpCode.Code) {
case Code.Switch:
foundSwitch = true;
break;
case Code.Xor:
foundXor = true;
break;
}
}
return foundSwitch && foundXor;
}
}
}

View File

@ -143,6 +143,14 @@ namespace de4dot.code.deobfuscators.DeepSea {
return decryptResource(data, 0, data.Length, 0);
}
protected static byte[] decryptResourceV41SL(EmbeddedResource resource) {
var data = resource.GetResourceData();
byte k = data[0];
for (int i = 0; i < data.Length - 1; i++)
data[i + 1] ^= (byte)((k << (i & 5)) + i);
return inflateIfNeeded(data, 1, data.Length - 1);
}
protected static byte[] decryptResourceV3(EmbeddedResource resource) {
return decryptResourceV3(resource.GetResourceData());
}
@ -158,7 +166,14 @@ namespace de4dot.code.deobfuscators.DeepSea {
protected static byte[] decryptResource(byte[] data, int start, int len, int magic) {
for (int i = start; i < start + len; i++)
data[i] ^= (byte)(i - start + magic);
return inflateIfNeeded(data, start, len);
}
protected static byte[] inflateIfNeeded(byte[] data) {
return inflateIfNeeded(data, 0, data.Length);
}
protected static byte[] inflateIfNeeded(byte[] data, int start, int len) {
if (BitConverter.ToInt16(data, start) != 0x5A4D)
return DeobUtils.inflate(data, start, len, true);

View File

@ -17,24 +17,70 @@
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;
namespace de4dot.code.deobfuscators.DeepSea {
class ResourceResolver : ResolverBase {
EmbeddedResource resource;
FieldDefinition resourceField;
MethodDefinition getDataMethod;
int magicV4;
bool isV3;
Data30 data30;
Data40 data40;
Data41 data41;
ResourceVersion version = ResourceVersion.Unknown;
enum ResourceVersion {
Unknown,
V3,
V40,
V41,
}
class Data30 {
public EmbeddedResource resource;
}
class Data40 {
public FieldDefinition resourceField;
public MethodDefinition resolveHandler2;
public MethodDefinition getDataMethod;
public int magic;
}
class Data41 {
public FieldDefinition resourceField;
public MethodDefinition resolveHandler2;
public int magic;
public bool isTrial;
}
class HandlerInfo {
public MethodDefinition handler;
public IList<object> args;
public HandlerInfo(MethodDefinition handler, IList<object> args) {
this.handler = handler;
this.args = args;
}
}
public MethodDefinition InitMethod2 {
get {
if (data40 != null)
return data40.resolveHandler2;
if (data41 != null)
return data41.resolveHandler2;
return null;
}
}
public MethodDefinition GetDataMethod {
get { return getDataMethod; }
get { return data40 != null ? data40.getDataMethod : null; }
}
public EmbeddedResource Resource {
get { return resource; }
get { return data30 != null ? data30.resource : null; }
}
public ResourceResolver(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob)
@ -47,23 +93,119 @@ namespace de4dot.code.deobfuscators.DeepSea {
protected override bool checkHandlerMethodDesktopInternal(MethodDefinition handler) {
if (checkHandlerV3(handler)) {
isV3 = true;
version = ResourceVersion.V3;
return true;
}
FieldDefinition resourceFieldTmp;
MethodDefinition getDataMethodTmp;
simpleDeobfuscator.deobfuscate(handler);
if (checkHandlerV4(handler, out resourceFieldTmp, out getDataMethodTmp, out magicV4)) {
isV3 = false;
resourceField = resourceFieldTmp;
getDataMethod = getDataMethodTmp;
if ((data40 = checkHandlerV40(handler)) != null) {
version = ResourceVersion.V40;
return true;
}
var info = getHandlerArgs41(handler);
Data41 data41Tmp;
if (info != null && checkHandlerV41(info, out data41Tmp)) {
version = ResourceVersion.V41;
data41 = data41Tmp;
return true;
}
return false;
}
HandlerInfo getHandlerArgs41(MethodDefinition handler) {
var instrs = handler.Body.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode.Code != Code.Call)
continue;
var calledMethod = instr.Operand as MethodDefinition;
if (calledMethod == null)
continue;
var args = DsUtils.getArgValues(instrs, i);
if (args == null)
continue;
return new HandlerInfo(calledMethod, args);
}
return null;
}
bool checkHandlerV41(HandlerInfo info, out Data41 data41) {
data41 = new Data41();
data41.resolveHandler2 = info.handler;
data41.resourceField = getLdtokenField(info.handler);
if (data41.resourceField == null)
return false;
int magicArgIndex = getMagicArgIndex41Retail(info.handler);
if (magicArgIndex < 0) {
magicArgIndex = getMagicArgIndex41Trial(info.handler);
data41.isTrial = true;
}
var asmVer = module.Assembly.Name.Version;
if (magicArgIndex < 0 || magicArgIndex >= info.args.Count)
return false;
var val = info.args[magicArgIndex];
if (!(val is int))
return false;
if (data41.isTrial)
data41.magic = (int)val >> 3;
else
data41.magic = ((asmVer.Major << 3) | (asmVer.Minor << 2) | asmVer.Revision) - (int)val;
return true;
}
static int getMagicArgIndex41Retail(MethodDefinition method) {
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count - 3; i++) {
var add = instrs[i];
if (add.OpCode.Code != Code.Add)
continue;
var ldarg = instrs[i + 1];
if (!DotNetUtils.isLdarg(ldarg))
continue;
var sub = instrs[i + 2];
if (sub.OpCode.Code != Code.Sub)
continue;
var ldci4 = instrs[i + 3];
if (!DotNetUtils.isLdcI4(ldci4) || DotNetUtils.getLdcI4Value(ldci4) != 0xFF)
continue;
return DotNetUtils.getArgIndex(ldarg);
}
return -1;
}
static int getMagicArgIndex41Trial(MethodDefinition method) {
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count - 2; i++) {
var ldarg = instrs[i];
if (!DotNetUtils.isLdarg(ldarg))
continue;
if (!DotNetUtils.isLdcI4(instrs[i + 1]))
continue;
if (instrs[i + 2].OpCode.Code != Code.Shr)
continue;
return DotNetUtils.getArgIndex(ldarg);
}
return -1;
}
static FieldDefinition getLdtokenField(MethodDefinition method) {
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code != Code.Ldtoken)
continue;
var field = instr.Operand as FieldDefinition;
if (field == null || field.InitialValue == null || field.InitialValue.Length == 0)
continue;
return field;
}
return null;
}
static string[] handlerLocalTypes_V3 = new string[] {
"System.AppDomain",
"System.Byte[]",
@ -79,7 +221,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
return new LocalTypes(handler).all(handlerLocalTypes_V3);
}
static bool checkHandlerV4(MethodDefinition handler, out FieldDefinition resourceField, out MethodDefinition getDataMethod, out int magic) {
static Data40 checkHandlerV40(MethodDefinition handler) {
var data40 = new Data40();
var instrs = handler.Body.Instructions;
for (int i = 0; i < instrs.Count; i++) {
int index = i;
@ -105,7 +249,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
// 4.0.1.18 .. 4.0.3
}
if (field.InitialValue == null || field.InitialValue.Length == 0)
if (field == null || field.InitialValue == null || field.InitialValue.Length == 0)
continue;
var ldci4_len = instrs[index++];
@ -120,25 +264,24 @@ namespace de4dot.code.deobfuscators.DeepSea {
var ldci4_magic = instrs[index++];
if (!DotNetUtils.isLdcI4(ldci4_magic))
continue;
magic = DotNetUtils.getLdcI4Value(ldci4_magic);
data40.magic = DotNetUtils.getLdcI4Value(ldci4_magic);
var call = instrs[index++];
if (call.OpCode.Code == Code.Tail)
call = instrs[index++];
if (call.OpCode.Code != Code.Call)
continue;
if (!DotNetUtils.isMethod(call.Operand as MethodReference, "System.Reflection.Assembly", methodSig))
var resolveHandler2 = call.Operand as MethodDefinition;
if (!DotNetUtils.isMethod(resolveHandler2, "System.Reflection.Assembly", methodSig))
continue;
resourceField = field;
getDataMethod = method;
return true;
data40.resourceField = field;
data40.getDataMethod = method;
data40.resolveHandler2 = resolveHandler2;
return data40;
}
magic = 0;
resourceField = null;
getDataMethod = null;
return false;
return null;
}
static FieldDefinition getResourceField(MethodDefinition method) {
@ -157,11 +300,12 @@ namespace de4dot.code.deobfuscators.DeepSea {
if (resolveHandler == null)
return;
if (isV3) {
if (version == ResourceVersion.V3) {
simpleDeobfuscator.deobfuscate(resolveHandler);
simpleDeobfuscator.decryptStrings(resolveHandler, deob);
resource = DeobUtils.getEmbeddedResourceFromCodeStrings(module, resolveHandler);
if (resource == null) {
data30 = new Data30();
data30.resource = DeobUtils.getEmbeddedResourceFromCodeStrings(module, resolveHandler);
if (data30.resource == null) {
Log.w("Could not find resource of encrypted resources");
return;
}
@ -171,22 +315,34 @@ namespace de4dot.code.deobfuscators.DeepSea {
public bool mergeResources(out EmbeddedResource rsrc) {
rsrc = null;
if (isV3) {
if (resource == null)
switch (version) {
case ResourceVersion.V3:
if (data30.resource == null)
return false;
DeobUtils.decryptAndAddResources(module, resource.Name, () => decryptResourceV3(resource));
rsrc = resource;
}
else {
if (resourceField == null)
return false;
DeobUtils.decryptAndAddResources(module, data30.resource.Name, () => decryptResourceV3(data30.resource));
rsrc = data30.resource;
return true;
string name = string.Format("Embedded data field {0:X8} RVA {1:X8}", resourceField.MetadataToken.ToInt32(), resourceField.RVA);
DeobUtils.decryptAndAddResources(module, name, () => decryptResourceV4(resourceField.InitialValue, magicV4));
resourceField.InitialValue = new byte[1];
resourceField.FieldType = module.TypeSystem.Byte;
case ResourceVersion.V40:
return decryptResource(data40.resourceField, data40.magic);
case ResourceVersion.V41:
return decryptResource(data41.resourceField, data41.magic);
default:
return true;
}
}
bool decryptResource(FieldDefinition resourceField, int magic) {
if (resourceField == null)
return false;
string name = string.Format("Embedded data field {0:X8} RVA {1:X8}", resourceField.MetadataToken.ToInt32(), resourceField.RVA);
DeobUtils.decryptAndAddResources(module, name, () => decryptResourceV4(resourceField.InitialValue, magic));
resourceField.InitialValue = new byte[1];
resourceField.FieldType = module.TypeSystem.Byte;
return true;
}
}

View File

@ -22,6 +22,7 @@ using System.Collections.Generic;
using System.Text;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Metadata;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.DeepSea {
@ -33,7 +34,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
public enum DecrypterVersion {
Unknown,
V1_3,
V4,
V4_0,
V4_1,
}
interface IDecrypterInfo {
@ -44,6 +46,12 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
static short[] findKey(MethodDefinition initMethod, FieldDefinition keyField) {
var fields = new FieldDefinitionAndDeclaringTypeDict<bool>();
fields.add(keyField, true);
return findKey(initMethod, fields);
}
static short[] findKey(MethodDefinition initMethod, FieldDefinitionAndDeclaringTypeDict<bool> fields) {
var instrs = initMethod.Body.Instructions;
for (int i = 0; i < instrs.Count - 2; i++) {
var ldci4 = instrs[i];
@ -69,7 +77,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
var field = getStoreField(initMethod, startInitIndex, local);
if (field == null)
continue;
if (keyField == field)
if (fields.find(field))
return array;
}
@ -94,9 +102,267 @@ namespace de4dot.code.deobfuscators.DeepSea {
return null;
}
class DecrypterInfo4 : IDecrypterInfo {
static bool findMagic(MethodDefinition method, out int magic) {
int arg1, arg2;
return findMagic(method, out arg1, out arg2, out magic);
}
static bool findMagic(MethodDefinition method, out int arg1, out int arg2, out int magic) {
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count - 3; i++) {
if ((arg1 = DotNetUtils.getArgIndex(instrs[i])) < 0)
continue;
var ldci4 = instrs[i + 1];
if (!DotNetUtils.isLdcI4(ldci4))
continue;
if (instrs[i + 2].OpCode.Code != Code.Xor)
continue;
if ((arg2 = DotNetUtils.getArgIndex(instrs[i + 3])) < 0)
continue;
magic = DotNetUtils.getLdcI4Value(ldci4);
return true;
}
arg1 = arg2 = 0;
magic = 0;
return false;
}
class DecrypterInfo41 : IDecrypterInfo {
MethodDefinition cctor;
int magic;
int arg1, arg2;
FieldDefinitionAndDeclaringTypeDict<bool> fields;
ArrayInfo arrayInfo;
ushort[] encryptedData;
short[] key;
int keyShift;
bool isTrial;
class ArrayInfo {
public int sizeInElems;
public TypeReference elementType;
public FieldDefinition initField;
public FieldDefinition field;
public ArrayInfo(int sizeInElems, TypeReference elementType, FieldDefinition initField, FieldDefinition field) {
this.sizeInElems = sizeInElems;
this.elementType = elementType;
this.initField = initField;
this.field = field;
}
}
public DecrypterVersion Version {
get { return DecrypterVersion.V4_1; }
}
public MethodDefinition Method { get; private set; }
public DecrypterInfo41(MethodDefinition cctor, MethodDefinition method) {
this.cctor = cctor;
Method = method;
}
public static bool isPossibleDecrypterMethod(MethodDefinition method) {
if (!checkMethodSignature(method))
return false;
var fields = getFields(method);
if (fields == null || fields.Count != 3)
return false;
return true;
}
static bool checkMethodSignature(MethodDefinition method) {
if (method.MethodReturnType.ReturnType.EType != ElementType.String)
return false;
int count = 0;
foreach (var arg in method.Parameters) {
if (arg.ParameterType.EType == ElementType.I4)
count++;
}
return count >= 2;
}
static FieldDefinitionAndDeclaringTypeDict<bool> getFields(MethodDefinition method) {
var fields = new FieldDefinitionAndDeclaringTypeDict<bool>();
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code != Code.Ldsfld && instr.OpCode.Code != Code.Stsfld)
continue;
var field = instr.Operand as FieldDefinition;
if (field == null)
continue;
if (field.DeclaringType != method.DeclaringType)
continue;
fields.add(field, true);
}
return fields;
}
public bool initialize() {
if (!findMagic(Method, out arg1, out arg2, out magic))
return false;
fields = getFields(Method);
if (fields == null)
return false;
arrayInfo = getArrayInfo(cctor);
if (arrayInfo == null)
return false;
if (arrayInfo.initField.InitialValue.Length % 2 == 1)
return false;
encryptedData = new ushort[arrayInfo.initField.InitialValue.Length / 2];
Buffer.BlockCopy(arrayInfo.initField.InitialValue, 0, encryptedData, 0, arrayInfo.initField.InitialValue.Length);
isTrial = !DeobUtils.hasInteger(Method, 0xFFF0);
keyShift = findKeyShift(cctor);
key = findKey();
if (key == null || key.Length == 0)
return false;
return true;
}
int findKeyShift(MethodDefinition method) {
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count - 3; i++) {
int index = i;
var ldci4 = instrs[index++];
if (!DotNetUtils.isLdcI4(ldci4))
continue;
if (DotNetUtils.getLdcI4Value(ldci4) != 0xFF)
continue;
if (instrs[index++].OpCode.Code != Code.And)
continue;
if (instrs[index++].OpCode.Code != Code.Dup)
continue;
var ldci4_2 = instrs[index++];
if (!DotNetUtils.isLdcI4(ldci4_2))
continue;
if (findNextFieldUse(method, index) < 0)
continue;
return DotNetUtils.getLdcI4Value(ldci4_2);
}
return -1;
}
int findNextFieldUse(MethodDefinition method, int index) {
var instrs = method.Body.Instructions;
for (int i = index; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode.Code != Code.Ldsfld && instr.OpCode.Code != Code.Stsfld)
continue;
var field = instr.Operand as FieldReference;
if (!fields.find(field))
return -1;
return i;
}
return -1;
}
ArrayInfo getArrayInfo(MethodDefinition method) {
var instructions = method.Body.Instructions;
for (int i = 0; i < instructions.Count; i++) {
var ldci4_arraySizeInBytes = instructions[i];
if (!DotNetUtils.isLdcI4(ldci4_arraySizeInBytes))
continue;
i++;
var instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Newarr, OpCodes.Dup, OpCodes.Ldtoken, OpCodes.Call, OpCodes.Stsfld);
if (instrs == null)
continue;
int sizeInBytes = DotNetUtils.getLdcI4Value(ldci4_arraySizeInBytes);
var elementType = instrs[0].Operand as TypeReference;
var initField = instrs[2].Operand as FieldDefinition;
var field = instrs[4].Operand as FieldDefinition;
if (elementType == null)
continue;
if (initField == null || initField.InitialValue == null || initField.InitialValue.Length == 0)
continue;
if (!fields.find(field))
continue;
return new ArrayInfo(sizeInBytes, elementType, initField, field);
}
return null;
}
short[] findKey() {
if (cctor.Module.Assembly == null)
return null;
var pkt = cctor.Module.Assembly.Name.PublicKeyToken;
if (pkt != null && pkt.Length > 0)
return getPublicKeyTokenKey(pkt);
return findKey(cctor);
}
short[] findKey(MethodDefinition initMethod) {
return StringDecrypter.findKey(initMethod, fields);
}
short[] getPublicKeyTokenKey(byte[] publicKeyToken) {
if (keyShift < 0)
throw new ApplicationException("Could not find shift value");
var key = new short[publicKeyToken.Length];
for (int i = 0; i < publicKeyToken.Length; i++) {
int b = publicKeyToken[i];
key[i] = (short)((b << keyShift) ^ b);
}
return key;
}
public string decrypt(object[] args) {
if (isTrial)
return decryptTrial((int)args[arg1], (int)args[arg2]);
return decryptRetail((int)args[arg1], (int)args[arg2]);
}
string decryptTrial(int magic2, int magic3) {
int offset = magic ^ magic2 ^ magic3;
var keyChar = encryptedData[offset + 1];
int cachedIndex = encryptedData[offset] ^ keyChar;
int numChars = ((keyChar ^ encryptedData[offset + 2]) << 16) + (keyChar ^ encryptedData[offset + 3]);
offset += 4;
var sb = new StringBuilder(numChars);
for (int i = 0; i < numChars; i++)
sb.Append((char)(keyChar ^ encryptedData[offset + i] ^ key[(offset + i) % key.Length]));
return sb.ToString();
}
string decryptRetail(int magic2, int magic3) {
int offset = magic ^ magic2 ^ magic3;
var keyChar = encryptedData[offset + 2];
int cachedIndex = encryptedData[offset + 1] ^ keyChar;
int flags = encryptedData[offset] ^ keyChar;
int numChars = (flags >> 1) & ~7 | (flags & 7);
if ((flags & 8) != 0) {
numChars <<= 15;
numChars |= encryptedData[offset + 3] ^ keyChar;
offset++;
}
offset += 3;
var sb = new StringBuilder(numChars);
for (int i = 0; i < numChars; i++)
sb.Append((char)(keyChar ^ encryptedData[offset + numChars - i - 1] ^ key[(i + 1 + offset) % key.Length]));
return sb.ToString();
}
public void cleanup() {
arrayInfo.initField.InitialValue = new byte[1];
arrayInfo.initField.FieldType = arrayInfo.initField.Module.TypeSystem.Byte;
}
}
class DecrypterInfo40 : IDecrypterInfo {
MethodDefinition cctor;
public MethodDefinition Method { get; set; }
int magic;
FieldDefinition cachedStringsField;
FieldDefinition keyField;
@ -105,15 +371,23 @@ namespace de4dot.code.deobfuscators.DeepSea {
short[] key;
ushort[] encryptedData;
public MethodDefinition Method { get; private set; }
public DecrypterVersion Version {
get { return DecrypterVersion.V4; }
get { return DecrypterVersion.V4_0; }
}
public DecrypterInfo4(MethodDefinition cctor, MethodDefinition method) {
public DecrypterInfo40(MethodDefinition cctor, MethodDefinition method) {
this.cctor = cctor;
this.Method = method;
}
public static bool isPossibleDecrypterMethod(MethodDefinition method) {
if (!checkFields(method.DeclaringType.Fields))
return false;
return DotNetUtils.isMethod(method, "System.String", "(System.Int32,System.Int32)");
}
public bool initialize() {
if (!findMagic(Method, out magic))
return false;
@ -140,24 +414,6 @@ namespace de4dot.code.deobfuscators.DeepSea {
return true;
}
static bool findMagic(MethodDefinition method, out int magic) {
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count - 2; i++) {
var ldarg = instrs[i];
if (DotNetUtils.getArgIndex(ldarg) < 0)
continue;
var ldci4 = instrs[i + 1];
if (!DotNetUtils.isLdcI4(ldci4))
continue;
if (instrs[i + 2].OpCode.Code != Code.Xor)
continue;
magic = DotNetUtils.getLdcI4Value(ldci4);
return true;
}
magic = 0;
return false;
}
List<FieldDefinition> findFields() {
var charArrayFields = new List<FieldDefinition>();
@ -263,20 +519,27 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
}
class DecrypterInfo3 : IDecrypterInfo {
class DecrypterInfo13 : IDecrypterInfo {
MethodDefinition cctor;
public MethodDefinition Method { get; set; }
FieldDefinition cachedStringsField;
FieldDefinition keyField;
int magic;
string[] encryptedStrings;
short[] key;
public MethodDefinition Method { get; private set; }
public DecrypterVersion Version {
get { return DecrypterVersion.V1_3; }
}
public DecrypterInfo3(MethodDefinition cctor, MethodDefinition method) {
public static bool isPossibleDecrypterMethod(MethodDefinition method) {
if (!checkFields(method.DeclaringType.Fields))
return false;
return DotNetUtils.isMethod(method, "System.String", "(System.Int32)");
}
public DecrypterInfo13(MethodDefinition cctor, MethodDefinition method) {
this.cctor = cctor;
this.Method = method;
}
@ -442,27 +705,31 @@ namespace de4dot.code.deobfuscators.DeepSea {
bool hasPublicKeyToken = module.Assembly.Name.PublicKeyToken != null && module.Assembly.Name.PublicKeyToken.Length != 0;
foreach (var type in module.GetTypes()) {
if (!checkFields(type.Fields))
continue;
var cctor = DotNetUtils.getMethod(type, ".cctor");
if (cctor == null)
continue;
if (!hasPublicKeyToken)
simpleDeobfuscator.deobfuscate(cctor);
bool deobfuscatedCctor = false;
foreach (var method in type.Methods) {
if (method.Body == null)
if (!method.IsStatic || method.Body == null)
continue;
IDecrypterInfo info = null;
if (DotNetUtils.isMethod(method, "System.String", "(System.Int32)")) {
if (DecrypterInfo13.isPossibleDecrypterMethod(method)) {
deobfuscateCctor(simpleDeobfuscator, cctor, ref deobfuscatedCctor, hasPublicKeyToken);
simpleDeobfuscator.deobfuscate(method);
info = getInfoV3(cctor, method);
info = getInfoV13(cctor, method);
}
else if (DotNetUtils.isMethod(method, "System.String", "(System.Int32,System.Int32)")) {
else if (DecrypterInfo40.isPossibleDecrypterMethod(method)) {
deobfuscateCctor(simpleDeobfuscator, cctor, ref deobfuscatedCctor, hasPublicKeyToken);
simpleDeobfuscator.deobfuscate(method);
info = getInfoV4(cctor, method);
info = getInfoV40(cctor, method);
}
else if (DecrypterInfo41.isPossibleDecrypterMethod(method)) {
deobfuscateCctor(simpleDeobfuscator, cctor, ref deobfuscatedCctor, hasPublicKeyToken);
simpleDeobfuscator.deobfuscate(method);
info = getInfoV41(cctor, method);
}
if (info == null)
@ -473,6 +740,13 @@ namespace de4dot.code.deobfuscators.DeepSea {
}
}
static void deobfuscateCctor(ISimpleDeobfuscator simpleDeobfuscator, MethodDefinition cctor, ref bool deobfuscatedCctor, bool hasPublicKeyToken) {
if (deobfuscatedCctor || hasPublicKeyToken)
return;
simpleDeobfuscator.deobfuscate(cctor);
deobfuscatedCctor = true;
}
static bool checkFields(IEnumerable<FieldDefinition> fields) {
bool foundCharAry = false, foundStringAry = false;
foreach (var field in fields) {
@ -490,15 +764,22 @@ namespace de4dot.code.deobfuscators.DeepSea {
return foundCharAry && foundStringAry;
}
DecrypterInfo3 getInfoV3(MethodDefinition cctor, MethodDefinition method) {
var info = new DecrypterInfo3(cctor, method);
DecrypterInfo13 getInfoV13(MethodDefinition cctor, MethodDefinition method) {
var info = new DecrypterInfo13(cctor, method);
if (!info.initialize())
return null;
return info;
}
DecrypterInfo4 getInfoV4(MethodDefinition cctor, MethodDefinition method) {
var info = new DecrypterInfo4(cctor, method);
DecrypterInfo40 getInfoV40(MethodDefinition cctor, MethodDefinition method) {
var info = new DecrypterInfo40(cctor, method);
if (!info.initialize())
return null;
return info;
}
DecrypterInfo41 getInfoV41(MethodDefinition cctor, MethodDefinition method) {
var info = new DecrypterInfo41(cctor, method);
if (!info.initialize())
return null;
return info;

View File

@ -95,11 +95,12 @@ namespace de4dot.code.deobfuscators {
get { return Operations.DecryptStrings != OpDecryptString.None && staticStringInliner.InlinedAllCalls; }
}
public virtual IMethodCallInliner MethodCallInliner {
public virtual IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
get {
var list = new List<IBlocksDeobfuscator>();
if (CanInlineMethods)
return new MethodCallInliner(false);
return new NoMethodInliner();
list.Add(new MethodCallInliner(false));
return list;
}
}
@ -744,6 +745,10 @@ namespace de4dot.code.deobfuscators {
return name != null && checkValidName(name);
}
public virtual bool isValidResourceKeyName(string name) {
return name != null && checkValidName(name);
}
public virtual void OnBeforeAddingResources(MetadataBuilder builder) {
}

View File

@ -1,220 +0,0 @@
/*
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.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Metadata;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Eazfuscator_NET {
class ConstantsReader {
IList<Instruction> instructions;
IList<VariableDefinition> locals;
Dictionary<VariableDefinition, int> localsValues = new Dictionary<VariableDefinition, int>();
public ConstantsReader(MethodDefinition method) {
instructions = method.Body.Instructions;
locals = method.Body.Variables;
initialize();
}
void initialize() {
findConstants();
}
void findConstants() {
for (int index = 0; index < instructions.Count; ) {
int value;
if (!getInt32(ref index, out value))
break;
var stloc = instructions[index];
if (!DotNetUtils.isStloc(stloc))
break;
var local = DotNetUtils.getLocalVar(locals, stloc);
if (local == null || local.VariableType.EType != ElementType.I4)
break;
localsValues[local] = value;
index++;
}
if (localsValues.Count != 2)
localsValues.Clear();
}
public bool getNextInt32(ref int index, out int val) {
for (; index < instructions.Count; index++) {
var instr = instructions[index];
if (!isLoadConstant(instr))
continue;
return getInt32(ref index, out val);
}
val = 0;
return false;
}
public bool isLoadConstant(Instruction instr) {
if (DotNetUtils.isLdcI4(instr))
return true;
if (!DotNetUtils.isLdloc(instr))
return false;
int tmp;
return getLocalConstant(instr, out tmp);
}
public bool getInt16(ref int index, out short val) {
int tmp;
if (!getInt32(ref index, out tmp)) {
val = 0;
return false;
}
val = (short)tmp;
return true;
}
public bool getInt32(ref int index, out int val) {
val = 0;
if (index >= instructions.Count)
return false;
var stack = new Stack<int>();
int op1;
for (; index < instructions.Count; index++) {
var instr = instructions[index];
switch (instr.OpCode.Code) {
case Code.Conv_I1:
if (stack.Count < 1)
goto done;
stack.Push((sbyte)stack.Pop());
break;
case Code.Conv_U1:
if (stack.Count < 1)
goto done;
stack.Push((byte)stack.Pop());
break;
case Code.Conv_I2:
if (stack.Count < 1)
goto done;
stack.Push((short)stack.Pop());
break;
case Code.Conv_U2:
if (stack.Count < 1)
goto done;
stack.Push((ushort)stack.Pop());
break;
case Code.Conv_I4:
case Code.Conv_U4:
break;
case Code.Not:
stack.Push(~stack.Pop());
break;
case Code.Neg:
stack.Push(-stack.Pop());
break;
case Code.Ldloc:
case Code.Ldloc_S:
case Code.Ldloc_0:
case Code.Ldloc_1:
case Code.Ldloc_2:
case Code.Ldloc_3:
if (!getLocalConstant(instr, out op1))
goto done;
stack.Push(op1);
break;
case Code.Ldc_I4:
case Code.Ldc_I4_S:
case Code.Ldc_I4_0:
case Code.Ldc_I4_1:
case Code.Ldc_I4_2:
case Code.Ldc_I4_3:
case Code.Ldc_I4_4:
case Code.Ldc_I4_5:
case Code.Ldc_I4_6:
case Code.Ldc_I4_7:
case Code.Ldc_I4_8:
case Code.Ldc_I4_M1:
stack.Push(DotNetUtils.getLdcI4Value(instr));
break;
case Code.Add:
if (stack.Count < 2)
goto done;
stack.Push(stack.Pop() + stack.Pop());
break;
case Code.Sub:
if (stack.Count < 2)
goto done;
stack.Push(-(stack.Pop() - stack.Pop()));
break;
case Code.Xor:
if (stack.Count < 2)
goto done;
stack.Push(stack.Pop() ^ stack.Pop());
break;
case Code.Or:
if (stack.Count < 2)
goto done;
stack.Push(stack.Pop() | stack.Pop());
break;
case Code.And:
if (stack.Count < 2)
goto done;
stack.Push(stack.Pop() & stack.Pop());
break;
default:
goto done;
}
}
done:
if (stack.Count == 0)
return false;
while (stack.Count > 1)
stack.Pop();
val = stack.Pop();
return true;
}
bool getLocalConstant(Instruction instr, out int value) {
value = 0;
var local = DotNetUtils.getLocalVar(locals, instr);
if (local == null)
return false;
if (local.VariableType.EType != ElementType.I4)
return false;
return localsValues.TryGetValue(local, out value);
}
}
}

View File

@ -283,7 +283,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
int index = 0;
var instrs = method.Body.Instructions;
var constantsReader = new ConstantsReader(method);
var constantsReader = new EfConstantsReader(method);
while (true) {
int val;
if (!constantsReader.getNextInt32(ref index, out val))

View File

@ -0,0 +1,54 @@
/*
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 Mono.Cecil;
using Mono.Cecil.Metadata;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Eazfuscator_NET {
class EfConstantsReader : ConstantsReader {
public EfConstantsReader(MethodDefinition method)
: base(method) {
initialize();
}
void initialize() {
findConstants();
}
void findConstants() {
for (int index = 0; index < instructions.Count; ) {
int value;
if (!getInt32(ref index, out value))
break;
var stloc = instructions[index];
if (!DotNetUtils.isStloc(stloc))
break;
var local = DotNetUtils.getLocalVar(locals, stloc);
if (local == null || local.VariableType.EType != ElementType.I4)
break;
localsValues[local] = value;
index++;
}
if (localsValues.Count != 2)
localsValues.Clear();
}
}
}

View File

@ -44,7 +44,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
BinaryReader reader;
DecrypterType decrypterType;
StreamHelperType streamHelperType;
ConstantsReader stringMethodConsts;
EfConstantsReader stringMethodConsts;
bool isV32OrLater;
class StreamHelperType {
@ -219,7 +219,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
bool findConstants(ISimpleDeobfuscator simpleDeobfuscator) {
simpleDeobfuscator.deobfuscate(stringMethod);
stringMethodConsts = new ConstantsReader(stringMethod);
stringMethodConsts = new EfConstantsReader(stringMethod);
if (!findResource(stringMethod))
return false;
@ -658,7 +658,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
return findIntsCctor2(cctor);
int tmp1, tmp2, tmp3 = 0;
var constantsReader = new ConstantsReader(cctor);
var constantsReader = new EfConstantsReader(cctor);
if (!constantsReader.getNextInt32(ref index, out tmp1))
return false;
if (tmp1 == 0 && !constantsReader.getNextInt32(ref index, out tmp1))
@ -684,7 +684,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
bool findIntsCctor2(MethodDefinition cctor) {
int index = 0;
var instrs = cctor.Body.Instructions;
var constantsReader = new ConstantsReader(cctor);
var constantsReader = new EfConstantsReader(cctor);
while (index >= 0) {
int val;
if (!constantsReader.getNextInt32(ref index, out val))

View File

@ -50,6 +50,7 @@ namespace de4dot.code.deobfuscators {
[Flags]
public enum RenamingOptions {
RemoveNamespaceIfOneType = 1,
RenameResourceKeys = 2,
}
public interface IDeobfuscator : INameChecker {
@ -61,7 +62,7 @@ namespace de4dot.code.deobfuscators {
StringFeatures StringFeatures { get; }
RenamingOptions RenamingOptions { get; }
DecrypterType DefaultDecrypterType { get; }
IMethodCallInliner MethodCallInliner { get; }
IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators { get; }
// This is non-null only in detect() and deobfuscateBegin().
IDeobfuscatedFile DeobfuscatedFile { get; set; }

View File

@ -100,11 +100,12 @@ namespace de4dot.code.deobfuscators.Spices_Net {
get { return startedDeobfuscating ? options.InlineMethods : true; }
}
public override IMethodCallInliner MethodCallInliner {
public override IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
get {
var list = new List<IBlocksDeobfuscator>();
if (CanInlineMethods)
return methodCallInliner;
return new NoMethodInliner();
list.Add(methodCallInliner);
return list;
}
}

View File

@ -218,6 +218,10 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 {
return name != null && checkValidName(name, isRandomNameMembers);
}
public override bool isValidResourceKeyName(string name) {
return name != null && checkValidName(name, isRandomNameMembers);
}
protected override int detectInternal() {
int val = 0;

View File

@ -222,6 +222,10 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
return name != null && checkValidName(name, isRandomNameMembers);
}
public override bool isValidResourceKeyName(string name) {
return name != null && checkValidName(name, isRandomNameMembers);
}
protected override int detectInternal() {
int val = 0;

View File

@ -27,5 +27,6 @@ namespace de4dot.code.renamer {
bool isValidFieldName(string name);
bool isValidGenericParamName(string name);
bool isValidMethodArgName(string name);
bool isValidResourceKeyName(string name);
}
}

View File

@ -78,6 +78,7 @@ namespace de4dot.code.renamer {
Log.n("Renaming all obfuscated symbols");
modules.initialize();
renameResourceKeys();
var groups = modules.initializeVirtualMembers();
memberInfos.initialize(modules);
renameTypeDefinitions();
@ -92,6 +93,14 @@ namespace de4dot.code.renamer {
modules.cleanUp();
}
void renameResourceKeys() {
foreach (var module in modules.TheModules) {
if (!module.ObfuscatedFile.RenameResourceKeys)
continue;
new ResourceKeysRenamer(module.ModuleDefinition, module.ObfuscatedFile.NameChecker).rename();
}
}
void removeUselessOverrides(MethodNameGroups groups) {
foreach (var group in groups.getAllGroups()) {
foreach (var method in group.Methods) {
@ -396,8 +405,60 @@ namespace de4dot.code.renamer {
var allGroups = groups.getAllGroups();
restoreVirtualProperties(allGroups);
restorePropertiesFromNames(allGroups);
resetVirtualPropertyNames(allGroups);
restoreVirtualEvents(allGroups);
restoreEventsFromNames(allGroups);
resetVirtualEventNames(allGroups);
}
void resetVirtualPropertyNames(IEnumerable<MethodNameGroup> allGroups) {
if (!this.RenameProperties)
return;
foreach (var group in allGroups) {
PropertyDef prop = null;
foreach (var method in group.Methods) {
if (method.Property == null)
continue;
if (method.Owner.HasModule)
continue;
prop = method.Property;
break;
}
if (prop == null)
continue;
foreach (var method in group.Methods) {
if (!method.Owner.HasModule)
continue;
if (method.Property == null)
continue;
memberInfos.prop(method.Property).rename(prop.PropertyDefinition.Name);
}
}
}
void resetVirtualEventNames(IEnumerable<MethodNameGroup> allGroups) {
if (!this.RenameEvents)
return;
foreach (var group in allGroups) {
EventDef evt = null;
foreach (var method in group.Methods) {
if (method.Event == null)
continue;
if (method.Owner.HasModule)
continue;
evt = method.Event;
break;
}
if (evt == null)
continue;
foreach (var method in group.Methods) {
if (!method.Owner.HasModule)
continue;
if (method.Event == null)
continue;
memberInfos.evt(method.Event).rename(evt.EventDefinition.Name);
}
}
}
void restoreVirtualProperties(IEnumerable<MethodNameGroup> allGroups) {

View File

@ -0,0 +1,257 @@
/*
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 System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
using de4dot.code.resources;
namespace de4dot.code.renamer {
class ResourceKeysRenamer {
const int RESOURCE_KEY_MAX_LEN = 50;
const string DEFAULT_KEY_NAME = "Key";
ModuleDefinition module;
INameChecker nameChecker;
Dictionary<string, bool> newNames = new Dictionary<string, bool>();
public ResourceKeysRenamer(ModuleDefinition module, INameChecker nameChecker) {
this.module = module;
this.nameChecker = nameChecker;
}
public void rename() {
Log.v("Renaming resource keys ({0})", module);
Log.indent();
foreach (var type in module.GetTypes()) {
string resourceName = getResourceName(type);
if (resourceName == null)
continue;
var resource = getResource(resourceName);
if (resource == null) {
Log.w("Could not find resource {0}", Utils.removeNewlines(resourceName));
continue;
}
Log.v("Resource: {0}", Utils.toCsharpString(resource.Name));
Log.indent();
rename(type, resource);
Log.deIndent();
}
Log.deIndent();
}
EmbeddedResource getResource(string resourceName) {
var resource = DotNetUtils.getResource(module, resourceName + ".resources") as EmbeddedResource;
if (resource != null)
return resource;
string name = "";
var pieces = resourceName.Split('.');
Array.Reverse(pieces);
foreach (var piece in pieces) {
name = piece + name;
resource = DotNetUtils.getResource(module, name + ".resources") as EmbeddedResource;
if (resource != null)
return resource;
}
return null;
}
static string getResourceName(TypeDefinition type) {
foreach (var method in type.Methods) {
if (method.Body == null)
continue;
var instrs = method.Body.Instructions;
string resourceName = null;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode.Code == Code.Ldstr) {
resourceName = instr.Operand as string;
continue;
}
if (instr.OpCode.Code == Code.Newobj) {
var ctor = instr.Operand as MethodReference;
if (ctor.FullName != "System.Void System.Resources.ResourceManager::.ctor(System.String,System.Reflection.Assembly)")
continue;
if (resourceName == null) {
Log.w("Could not find resource name");
continue;
}
return resourceName;
}
}
}
return null;
}
class RenameInfo {
public readonly ResourceElement element;
public string newName;
public bool foundInCode;
public RenameInfo(ResourceElement element, string newName) {
this.element = element;
this.newName = newName;
this.foundInCode = false;
}
public override string ToString() {
return string.Format("{0} => {1}", element, newName);
}
}
void rename(TypeDefinition type, EmbeddedResource resource) {
newNames.Clear();
var resourceSet = ResourceReader.read(module, resource.GetResourceStream());
var renamed = new List<RenameInfo>();
foreach (var elem in resourceSet.ResourceElements) {
if (nameChecker.isValidResourceKeyName(elem.Name)) {
newNames.Add(elem.Name, true);
continue;
}
renamed.Add(new RenameInfo(elem, getNewName(elem)));
}
if (renamed.Count == 0)
return;
rename(type, renamed);
var outStream = new MemoryStream();
ResourceWriter.write(module, outStream, resourceSet);
outStream.Position = 0;
var newResource = new EmbeddedResource(resource.Name, resource.Attributes, outStream);
int resourceIndex = module.Resources.IndexOf(resource);
if (resourceIndex < 0)
throw new ApplicationException("Could not find index of resource");
module.Resources[resourceIndex] = newResource;
}
void rename(TypeDefinition type, List<RenameInfo> renamed) {
var nameToInfo = new Dictionary<string, RenameInfo>(StringComparer.Ordinal);
foreach (var info in renamed)
nameToInfo[info.element.Name] = info;
foreach (var method in type.Methods) {
if (method.Body == null)
continue;
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var call = instrs[i];
if (call.OpCode.Code != Code.Call && call.OpCode.Code != Code.Callvirt)
continue;
var calledMethod = call.Operand as MethodReference;
if (calledMethod == null)
continue;
int ldstrIndex;
switch (calledMethod.FullName) {
case "System.String System.Resources.ResourceManager::GetString(System.String,System.Globalization.CultureInfo)":
case "System.IO.UnmanagedMemoryStream System.Resources.ResourceManager::GetStream(System.String,System.Globalization.CultureInfo)":
case "System.Object System.Resources.ResourceManager::GetObject(System.String,System.Globalization.CultureInfo)":
ldstrIndex = i - 2;
break;
case "System.String System.Resources.ResourceManager::GetString(System.String)":
case "System.IO.UnmanagedMemoryStream System.Resources.ResourceManager::GetStream(System.String)":
case "System.Object System.Resources.ResourceManager::GetObject(System.String)":
ldstrIndex = i - 1;
break;
default:
continue;
}
Instruction ldstr = null;
string name = null;
if (ldstrIndex >= 0)
ldstr = instrs[ldstrIndex];
if (ldstr == null || (name = ldstr.Operand as string) == null) {
Log.w("Could not find string argument to method {0}", calledMethod);
continue;
}
RenameInfo info;
if (!nameToInfo.TryGetValue(name, out info))
continue; // should not be renamed
ldstr.Operand = info.newName;
Log.v("Renamed resource key {0} => {1}", Utils.toCsharpString(info.element.Name), Utils.toCsharpString(info.newName));
info.element.Name = info.newName;
info.foundInCode = true;
}
}
foreach (var info in renamed) {
if (!info.foundInCode)
Log.w("Could not find resource key {0} in code", Utils.removeNewlines(info.element.Name));
}
}
string getNewName(ResourceElement elem) {
if (elem.ResourceData.Code != ResourceTypeCode.String)
return createDefaultName();
var stringData = (BuiltInResourceData)elem.ResourceData;
var name = createPrefixFromStringData((string)stringData.Data);
return createName(counter => counter == 0 ? name : string.Format("{0}_{1}", name, counter));
}
string createPrefixFromStringData(string data) {
var sb = new StringBuilder();
data = data.Substring(0, Math.Min(data.Length, 100));
data = Regex.Replace(data, "[`'\"]", "");
data = Regex.Replace(data, @"[^\w]+", " ");
foreach (var piece in data.Split(' ')) {
if (piece.Length == 0)
continue;
var piece2 = piece.Substring(0, 1).ToUpperInvariant() + piece.Substring(1).ToLowerInvariant();
int maxLen = RESOURCE_KEY_MAX_LEN - sb.Length;
if (maxLen <= 0)
break;
if (piece2.Length > maxLen)
piece2 = piece2.Substring(0, maxLen);
sb.Append(piece2);
}
if (sb.Length <= 3)
return createDefaultName();
return sb.ToString();
}
string createDefaultName() {
return createName(counter => string.Format("{0}{1}", DEFAULT_KEY_NAME, counter));
}
string createName(Func<int, string> create) {
for (int counter = 0; ; counter++) {
string newName = create(counter);
if (!newNames.ContainsKey(newName)) {
newNames[newName] = true;
return newName;
}
}
}
}
}

View File

@ -89,7 +89,7 @@ namespace de4dot.code.renamer {
public string getNewPropertyName(PropertyDefinition propertyDefinition) {
var propType = propertyDefinition.PropertyType;
string newName;
if (propType is GenericParameter)
if (isGeneric(propType))
newName = existingPropertyNames.getName(propertyDefinition.Name, genericPropertyNameCreator);
else
newName = existingPropertyNames.getName(propertyDefinition.Name, () => propertyNameCreator.create(propType));
@ -97,6 +97,17 @@ namespace de4dot.code.renamer {
return newName;
}
static bool isGeneric(TypeReference type) {
while (true) {
if (type is GenericParameter)
return true;
var ts = type as TypeSpecification;
if (ts == null)
return false;
type = ts.ElementType;
}
}
public string getNewEventName(EventDefinition eventDefinition) {
string newName = eventNameCreator.create();
addEventName(newName);

View File

@ -26,6 +26,10 @@ namespace de4dot.code.resources {
readonly ResourceTypeCode code;
readonly object data;
public object Data {
get { return data; }
}
public ResourceTypeCode Code {
get { return code; }
}

View File

@ -0,0 +1,187 @@
/*
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 System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Mono.Cecil;
namespace de4dot.code.resources {
[Serializable]
class ResourceReaderException : Exception {
public ResourceReaderException(string msg)
: base(msg) {
}
}
class ResourceReader {
ModuleDefinition module;
BinaryReader reader;
ResourceDataCreator resourceDataCreator;
ResourceReader(ModuleDefinition module, Stream stream) {
this.module = module;
this.reader = new BinaryReader(stream);
this.resourceDataCreator = new ResourceDataCreator(module);
}
public static ResourceElementSet read(ModuleDefinition module, Stream stream) {
return new ResourceReader(module, stream).read();
}
ResourceElementSet read() {
ResourceElementSet resources = new ResourceElementSet();
uint sig = reader.ReadUInt32();
if (sig != 0xBEEFCACE)
throw new ResourceReaderException(string.Format("Invalid resource sig: {0:X8}", sig));
if (!checkReaders())
throw new ResourceReaderException("Invalid resource reader");
int version = reader.ReadInt32();
if (version != 2)
throw new ResourceReaderException(string.Format("Invalid resource version: {0}", version));
int numResources = reader.ReadInt32();
if (numResources < 0)
throw new ResourceReaderException(string.Format("Invalid number of resources: {0}", numResources));
int numUserTypes = reader.ReadInt32();
if (numUserTypes < 0)
throw new ResourceReaderException(string.Format("Invalid number of user types: {0}", numUserTypes));
var userTypes = new List<UserResourceType>();
for (int i = 0; i < numUserTypes; i++)
userTypes.Add(new UserResourceType(reader.ReadString(), ResourceTypeCode.UserTypes + i));
reader.BaseStream.Position = (reader.BaseStream.Position + 7) & ~7;
var hashes = new int[numResources];
for (int i = 0; i < numResources; i++)
hashes[i] = reader.ReadInt32();
var offsets = new int[numResources];
for (int i = 0; i < numResources; i++)
offsets[i] = reader.ReadInt32();
long baseOffset = reader.BaseStream.Position;
long dataBaseOffset = reader.ReadInt32();
long nameBaseOffset = reader.BaseStream.Position;
long end = reader.BaseStream.Length;
var infos = new List<ResourceInfo>(numResources);
var nameReader = new BinaryReader(reader.BaseStream, Encoding.Unicode);
for (int i = 0; i < numResources; i++) {
nameReader.BaseStream.Position = nameBaseOffset + offsets[i];
var name = nameReader.ReadString();
long offset = dataBaseOffset + nameReader.ReadInt32();
infos.Add(new ResourceInfo(name, offset));
}
infos.Sort(sortResourceInfo);
for (int i = 0; i < infos.Count; i++) {
var info = infos[i];
var element = new ResourceElement();
element.Name = info.name;
reader.BaseStream.Position = info.offset;
long nextDataOffset = i == infos.Count - 1 ? end : infos[i + 1].offset;
int size = (int)(nextDataOffset - info.offset);
element.ResourceData = readResourceData(userTypes, size);
resources.add(element);
}
return resources;
}
static int sortResourceInfo(ResourceInfo a, ResourceInfo b) {
return Utils.compareInt32((int)a.offset, (int)b.offset);
}
class ResourceInfo {
public string name;
public long offset;
public ResourceInfo(string name, long offset) {
this.name = name;
this.offset = offset;
}
public override string ToString() {
return string.Format("{0:X8} - {1}", offset, name);
}
}
IResourceData readResourceData(List<UserResourceType> userTypes, int size) {
uint code = readUInt32(reader);
switch ((ResourceTypeCode)code) {
case ResourceTypeCode.Null: return resourceDataCreator.createNull();
case ResourceTypeCode.String: return resourceDataCreator.create(reader.ReadString());
case ResourceTypeCode.Boolean: return resourceDataCreator.create(reader.ReadBoolean());
case ResourceTypeCode.Char: return resourceDataCreator.create((char)reader.ReadUInt16());
case ResourceTypeCode.Byte: return resourceDataCreator.create(reader.ReadByte());
case ResourceTypeCode.SByte: return resourceDataCreator.create(reader.ReadSByte());
case ResourceTypeCode.Int16: return resourceDataCreator.create(reader.ReadInt16());
case ResourceTypeCode.UInt16: return resourceDataCreator.create(reader.ReadUInt16());
case ResourceTypeCode.Int32: return resourceDataCreator.create(reader.ReadInt32());
case ResourceTypeCode.UInt32: return resourceDataCreator.create(reader.ReadUInt32());
case ResourceTypeCode.Int64: return resourceDataCreator.create(reader.ReadInt64());
case ResourceTypeCode.UInt64: return resourceDataCreator.create(reader.ReadUInt64());
case ResourceTypeCode.Single: return resourceDataCreator.create(reader.ReadSingle());
case ResourceTypeCode.Double: return resourceDataCreator.create(reader.ReadDouble());
case ResourceTypeCode.Decimal: return resourceDataCreator.create(reader.ReadDecimal());
case ResourceTypeCode.DateTime: return resourceDataCreator.create(new DateTime(reader.ReadInt64()));
case ResourceTypeCode.TimeSpan: return resourceDataCreator.create(new TimeSpan(reader.ReadInt64()));
case ResourceTypeCode.ByteArray: return resourceDataCreator.create(reader.ReadBytes(reader.ReadInt32()));
default:
int userTypeIndex = (int)(code - (uint)ResourceTypeCode.UserTypes);
if (userTypeIndex < 0 || userTypeIndex >= userTypes.Count)
throw new ResourceReaderException(string.Format("Invalid resource data code: {0}", code));
return resourceDataCreator.createSerialized(reader.ReadBytes(size));
}
}
static uint readUInt32(BinaryReader reader) {
uint val = 0;
for (int i = 0; i < 5; i++) {
byte b = reader.ReadByte();
val |= b;
if ((b & 0x80) == 0)
return val;
}
throw new ResourceReaderException("Invalid encoded int32");
}
bool checkReaders() {
bool validReader = false;
int numReaders = reader.ReadInt32();
if (numReaders < 0)
throw new ResourceReaderException(string.Format("Invalid number of readers: {0}", numReaders));
int readersSize = reader.ReadInt32();
if (readersSize < 0)
throw new ResourceReaderException(string.Format("Invalid readers size: {0:X8}", readersSize));
for (int i = 0; i < numReaders; i++) {
var resourceReaderFullName = reader.ReadString();
var resourceSetFullName = reader.ReadString();
if (Regex.IsMatch(resourceReaderFullName, @"^System\.Resources\.ResourceReader,\s*mscorlib,"))
validReader = true;
}
return validReader;
}
}
}

View File

@ -79,9 +79,18 @@ namespace de4dot.cui {
exitCode = 1;
}
catch (Exception ex) {
printStackTrace(ex);
Log.e("\nTry the latest version before reporting this problem!");
Log.e("Send bug reports to de4dot@gmail.com or go to https://github.com/0xd4d/de4dot/issues");
if (printFullStackTrace()) {
printStackTrace(ex);
Log.e("\nTry the latest version before reporting this problem!");
Log.e("Send bug reports to de4dot@gmail.com or go to https://github.com/0xd4d/de4dot/issues");
}
else {
Log.e("\n\n");
Log.e("Hmmmm... something didn't work. Try the latest version. ({0})", ex.GetType());
Log.e("If it's a supported obfuscator, it could be a bug or a new obfuscator version.");
Log.e("If it's an unsupported obfuscator, make sure the methods are decrypted!");
Log.e("Send bug reports to de4dot@gmail.com or go to https://github.com/0xd4d/de4dot/issues");
}
exitCode = 1;
}
@ -97,11 +106,30 @@ namespace de4dot.cui {
return exitCode;
}
static bool printFullStackTrace() {
if (Log.isAtLeast(Log.LogLevel.verbose))
return true;
if (hasEnv("STACKTRACE"))
return true;
return false;
}
static bool hasEnv(string name) {
foreach (var tmp in Environment.GetEnvironmentVariables().Keys) {
var env = tmp as string;
if (env == null)
continue;
if (string.Equals(env, name, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
static bool isN00bUser() {
var env = Environment.GetEnvironmentVariables();
if (env["VisualStudioDir"] != null)
if (hasEnv("VisualStudioDir"))
return false;
return env["windir"] != null && env["PROMPT"] == null;
return hasEnv("windir") && !hasEnv("PROMPT");
}
public static void printStackTrace(Exception ex, Log.LogLevel logLevel = Log.LogLevel.error) {