Merge branch 'ds'
This commit is contained in:
commit
654ebf652e
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
55
blocks/cflow/BlockDeobfuscator.cs
Normal file
55
blocks/cflow/BlockDeobfuscator.cs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
/*
|
||||
|
|
74
blocks/cflow/CachedCflowDeobfuscator.cs
Normal file
74
blocks/cflow/CachedCflowDeobfuscator.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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,21 +25,18 @@ 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++) {
|
||||
|
@ -77,12 +75,12 @@ namespace de4dot.blocks.cflow {
|
|||
try {
|
||||
instructionEmulator.emulate(instr.Instruction);
|
||||
}
|
||||
catch (System.NullReferenceException) {
|
||||
catch (NullReferenceException) {
|
||||
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
public bool deobfuscate() {
|
||||
bool changed = false;
|
||||
|
||||
foreach (var switchBlock in allBlocks) {
|
||||
protected override bool deobfuscate(Block switchBlock) {
|
||||
if (switchBlock.LastInstr.OpCode.Code != Code.Switch)
|
||||
continue;
|
||||
return false;
|
||||
|
||||
if (isSwitchTopOfStack(switchBlock) && deobfuscateTos(switchBlock)) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
if (isSwitchTopOfStack(switchBlock) && deobfuscateTos(switchBlock))
|
||||
return true;
|
||||
|
||||
if (isLdlocBranch(switchBlock, true) && deobfuscateLdloc(switchBlock)) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
if (isLdlocBranch(switchBlock, true) && deobfuscateLdloc(switchBlock))
|
||||
return true;
|
||||
|
||||
if (isStLdlocBranch(switchBlock, true) && deobfuscateStLdloc(switchBlock)) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
if (isStLdlocBranch(switchBlock, true) && deobfuscateStLdloc(switchBlock))
|
||||
return true;
|
||||
|
||||
if (isSwitchType1(switchBlock) && deobfuscateType1(switchBlock)) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
if (isSwitchType1(switchBlock) && deobfuscateType1(switchBlock))
|
||||
return true;
|
||||
|
||||
if (switchBlock.FirstInstr.isLdloc() && fixSwitchBranch(switchBlock)) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (switchBlock.FirstInstr.isLdloc() && fixSwitchBranch(switchBlock))
|
||||
return true;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace de4dot.code {
|
|||
INameChecker NameChecker { get; }
|
||||
bool RenameResourcesInCode { get; }
|
||||
bool RemoveNamespaceWithOneType { get; }
|
||||
bool RenameResourceKeys { get; }
|
||||
|
||||
void deobfuscateBegin();
|
||||
void deobfuscate();
|
||||
|
|
|
@ -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,11 +329,15 @@ namespace de4dot.code {
|
|||
getLocalVariableValue(Instr.getLocalVar(blocks.Locals, instr), out arg);
|
||||
break;
|
||||
|
||||
case Code.Ldfld:
|
||||
case Code.Ldsfld:
|
||||
arg = instr.Operand;
|
||||
break;
|
||||
|
||||
default:
|
||||
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(),
|
||||
|
@ -332,6 +345,14 @@ namespace de4dot.code {
|
|||
errors++;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pops; i++) {
|
||||
if (!getArg(method, block, ref arg, ref instrIndex))
|
||||
return false;
|
||||
}
|
||||
arg = null;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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)
|
||||
|
|
316
de4dot.code/deobfuscators/ConstantsReader.cs
Normal file
316
de4dot.code/deobfuscators/ConstantsReader.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
326
de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs
Normal file
326
de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
213
de4dot.code/deobfuscators/DeepSea/CastDeobfuscator.cs
Normal file
213
de4dot.code/deobfuscators/DeepSea/CastDeobfuscator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
333
de4dot.code/deobfuscators/DeepSea/DsMethodCallInliner.cs
Normal file
333
de4dot.code/deobfuscators/DeepSea/DsMethodCallInliner.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
91
de4dot.code/deobfuscators/DeepSea/DsUtils.cs
Normal file
91
de4dot.code/deobfuscators/DeepSea/DsUtils.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
DeobUtils.decryptAndAddResources(module, data30.resource.Name, () => decryptResourceV3(data30.resource));
|
||||
rsrc = data30.resource;
|
||||
return true;
|
||||
|
||||
case ResourceVersion.V40:
|
||||
return decryptResource(data40.resourceField, data40.magic);
|
||||
|
||||
case ResourceVersion.V41:
|
||||
return decryptResource(data41.resourceField, data41.magic);
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
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, magicV4));
|
||||
DeobUtils.decryptAndAddResources(module, name, () => decryptResourceV4(resourceField.InitialValue, magic));
|
||||
resourceField.InitialValue = new byte[1];
|
||||
resourceField.FieldType = module.TypeSystem.Byte;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -27,5 +27,6 @@ namespace de4dot.code.renamer {
|
|||
bool isValidFieldName(string name);
|
||||
bool isValidGenericParamName(string name);
|
||||
bool isValidMethodArgName(string name);
|
||||
bool isValidResourceKeyName(string name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
257
de4dot.code/renamer/ResourceKeysRenamer.cs
Normal file
257
de4dot.code/renamer/ResourceKeysRenamer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
|
|
187
de4dot.code/resources/ResourceReader.cs
Normal file
187
de4dot.code/resources/ResourceReader.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -79,9 +79,18 @@ namespace de4dot.cui {
|
|||
exitCode = 1;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
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 isN00bUser() {
|
||||
var env = Environment.GetEnvironmentVariables();
|
||||
if (env["VisualStudioDir"] != null)
|
||||
static bool printFullStackTrace() {
|
||||
if (Log.isAtLeast(Log.LogLevel.verbose))
|
||||
return true;
|
||||
if (hasEnv("STACKTRACE"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return env["windir"] != null && env["PROMPT"] == null;
|
||||
}
|
||||
|
||||
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() {
|
||||
if (hasEnv("VisualStudioDir"))
|
||||
return false;
|
||||
return hasEnv("windir") && !hasEnv("PROMPT");
|
||||
}
|
||||
|
||||
public static void printStackTrace(Exception ex, Log.LogLevel logLevel = Log.LogLevel.error) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user