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
|
// Return true if it's one of the stloc instructions
|
||||||
public static bool isStloc(Instruction instr) {
|
public static bool isStloc(Instruction instr) {
|
||||||
switch (instr.OpCode.Code) {
|
switch (instr.OpCode.Code) {
|
||||||
|
@ -577,6 +592,25 @@ namespace de4dot.blocks {
|
||||||
return null;
|
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) {
|
public static Instruction clone(Instruction instr) {
|
||||||
return new Instruction {
|
return new Instruction {
|
||||||
Offset = instr.Offset,
|
Offset = instr.Offset,
|
||||||
|
@ -1174,5 +1208,50 @@ namespace de4dot.blocks {
|
||||||
|
|
||||||
return false;
|
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);
|
return DotNetUtils.getLdcI4Value(instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool isLdarg() {
|
||||||
|
return DotNetUtils.isLdarg(instruction);
|
||||||
|
}
|
||||||
|
|
||||||
public bool isStloc() {
|
public bool isStloc() {
|
||||||
return DotNetUtils.isStloc(instruction);
|
return DotNetUtils.isStloc(instruction);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,19 +39,20 @@
|
||||||
<Compile Include="Blocks.cs" />
|
<Compile Include="Blocks.cs" />
|
||||||
<Compile Include="BlocksSorter.cs" />
|
<Compile Include="BlocksSorter.cs" />
|
||||||
<Compile Include="cflow\BlockCflowDeobfuscator.cs" />
|
<Compile Include="cflow\BlockCflowDeobfuscator.cs" />
|
||||||
|
<Compile Include="cflow\BlockDeobfuscator.cs" />
|
||||||
|
<Compile Include="cflow\CachedCflowDeobfuscator.cs" />
|
||||||
<Compile Include="cflow\CflowDeobfuscator.cs" />
|
<Compile Include="cflow\CflowDeobfuscator.cs" />
|
||||||
<Compile Include="cflow\CflowUtils.cs" />
|
<Compile Include="cflow\CflowUtils.cs" />
|
||||||
<Compile Include="cflow\ConstantsFolder.cs" />
|
<Compile Include="cflow\ConstantsFolder.cs" />
|
||||||
<Compile Include="cflow\DeadCodeRemover.cs" />
|
<Compile Include="cflow\DeadCodeRemover.cs" />
|
||||||
<Compile Include="cflow\DeadStoreRemover.cs" />
|
<Compile Include="cflow\DeadStoreRemover.cs" />
|
||||||
|
<Compile Include="cflow\IBlocksDeobfuscator.cs" />
|
||||||
<Compile Include="cflow\ICflowDeobfuscator.cs" />
|
<Compile Include="cflow\ICflowDeobfuscator.cs" />
|
||||||
<Compile Include="cflow\IMethodCallInliner.cs" />
|
|
||||||
<Compile Include="cflow\InstructionEmulator.cs" />
|
<Compile Include="cflow\InstructionEmulator.cs" />
|
||||||
<Compile Include="cflow\Int32Value.cs" />
|
<Compile Include="cflow\Int32Value.cs" />
|
||||||
<Compile Include="cflow\Int64Value.cs" />
|
<Compile Include="cflow\Int64Value.cs" />
|
||||||
<Compile Include="cflow\MethodCallInliner.cs" />
|
<Compile Include="cflow\MethodCallInliner.cs" />
|
||||||
<Compile Include="cflow\MethodCallInlinerBase.cs" />
|
<Compile Include="cflow\MethodCallInlinerBase.cs" />
|
||||||
<Compile Include="cflow\NoMethodInliner.cs" />
|
|
||||||
<Compile Include="cflow\Real8Value.cs" />
|
<Compile Include="cflow\Real8Value.cs" />
|
||||||
<Compile Include="cflow\StLdlocFixer.cs" />
|
<Compile Include="cflow\StLdlocFixer.cs" />
|
||||||
<Compile Include="cflow\SwitchCflowDeobfuscator.cs" />
|
<Compile Include="cflow\SwitchCflowDeobfuscator.cs" />
|
||||||
|
|
|
@ -17,24 +17,22 @@
|
||||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
|
||||||
namespace de4dot.blocks.cflow {
|
namespace de4dot.blocks.cflow {
|
||||||
class BlockCflowDeobfuscator {
|
class BlockCflowDeobfuscator : BlockDeobfuscator {
|
||||||
Blocks blocks;
|
|
||||||
Block block;
|
Block block;
|
||||||
InstructionEmulator instructionEmulator = new InstructionEmulator();
|
InstructionEmulator instructionEmulator = new InstructionEmulator();
|
||||||
|
|
||||||
public void init(Blocks blocks, Block block) {
|
protected override bool deobfuscate(Block block) {
|
||||||
this.blocks = blocks;
|
|
||||||
this.block = block;
|
this.block = block;
|
||||||
|
if (!DotNetUtils.isConditionalBranch(block.LastInstr.OpCode.Code) && block.LastInstr.OpCode.Code != Code.Switch)
|
||||||
|
return false;
|
||||||
instructionEmulator.init(blocks);
|
instructionEmulator.init(blocks);
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if code was updated, false otherwise
|
|
||||||
public bool deobfuscate() {
|
|
||||||
var instructions = block.Instructions;
|
var instructions = block.Instructions;
|
||||||
if (instructions.Count == 0)
|
if (instructions.Count == 0)
|
||||||
return false;
|
return false;
|
||||||
|
@ -44,7 +42,7 @@ namespace de4dot.blocks.cflow {
|
||||||
instructionEmulator.emulate(instr);
|
instructionEmulator.emulate(instr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (System.NullReferenceException) {
|
catch (NullReferenceException) {
|
||||||
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||||
return false;
|
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 {
|
namespace de4dot.blocks.cflow {
|
||||||
public class BlocksCflowDeobfuscator {
|
public class BlocksCflowDeobfuscator {
|
||||||
BlockCflowDeobfuscator blockCflowDeobfuscator = new BlockCflowDeobfuscator();
|
|
||||||
Blocks blocks;
|
Blocks blocks;
|
||||||
List<Block> allBlocks = new List<Block>();
|
List<Block> allBlocks = new List<Block>();
|
||||||
SwitchCflowDeobfuscator switchCflowDeobfuscator = new SwitchCflowDeobfuscator();
|
List<IBlocksDeobfuscator> userBlocksDeobfuscators = new List<IBlocksDeobfuscator>();
|
||||||
DeadCodeRemover deadCodeRemover = new DeadCodeRemover();
|
List<IBlocksDeobfuscator> ourBlocksDeobfuscators = new List<IBlocksDeobfuscator>();
|
||||||
DeadStoreRemover deadStoreRemover = new DeadStoreRemover();
|
|
||||||
StLdlocFixer stLdlocFixer = new StLdlocFixer();
|
|
||||||
ConstantsFolder constantsFolder = new ConstantsFolder();
|
|
||||||
|
|
||||||
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) {
|
public void init(Blocks blocks) {
|
||||||
this.blocks = blocks;
|
this.blocks = blocks;
|
||||||
|
@ -41,6 +63,10 @@ namespace de4dot.blocks.cflow {
|
||||||
public void deobfuscate() {
|
public void deobfuscate() {
|
||||||
bool changed;
|
bool changed;
|
||||||
int iterations = -1;
|
int iterations = -1;
|
||||||
|
|
||||||
|
deobfuscateBegin(userBlocksDeobfuscators);
|
||||||
|
deobfuscateBegin(ourBlocksDeobfuscators);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
iterations++;
|
iterations++;
|
||||||
changed = false;
|
changed = false;
|
||||||
|
@ -52,40 +78,39 @@ namespace de4dot.blocks.cflow {
|
||||||
if (iterations == 0)
|
if (iterations == 0)
|
||||||
changed |= fixDotfuscatorLoop();
|
changed |= fixDotfuscatorLoop();
|
||||||
|
|
||||||
foreach (var block in allBlocks) {
|
changed |= deobfuscate(userBlocksDeobfuscators, allBlocks);
|
||||||
MethodCallInliner.init(blocks, block);
|
changed |= deobfuscate(ourBlocksDeobfuscators, allBlocks);
|
||||||
changed |= MethodCallInliner.deobfuscate();
|
changed |= deobfuscateNoChange(changed, userBlocksDeobfuscators, allBlocks);
|
||||||
}
|
changed |= deobfuscateNoChange(changed, ourBlocksDeobfuscators, allBlocks);
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
} while (changed);
|
} 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
|
// Hack for old Dotfuscator
|
||||||
bool fixDotfuscatorLoop() {
|
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 {
|
public class CflowDeobfuscator : ICflowDeobfuscator {
|
||||||
BlocksCflowDeobfuscator cflowDeobfuscator = new BlocksCflowDeobfuscator();
|
BlocksCflowDeobfuscator cflowDeobfuscator = new BlocksCflowDeobfuscator();
|
||||||
|
|
||||||
public CflowDeobfuscator(IMethodCallInliner methodCallInliner) {
|
public CflowDeobfuscator() {
|
||||||
cflowDeobfuscator.MethodCallInliner = methodCallInliner;
|
}
|
||||||
|
|
||||||
|
public CflowDeobfuscator(IBlocksDeobfuscator blocksDeobfuscator) {
|
||||||
|
cflowDeobfuscator.add(blocksDeobfuscator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deobfuscate(MethodDefinition method) {
|
public void deobfuscate(MethodDefinition method) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
@ -24,21 +25,18 @@ using de4dot.blocks;
|
||||||
|
|
||||||
namespace de4dot.blocks.cflow {
|
namespace de4dot.blocks.cflow {
|
||||||
// Very simple constants folder which is all that's needed at the moment
|
// Very simple constants folder which is all that's needed at the moment
|
||||||
class ConstantsFolder {
|
class ConstantsFolder : BlockDeobfuscator {
|
||||||
List<Block> allBlocks;
|
|
||||||
Blocks blocks;
|
|
||||||
InstructionEmulator instructionEmulator = new InstructionEmulator();
|
InstructionEmulator instructionEmulator = new InstructionEmulator();
|
||||||
List<ParameterDefinition> args;
|
List<ParameterDefinition> args;
|
||||||
|
|
||||||
public void init(Blocks blocks, List<Block> allBlocks) {
|
protected override void init(List<Block> allBlocks) {
|
||||||
this.blocks = blocks;
|
base.init(allBlocks);
|
||||||
this.allBlocks = allBlocks;
|
|
||||||
args = DotNetUtils.getParameters(blocks.Method);
|
args = DotNetUtils.getParameters(blocks.Method);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool deobfuscate() {
|
protected override bool deobfuscate(Block block) {
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
foreach (var block in allBlocks) {
|
|
||||||
instructionEmulator.init(blocks);
|
instructionEmulator.init(blocks);
|
||||||
var instrs = block.Instructions;
|
var instrs = block.Instructions;
|
||||||
for (int i = 0; i < instrs.Count; i++) {
|
for (int i = 0; i < instrs.Count; i++) {
|
||||||
|
@ -77,12 +75,12 @@ namespace de4dot.blocks.cflow {
|
||||||
try {
|
try {
|
||||||
instructionEmulator.emulate(instr.Instruction);
|
instructionEmulator.emulate(instr.Instruction);
|
||||||
}
|
}
|
||||||
catch (System.NullReferenceException) {
|
catch (NullReferenceException) {
|
||||||
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,31 +23,11 @@ using Mono.Cecil.Cil;
|
||||||
namespace de4dot.blocks.cflow {
|
namespace de4dot.blocks.cflow {
|
||||||
// Removes dead code that is the result of one of our optimizations, or created by the
|
// Removes dead code that is the result of one of our optimizations, or created by the
|
||||||
// obfuscator.
|
// obfuscator.
|
||||||
class DeadCodeRemover {
|
class DeadCodeRemover : BlockDeobfuscator {
|
||||||
List<Block> allBlocks;
|
|
||||||
List<int> allDeadInstructions = new List<int>();
|
List<int> allDeadInstructions = new List<int>();
|
||||||
InstructionExpressionFinder instructionExpressionFinder = new InstructionExpressionFinder();
|
InstructionExpressionFinder instructionExpressionFinder = new InstructionExpressionFinder();
|
||||||
|
|
||||||
public void init(List<Block> allBlocks) {
|
protected override bool deobfuscate(Block block) {
|
||||||
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) {
|
|
||||||
allDeadInstructions.Clear();
|
allDeadInstructions.Clear();
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
|
@ -26,17 +26,12 @@ namespace de4dot.blocks.cflow {
|
||||||
// dead code and remove it.
|
// dead code and remove it.
|
||||||
// I've only seen Xenocode generate this kind of code, so the code below is a special case of
|
// I've only seen Xenocode generate this kind of code, so the code below is a special case of
|
||||||
// the more general case.
|
// the more general case.
|
||||||
class DeadStoreRemover {
|
class DeadStoreRemover : IBlocksDeobfuscator {
|
||||||
Blocks blocks;
|
Blocks blocks;
|
||||||
List<Block> allBlocks = new List<Block>();
|
List<Block> allBlocks;
|
||||||
List<AccessFlags> localFlags = new List<AccessFlags>();
|
List<AccessFlags> localFlags = new List<AccessFlags>();
|
||||||
List<bool> deadLocals = new List<bool>();
|
List<bool> deadLocals = new List<bool>();
|
||||||
|
|
||||||
public void init(Blocks blocks, List<Block> allBlocks) {
|
|
||||||
this.blocks = blocks;
|
|
||||||
this.allBlocks = allBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
enum AccessFlags {
|
enum AccessFlags {
|
||||||
None = 0,
|
None = 0,
|
||||||
|
@ -44,7 +39,18 @@ namespace de4dot.blocks.cflow {
|
||||||
Write = 2,
|
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)
|
if (blocks.Locals.Count == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,15 @@
|
||||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace de4dot.blocks.cflow {
|
using System.Collections.Generic;
|
||||||
public interface IMethodCallInliner {
|
|
||||||
void init(Blocks blocks, Block block);
|
|
||||||
|
|
||||||
// Returns true if something was inlined
|
namespace de4dot.blocks.cflow {
|
||||||
bool deobfuscate();
|
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 {
|
namespace de4dot.blocks.cflow {
|
||||||
public class InstructionEmulator {
|
public class InstructionEmulator {
|
||||||
ValueStack valueStack = new ValueStack();
|
ValueStack valueStack = new ValueStack();
|
||||||
|
Dictionary<Value, bool> protectedStackValues = new Dictionary<Value, bool>();
|
||||||
IList<ParameterDefinition> parameterDefinitions;
|
IList<ParameterDefinition> parameterDefinitions;
|
||||||
IList<VariableDefinition> variableDefinitions;
|
IList<VariableDefinition> variableDefinitions;
|
||||||
List<Value> args = new List<Value>();
|
List<Value> args = new List<Value>();
|
||||||
|
@ -52,6 +53,7 @@ namespace de4dot.blocks.cflow {
|
||||||
this.parameterDefinitions = method.Parameters;
|
this.parameterDefinitions = method.Parameters;
|
||||||
this.variableDefinitions = method.Body.Variables;
|
this.variableDefinitions = method.Body.Variables;
|
||||||
valueStack.init();
|
valueStack.init();
|
||||||
|
protectedStackValues.Clear();
|
||||||
|
|
||||||
if (method != prev_method) {
|
if (method != prev_method) {
|
||||||
prev_method = method;
|
prev_method = method;
|
||||||
|
@ -77,6 +79,10 @@ namespace de4dot.blocks.cflow {
|
||||||
locals.AddRange(cached_locals);
|
locals.AddRange(cached_locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProtected(Value value) {
|
||||||
|
protectedStackValues[value] = true;
|
||||||
|
}
|
||||||
|
|
||||||
static Value getUnknownValue(TypeReference typeReference) {
|
static Value getUnknownValue(TypeReference typeReference) {
|
||||||
if (typeReference == null)
|
if (typeReference == null)
|
||||||
return new UnknownValue();
|
return new UnknownValue();
|
||||||
|
@ -94,9 +100,11 @@ namespace de4dot.blocks.cflow {
|
||||||
return new UnknownValue();
|
return new UnknownValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value truncateValue(Value value, TypeReference typeReference) {
|
Value truncateValue(Value value, TypeReference typeReference) {
|
||||||
if (typeReference == null)
|
if (typeReference == null)
|
||||||
return value;
|
return value;
|
||||||
|
if (protectedStackValues.ContainsKey(value))
|
||||||
|
return value;
|
||||||
|
|
||||||
switch (typeReference.EType) {
|
switch (typeReference.EType) {
|
||||||
case ElementType.Boolean:
|
case ElementType.Boolean:
|
||||||
|
@ -208,6 +216,10 @@ namespace de4dot.blocks.cflow {
|
||||||
return new UnknownValue();
|
return new UnknownValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int stackSize() {
|
||||||
|
return valueStack.Size;
|
||||||
|
}
|
||||||
|
|
||||||
public void push(Value value) {
|
public void push(Value value) {
|
||||||
valueStack.push(value);
|
valueStack.push(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,12 @@
|
||||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
|
||||||
namespace de4dot.blocks.cflow {
|
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
|
// We can't catch all infinite loops, so inline methods at most this many times
|
||||||
const int MAX_ITERATIONS = 10;
|
const int MAX_ITERATIONS = 10;
|
||||||
|
|
||||||
|
@ -29,24 +30,46 @@ namespace de4dot.blocks.cflow {
|
||||||
protected Block block;
|
protected Block block;
|
||||||
int iteration;
|
int iteration;
|
||||||
|
|
||||||
public void init(Blocks blocks, Block block) {
|
public bool ExecuteOnNoChange { get; set; }
|
||||||
|
|
||||||
|
public void deobfuscateBegin(Blocks blocks) {
|
||||||
this.blocks = blocks;
|
this.blocks = blocks;
|
||||||
this.block = block;
|
iteration = 0;
|
||||||
this.iteration = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool deobfuscate() {
|
public bool deobfuscate(List<Block> allBlocks) {
|
||||||
if (iteration++ >= MAX_ITERATIONS)
|
if (iteration++ >= MAX_ITERATIONS)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return deobfuscateInternal();
|
bool changed = false;
|
||||||
|
foreach (var block in allBlocks) {
|
||||||
|
this.block = block;
|
||||||
|
changed |= deobfuscateInternal();
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract bool deobfuscateInternal();
|
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) {
|
protected bool inlineLoadMethod(int patchIndex, MethodDefinition methodToInline, Instruction loadInstr, int instrIndex) {
|
||||||
var instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
|
if (!isReturn(methodToInline, instrIndex))
|
||||||
if (instr == null || instr.OpCode.Code != Code.Ret)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int methodArgsCount = DotNetUtils.getArgsCount(methodToInline);
|
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) {
|
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 loadIndex = 0;
|
||||||
int methodArgsCount = DotNetUtils.getArgsCount(methodToInline);
|
int methodArgsCount = DotNetUtils.getArgsCount(methodToInline);
|
||||||
bool foundLdarga = false;
|
bool foundLdarga = false;
|
||||||
|
@ -83,75 +121,66 @@ namespace de4dot.blocks.cflow {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (DotNetUtils.getArgIndex(instr) != loadIndex)
|
if (DotNetUtils.getArgIndex(instr) != loadIndex)
|
||||||
return false;
|
return null;
|
||||||
loadIndex++;
|
loadIndex++;
|
||||||
instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
|
instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
|
||||||
}
|
}
|
||||||
if (instr == null || loadIndex != methodArgsCount - popLastArgs)
|
if (instr == null || loadIndex != methodArgsCount - popLastArgs)
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
if (instr.OpCode.Code == Code.Call || instr.OpCode.Code == Code.Callvirt) {
|
if (instr.OpCode.Code == Code.Call || instr.OpCode.Code == Code.Callvirt) {
|
||||||
if (foundLdarga)
|
if (foundLdarga)
|
||||||
return false;
|
return null;
|
||||||
var callInstr = instr;
|
var callInstr = instr;
|
||||||
var calledMethod = callInstr.Operand as MethodReference;
|
var calledMethod = callInstr.Operand as MethodReference;
|
||||||
if (calledMethod == null)
|
if (calledMethod == null)
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
if (!isCompatibleType(-1, calledMethod.MethodReturnType.ReturnType, methodToInline.MethodReturnType.ReturnType))
|
if (!isCompatibleType(-1, calledMethod.MethodReturnType.ReturnType, methodToInline.MethodReturnType.ReturnType))
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
if (!checkSameMethods(calledMethod, methodToInline, popLastArgs))
|
if (!checkSameMethods(calledMethod, methodToInline, popLastArgs))
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
|
return new InstructionPatcher(patchIndex, instrIndex, callInstr);
|
||||||
if (instr == null || instr.OpCode.Code != Code.Ret)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
block.Instructions[patchIndex] = new Instr(DotNetUtils.clone(callInstr));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else if (instr.OpCode.Code == Code.Newobj) {
|
else if (instr.OpCode.Code == Code.Newobj) {
|
||||||
if (foundLdarga)
|
if (foundLdarga)
|
||||||
return false;
|
return null;
|
||||||
var newobjInstr = instr;
|
var newobjInstr = instr;
|
||||||
var ctor = newobjInstr.Operand as MethodReference;
|
var ctor = newobjInstr.Operand as MethodReference;
|
||||||
if (ctor == null)
|
if (ctor == null)
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
if (!isCompatibleType(-1, ctor.DeclaringType, methodToInline.MethodReturnType.ReturnType))
|
if (!isCompatibleType(-1, ctor.DeclaringType, methodToInline.MethodReturnType.ReturnType))
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
var methodArgs = DotNetUtils.getArgs(methodToInline);
|
var methodArgs = DotNetUtils.getArgs(methodToInline);
|
||||||
var calledMethodArgs = DotNetUtils.getArgs(ctor);
|
var calledMethodArgs = DotNetUtils.getArgs(ctor);
|
||||||
if (methodArgs.Count + 1 - popLastArgs != calledMethodArgs.Count)
|
if (methodArgs.Count + 1 - popLastArgs != calledMethodArgs.Count)
|
||||||
return false;
|
return null;
|
||||||
for (int i = 1; i < calledMethodArgs.Count; i++) {
|
for (int i = 1; i < calledMethodArgs.Count; i++) {
|
||||||
if (!isCompatibleType(i, calledMethodArgs[i], methodArgs[i - 1]))
|
if (!isCompatibleType(i, calledMethodArgs[i], methodArgs[i - 1]))
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
|
return new InstructionPatcher(patchIndex, instrIndex, newobjInstr);
|
||||||
if (instr == null || instr.OpCode.Code != Code.Ret)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
block.Instructions[patchIndex] = new Instr(DotNetUtils.clone(newobjInstr));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else if (instr.OpCode.Code == Code.Ldfld || instr.OpCode.Code == Code.Ldflda ||
|
else if (instr.OpCode.Code == Code.Ldfld || instr.OpCode.Code == Code.Ldflda ||
|
||||||
instr.OpCode.Code == Code.Ldftn || instr.OpCode.Code == Code.Ldvirtftn) {
|
instr.OpCode.Code == Code.Ldftn || instr.OpCode.Code == Code.Ldvirtftn) {
|
||||||
var ldInstr = instr;
|
var ldInstr = instr;
|
||||||
instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex);
|
|
||||||
if (instr == null || instr.OpCode.Code != Code.Ret)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (methodArgsCount != 1)
|
if (methodArgsCount != 1)
|
||||||
return false;
|
return null;
|
||||||
block.Instructions[patchIndex] = new Instr(DotNetUtils.clone(ldInstr));
|
|
||||||
return true;
|
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) {
|
protected bool checkSameMethods(MethodReference method, MethodDefinition methodToInline, int ignoreLastMethodToInlineArgs = 0) {
|
||||||
|
|
|
@ -22,25 +22,15 @@ using Mono.Cecil.Cil;
|
||||||
|
|
||||||
namespace de4dot.blocks.cflow {
|
namespace de4dot.blocks.cflow {
|
||||||
// Replace stloc + ldloc with dup + stloc
|
// Replace stloc + ldloc with dup + stloc
|
||||||
class StLdlocFixer {
|
class StLdlocFixer : BlockDeobfuscator {
|
||||||
IList<VariableDefinition> locals;
|
IList<VariableDefinition> locals;
|
||||||
List<Block> allBlocks;
|
|
||||||
|
|
||||||
public void init(List<Block> allBlocks, IList<VariableDefinition> locals) {
|
protected override void init(List<Block> allBlocks) {
|
||||||
this.allBlocks = allBlocks;
|
base.init(allBlocks);
|
||||||
this.locals = locals;
|
locals = blocks.Locals;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool fix() {
|
protected override bool deobfuscate(Block block) {
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
foreach (var block in allBlocks)
|
|
||||||
changed |= fix(block);
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fix(Block block) {
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
var instructions = block.Instructions;
|
var instructions = block.Instructions;
|
||||||
for (int i = 0; i < instructions.Count; i++) {
|
for (int i = 0; i < instructions.Count; i++) {
|
||||||
|
|
|
@ -17,54 +17,34 @@
|
||||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
|
||||||
namespace de4dot.blocks.cflow {
|
namespace de4dot.blocks.cflow {
|
||||||
class SwitchCflowDeobfuscator {
|
class SwitchCflowDeobfuscator : BlockDeobfuscator {
|
||||||
List<Block> allBlocks;
|
|
||||||
Blocks blocks;
|
|
||||||
InstructionEmulator instructionEmulator = new InstructionEmulator();
|
InstructionEmulator instructionEmulator = new InstructionEmulator();
|
||||||
|
|
||||||
public void init(Blocks blocks, List<Block> allBlocks) {
|
protected override bool deobfuscate(Block switchBlock) {
|
||||||
this.blocks = blocks;
|
|
||||||
this.allBlocks = allBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool deobfuscate() {
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
foreach (var switchBlock in allBlocks) {
|
|
||||||
if (switchBlock.LastInstr.OpCode.Code != Code.Switch)
|
if (switchBlock.LastInstr.OpCode.Code != Code.Switch)
|
||||||
continue;
|
return false;
|
||||||
|
|
||||||
if (isSwitchTopOfStack(switchBlock) && deobfuscateTos(switchBlock)) {
|
if (isSwitchTopOfStack(switchBlock) && deobfuscateTos(switchBlock))
|
||||||
changed = true;
|
return true;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLdlocBranch(switchBlock, true) && deobfuscateLdloc(switchBlock)) {
|
if (isLdlocBranch(switchBlock, true) && deobfuscateLdloc(switchBlock))
|
||||||
changed = true;
|
return true;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isStLdlocBranch(switchBlock, true) && deobfuscateStLdloc(switchBlock)) {
|
if (isStLdlocBranch(switchBlock, true) && deobfuscateStLdloc(switchBlock))
|
||||||
changed = true;
|
return true;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSwitchType1(switchBlock) && deobfuscateType1(switchBlock)) {
|
if (isSwitchType1(switchBlock) && deobfuscateType1(switchBlock))
|
||||||
changed = true;
|
return true;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (switchBlock.FirstInstr.isLdloc() && fixSwitchBranch(switchBlock)) {
|
if (switchBlock.FirstInstr.isLdloc() && fixSwitchBranch(switchBlock))
|
||||||
changed = true;
|
return true;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isSwitchTopOfStack(Block switchBlock) {
|
static bool isSwitchTopOfStack(Block switchBlock) {
|
||||||
|
@ -268,7 +248,7 @@ namespace de4dot.blocks.cflow {
|
||||||
try {
|
try {
|
||||||
instructionEmulator.emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1);
|
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)
|
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||||
target = null;
|
target = null;
|
||||||
return false;
|
return false;
|
||||||
|
@ -283,7 +263,7 @@ namespace de4dot.blocks.cflow {
|
||||||
instructionEmulator.emulate(source.Instructions);
|
instructionEmulator.emulate(source.Instructions);
|
||||||
instructionEmulator.emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1);
|
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)
|
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,10 @@ namespace de4dot.blocks.cflow {
|
||||||
class ValueStack {
|
class ValueStack {
|
||||||
List<Value> stack = new List<Value>();
|
List<Value> stack = new List<Value>();
|
||||||
|
|
||||||
|
public int Size {
|
||||||
|
get { return stack.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
stack.Clear();
|
stack.Clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ namespace de4dot.code {
|
||||||
INameChecker NameChecker { get; }
|
INameChecker NameChecker { get; }
|
||||||
bool RenameResourcesInCode { get; }
|
bool RenameResourcesInCode { get; }
|
||||||
bool RemoveNamespaceWithOneType { get; }
|
bool RemoveNamespaceWithOneType { get; }
|
||||||
|
bool RenameResourceKeys { get; }
|
||||||
|
|
||||||
void deobfuscateBegin();
|
void deobfuscateBegin();
|
||||||
void deobfuscate();
|
void deobfuscate();
|
||||||
|
|
|
@ -146,6 +146,12 @@ namespace de4dot.code {
|
||||||
Blocks blocks;
|
Blocks blocks;
|
||||||
VariableValues variableValues;
|
VariableValues variableValues;
|
||||||
int errors = 0;
|
int errors = 0;
|
||||||
|
bool useUnknownArgs = false;
|
||||||
|
|
||||||
|
public bool UseUnknownArgs {
|
||||||
|
get { return useUnknownArgs; }
|
||||||
|
set { useUnknownArgs = value; }
|
||||||
|
}
|
||||||
|
|
||||||
protected class CallResult {
|
protected class CallResult {
|
||||||
public Block block;
|
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)
|
if (variableValues == null)
|
||||||
variableValues = new VariableValues(blocks.Locals, allBlocks);
|
variableValues = new VariableValues(blocks.Locals, allBlocks);
|
||||||
var val = variableValues.getValue(variable);
|
var val = variableValues.getValue(variable);
|
||||||
if (!val.isValid())
|
if (!val.isValid()) {
|
||||||
throw new ApplicationException("Could not get value of local variable");
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
value = val.Value;
|
value = val.Value;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void findAllCallResults() {
|
void findAllCallResults() {
|
||||||
|
@ -320,11 +329,15 @@ namespace de4dot.code {
|
||||||
getLocalVariableValue(Instr.getLocalVar(blocks.Locals, instr), out arg);
|
getLocalVariableValue(Instr.getLocalVar(blocks.Locals, instr), out arg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Code.Ldfld:
|
||||||
case Code.Ldsfld:
|
case Code.Ldsfld:
|
||||||
arg = instr.Operand;
|
arg = instr.Operand;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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}",
|
Log.w("Could not find all arguments to method {0} ({1:X8}), instr: {2}",
|
||||||
Utils.removeNewlines(method),
|
Utils.removeNewlines(method),
|
||||||
method.MetadataToken.ToInt32(),
|
method.MetadataToken.ToInt32(),
|
||||||
|
@ -332,6 +345,14 @@ namespace de4dot.code {
|
||||||
errors++;
|
errors++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < pops; i++) {
|
||||||
|
if (!getArg(method, block, ref arg, ref instrIndex))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
arg = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,10 @@ namespace de4dot.code {
|
||||||
get { return (deob.RenamingOptions & RenamingOptions.RemoveNamespaceIfOneType) != 0; }
|
get { return (deob.RenamingOptions & RenamingOptions.RemoveNamespaceIfOneType) != 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool RenameResourceKeys {
|
||||||
|
get { return (deob.RenamingOptions & RenamingOptions.RenameResourceKeys) != 0; }
|
||||||
|
}
|
||||||
|
|
||||||
public IDeobfuscator Deobfuscator {
|
public IDeobfuscator Deobfuscator {
|
||||||
get { return deob; }
|
get { return deob; }
|
||||||
}
|
}
|
||||||
|
@ -541,7 +545,7 @@ namespace de4dot.code {
|
||||||
|
|
||||||
Log.v("Deobfuscating methods");
|
Log.v("Deobfuscating methods");
|
||||||
var methodPrinter = new MethodPrinter();
|
var methodPrinter = new MethodPrinter();
|
||||||
var cflowDeobfuscator = new BlocksCflowDeobfuscator { MethodCallInliner = deob.MethodCallInliner };
|
var cflowDeobfuscator = new BlocksCflowDeobfuscator(deob.BlocksDeobfuscators);
|
||||||
foreach (var method in allMethods) {
|
foreach (var method in allMethods) {
|
||||||
Log.v("Deobfuscating {0} ({1:X8})", Utils.removeNewlines(method), method.MetadataToken.ToUInt32());
|
Log.v("Deobfuscating {0} ({1:X8})", Utils.removeNewlines(method), method.MetadataToken.ToUInt32());
|
||||||
Log.indent();
|
Log.indent();
|
||||||
|
@ -715,7 +719,7 @@ namespace de4dot.code {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
deobfuscate(method, "Deobfuscating control flow", (blocks) => {
|
deobfuscate(method, "Deobfuscating control flow", (blocks) => {
|
||||||
var cflowDeobfuscator = new BlocksCflowDeobfuscator { MethodCallInliner = deob.MethodCallInliner };
|
var cflowDeobfuscator = new BlocksCflowDeobfuscator(deob.BlocksDeobfuscators);
|
||||||
cflowDeobfuscator.init(blocks);
|
cflowDeobfuscator.init(blocks);
|
||||||
cflowDeobfuscator.deobfuscate();
|
cflowDeobfuscator.deobfuscate();
|
||||||
});
|
});
|
||||||
|
|
|
@ -107,6 +107,7 @@
|
||||||
<Compile Include="deobfuscators\CodeVeil\Deobfuscator.cs" />
|
<Compile Include="deobfuscators\CodeVeil\Deobfuscator.cs" />
|
||||||
<Compile Include="deobfuscators\CodeVeil\ProxyDelegateFinder.cs" />
|
<Compile Include="deobfuscators\CodeVeil\ProxyDelegateFinder.cs" />
|
||||||
<Compile Include="deobfuscators\CodeVeil\TamperDetection.cs" />
|
<Compile Include="deobfuscators\CodeVeil\TamperDetection.cs" />
|
||||||
|
<Compile Include="deobfuscators\ConstantsReader.cs" />
|
||||||
<Compile Include="deobfuscators\CryptoObfuscator\AntiDebugger.cs" />
|
<Compile Include="deobfuscators\CryptoObfuscator\AntiDebugger.cs" />
|
||||||
<Compile Include="deobfuscators\CryptoObfuscator\AssemblyResolver.cs" />
|
<Compile Include="deobfuscators\CryptoObfuscator\AssemblyResolver.cs" />
|
||||||
<Compile Include="deobfuscators\CryptoObfuscator\Deobfuscator.cs" />
|
<Compile Include="deobfuscators\CryptoObfuscator\Deobfuscator.cs" />
|
||||||
|
@ -115,6 +116,10 @@
|
||||||
<Compile Include="deobfuscators\CryptoObfuscator\ResourceResolver.cs" />
|
<Compile Include="deobfuscators\CryptoObfuscator\ResourceResolver.cs" />
|
||||||
<Compile Include="deobfuscators\CryptoObfuscator\StringDecrypter.cs" />
|
<Compile Include="deobfuscators\CryptoObfuscator\StringDecrypter.cs" />
|
||||||
<Compile Include="deobfuscators\CryptoObfuscator\TamperDetection.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\DeepSea\FieldsRestorer.cs" />
|
||||||
<Compile Include="deobfuscators\DeobfuscatorBase.cs" />
|
<Compile Include="deobfuscators\DeobfuscatorBase.cs" />
|
||||||
<Compile Include="deobfuscators\DeobfuscatorInfoBase.cs" />
|
<Compile Include="deobfuscators\DeobfuscatorInfoBase.cs" />
|
||||||
|
@ -125,7 +130,7 @@
|
||||||
<Compile Include="deobfuscators\DeepSea\ResourceResolver.cs" />
|
<Compile Include="deobfuscators\DeepSea\ResourceResolver.cs" />
|
||||||
<Compile Include="deobfuscators\DeepSea\StringDecrypter.cs" />
|
<Compile Include="deobfuscators\DeepSea\StringDecrypter.cs" />
|
||||||
<Compile Include="deobfuscators\DeepSea\Deobfuscator.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\Deobfuscator.cs" />
|
||||||
<Compile Include="deobfuscators\Dotfuscator\StringDecrypter.cs" />
|
<Compile Include="deobfuscators\Dotfuscator\StringDecrypter.cs" />
|
||||||
<Compile Include="deobfuscators\dotNET_Reactor\v3\AntiStrongName.cs" />
|
<Compile Include="deobfuscators\dotNET_Reactor\v3\AntiStrongName.cs" />
|
||||||
|
@ -152,7 +157,7 @@
|
||||||
<Compile Include="deobfuscators\dotNET_Reactor\v3\MemoryPatcher.cs" />
|
<Compile Include="deobfuscators\dotNET_Reactor\v3\MemoryPatcher.cs" />
|
||||||
<Compile Include="deobfuscators\Eazfuscator_NET\AssemblyResolver.cs" />
|
<Compile Include="deobfuscators\Eazfuscator_NET\AssemblyResolver.cs" />
|
||||||
<Compile Include="deobfuscators\Eazfuscator_NET\CodeCompilerMethodCallRestorer.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\DecrypterType.cs" />
|
||||||
<Compile Include="deobfuscators\Eazfuscator_NET\Deobfuscator.cs" />
|
<Compile Include="deobfuscators\Eazfuscator_NET\Deobfuscator.cs" />
|
||||||
<Compile Include="deobfuscators\Eazfuscator_NET\EfUtils.cs" />
|
<Compile Include="deobfuscators\Eazfuscator_NET\EfUtils.cs" />
|
||||||
|
@ -253,6 +258,7 @@
|
||||||
<Compile Include="renamer\MemberInfos.cs" />
|
<Compile Include="renamer\MemberInfos.cs" />
|
||||||
<Compile Include="renamer\NameCreators.cs" />
|
<Compile Include="renamer\NameCreators.cs" />
|
||||||
<Compile Include="renamer\Renamer.cs" />
|
<Compile Include="renamer\Renamer.cs" />
|
||||||
|
<Compile Include="renamer\ResourceKeysRenamer.cs" />
|
||||||
<Compile Include="renamer\ResourceRenamer.cs" />
|
<Compile Include="renamer\ResourceRenamer.cs" />
|
||||||
<Compile Include="renamer\TypeInfo.cs" />
|
<Compile Include="renamer\TypeInfo.cs" />
|
||||||
<Compile Include="renamer\TypeNames.cs" />
|
<Compile Include="renamer\TypeNames.cs" />
|
||||||
|
@ -262,6 +268,7 @@
|
||||||
<Compile Include="resources\IResourceData.cs" />
|
<Compile Include="resources\IResourceData.cs" />
|
||||||
<Compile Include="resources\ResourceElement.cs" />
|
<Compile Include="resources\ResourceElement.cs" />
|
||||||
<Compile Include="resources\ResourceElementSet.cs" />
|
<Compile Include="resources\ResourceElementSet.cs" />
|
||||||
|
<Compile Include="resources\ResourceReader.cs" />
|
||||||
<Compile Include="resources\ResourceTypeCode.cs" />
|
<Compile Include="resources\ResourceTypeCode.cs" />
|
||||||
<Compile Include="resources\ResourceDataCreator.cs" />
|
<Compile Include="resources\ResourceDataCreator.cs" />
|
||||||
<Compile Include="resources\ResourceWriter.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.Collections.Generic.Dictionary`2<System.UInt16,System.Type>",
|
||||||
"System.UInt16",
|
"System.UInt16",
|
||||||
};
|
};
|
||||||
var cflowDeobfuscator = new CflowDeobfuscator(new NoMethodInliner());
|
var cflowDeobfuscator = new CflowDeobfuscator();
|
||||||
foreach (var type in module.Types) {
|
foreach (var type in module.Types) {
|
||||||
var cctor = DotNetUtils.getMethod(type, ".cctor");
|
var cctor = DotNetUtils.getMethod(type, ".cctor");
|
||||||
if (cctor == null)
|
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 {
|
class AssemblyResolver : ResolverBase {
|
||||||
Version version;
|
Version version;
|
||||||
List<FieldInfo> fieldInfos;
|
List<FieldInfo> fieldInfos;
|
||||||
|
MethodDefinition decryptMethod;
|
||||||
|
|
||||||
enum Version {
|
enum Version {
|
||||||
Unknown,
|
Unknown,
|
||||||
V3Old,
|
V3Old,
|
||||||
V3,
|
V3,
|
||||||
V4,
|
V4,
|
||||||
|
V404,
|
||||||
|
V41,
|
||||||
|
V41SL,
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AssemblyInfo {
|
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)
|
public AssemblyResolver(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob)
|
||||||
: base(module, simpleDeobfuscator, deob) {
|
: base(module, simpleDeobfuscator, deob) {
|
||||||
}
|
}
|
||||||
|
@ -89,6 +97,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
continue;
|
continue;
|
||||||
if (!DotNetUtils.isMethod(method, "System.Void", "(System.String)"))
|
if (!DotNetUtils.isMethod(method, "System.Void", "(System.String)"))
|
||||||
continue;
|
continue;
|
||||||
|
simpleDeobfuscator.deobfuscate(method);
|
||||||
if (!new LocalTypes(method).all(requiredLocals_sl))
|
if (!new LocalTypes(method).all(requiredLocals_sl))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -102,10 +111,50 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateVersion(MethodDefinition handler) {
|
void updateVersion(MethodDefinition handler) {
|
||||||
if (isV3Old(handler))
|
if (isV3Old(handler)) {
|
||||||
version = Version.V3Old;
|
version = Version.V3Old;
|
||||||
else
|
return;
|
||||||
version = Version.V3;
|
}
|
||||||
|
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) {
|
static bool isV3Old(MethodDefinition method) {
|
||||||
|
@ -127,10 +176,19 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
|
|
||||||
simpleDeobfuscator.deobfuscate(handler);
|
simpleDeobfuscator.deobfuscate(handler);
|
||||||
List<FieldInfo> fieldInfosTmp;
|
List<FieldInfo> fieldInfosTmp;
|
||||||
if (checkHandlerV4(handler, out fieldInfosTmp) ||
|
MethodDefinition decryptMethodTmp;
|
||||||
checkHandlerV4_0_4(handler, out fieldInfosTmp)) {
|
if (checkHandlerV4(handler, out fieldInfosTmp, out decryptMethodTmp)) {
|
||||||
version = Version.V4;
|
version = Version.V4;
|
||||||
fieldInfos = fieldInfosTmp;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,8 +221,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4.0.1.18 .. 4.0.3
|
// 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>();
|
fieldInfos = new List<FieldInfo>();
|
||||||
|
decryptMethod = null;
|
||||||
|
|
||||||
var instrs = handler.Body.Instructions;
|
var instrs = handler.Body.Instructions;
|
||||||
for (int i = 0; i < instrs.Count - 3; i++) {
|
for (int i = 0; i < instrs.Count - 3; i++) {
|
||||||
|
@ -193,21 +252,25 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
call = instrs[index++];
|
call = instrs[index++];
|
||||||
if (call.OpCode.Code != Code.Call)
|
if (call.OpCode.Code != Code.Call)
|
||||||
return false;
|
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;
|
return false;
|
||||||
|
|
||||||
|
decryptMethod = decryptMethodTmp;
|
||||||
fieldInfos.Add(new FieldInfo(field, magic));
|
fieldInfos.Add(new FieldInfo(field, magic));
|
||||||
}
|
}
|
||||||
|
|
||||||
return fieldInfos.Count != 0;
|
return fieldInfos.Count != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4.0.4+
|
// 4.0.4, 4.1+
|
||||||
bool checkHandlerV4_0_4(MethodDefinition handler, out List<FieldInfo> fieldInfos) {
|
Version checkHandlerV404_41(MethodDefinition handler, out List<FieldInfo> fieldInfos, out MethodDefinition decryptMethod) {
|
||||||
|
Version version = Version.Unknown;
|
||||||
fieldInfos = new List<FieldInfo>();
|
fieldInfos = new List<FieldInfo>();
|
||||||
|
decryptMethod = null;
|
||||||
|
|
||||||
var instrs = handler.Body.Instructions;
|
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;
|
int index = i;
|
||||||
|
|
||||||
var ldci4_len = instrs[index++];
|
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)"))
|
if (!DotNetUtils.isMethod(call1.Operand as MethodReference, "System.Void", "(System.Array,System.RuntimeFieldHandle)"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!DotNetUtils.isLdloc(instrs[index++]))
|
int callIndex = getCallDecryptMethodIndex(instrs, index);
|
||||||
continue;
|
if (callIndex < 0)
|
||||||
|
|
||||||
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)"))
|
|
||||||
continue;
|
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));
|
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() {
|
public IEnumerable<AssemblyInfo> getAssemblyInfos() {
|
||||||
|
@ -261,16 +435,22 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
|
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version.V3Old:
|
case Version.V3Old:
|
||||||
|
return getAssemblyInfos(resource => decryptResourceV3Old(resource));
|
||||||
case Version.V3:
|
case Version.V3:
|
||||||
return getAssemblyInfosV3();
|
return getAssemblyInfos(resource => decryptResourceV3(resource));
|
||||||
case Version.V4:
|
case Version.V4:
|
||||||
|
case Version.V404:
|
||||||
return getAssemblyInfosV4();
|
return getAssemblyInfosV4();
|
||||||
|
case Version.V41:
|
||||||
|
return getAssemblyInfosV41();
|
||||||
|
case Version.V41SL:
|
||||||
|
return getAssemblyInfos(resource => decryptResourceV41SL(resource));
|
||||||
default:
|
default:
|
||||||
throw new ApplicationException("Unknown version");
|
throw new ApplicationException("Unknown version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<AssemblyInfo> getAssemblyInfosV3() {
|
IEnumerable<AssemblyInfo> getAssemblyInfos(Func<EmbeddedResource, byte[]> decrypter) {
|
||||||
var infos = new List<AssemblyInfo>();
|
var infos = new List<AssemblyInfo>();
|
||||||
|
|
||||||
foreach (var tmp in module.Resources) {
|
foreach (var tmp in module.Resources) {
|
||||||
|
@ -279,7 +459,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
continue;
|
continue;
|
||||||
if (!Regex.IsMatch(resource.Name, @"^[0-9A-F]{40}$"))
|
if (!Regex.IsMatch(resource.Name, @"^[0-9A-F]{40}$"))
|
||||||
continue;
|
continue;
|
||||||
var info = getAssemblyInfoV3(resource);
|
var info = getAssemblyInfo(resource, decrypter);
|
||||||
if (info == null)
|
if (info == null)
|
||||||
continue;
|
continue;
|
||||||
infos.Add(info);
|
infos.Add(info);
|
||||||
|
@ -288,9 +468,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssemblyInfo getAssemblyInfoV3(EmbeddedResource resource) {
|
AssemblyInfo getAssemblyInfo(EmbeddedResource resource, Func<EmbeddedResource, byte[]> decrypter) {
|
||||||
try {
|
try {
|
||||||
var decrypted = version == Version.V3Old ? decryptResourceV3Old(resource) : decryptResourceV3(resource);
|
var decrypted = decrypter(resource);
|
||||||
return getAssemblyInfo(decrypted, resource);
|
return getAssemblyInfo(decrypted, resource);
|
||||||
}
|
}
|
||||||
catch (Exception) {
|
catch (Exception) {
|
||||||
|
@ -306,14 +486,14 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return new AssemblyInfo(decryptedData, fullName, simpleName, extension, resource);
|
return new AssemblyInfo(decryptedData, fullName, simpleName, extension, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<AssemblyInfo> getAssemblyInfosV4() {
|
IEnumerable<AssemblyInfo> getAssemblyInfos(Func<byte[], int, byte[]> decrypter) {
|
||||||
var infos = new List<AssemblyInfo>();
|
var infos = new List<AssemblyInfo>();
|
||||||
|
|
||||||
if (fieldInfos == null)
|
if (fieldInfos == null)
|
||||||
return infos;
|
return infos;
|
||||||
|
|
||||||
foreach (var fieldInfo in fieldInfos) {
|
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));
|
infos.Add(getAssemblyInfo(decrypted, null));
|
||||||
fieldInfo.field.InitialValue = new byte[1];
|
fieldInfo.field.InitialValue = new byte[1];
|
||||||
fieldInfo.field.FieldType = module.TypeSystem.Byte;
|
fieldInfo.field.FieldType = module.TypeSystem.Byte;
|
||||||
|
@ -321,5 +501,19 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
|
|
||||||
return infos;
|
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 decryptResources;
|
||||||
BoolOption dumpEmbeddedAssemblies;
|
BoolOption dumpEmbeddedAssemblies;
|
||||||
BoolOption restoreFields;
|
BoolOption restoreFields;
|
||||||
|
BoolOption renameResourceKeys;
|
||||||
|
BoolOption castDeobfuscation;
|
||||||
|
|
||||||
public DeobfuscatorInfo()
|
public DeobfuscatorInfo()
|
||||||
: base() {
|
: base() {
|
||||||
|
@ -39,6 +41,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
decryptResources = new BoolOption(null, makeArgName("rsrc"), "Decrypt resources", true);
|
decryptResources = new BoolOption(null, makeArgName("rsrc"), "Decrypt resources", true);
|
||||||
dumpEmbeddedAssemblies = new BoolOption(null, makeArgName("embedded"), "Dump embedded assemblies", true);
|
dumpEmbeddedAssemblies = new BoolOption(null, makeArgName("embedded"), "Dump embedded assemblies", true);
|
||||||
restoreFields = new BoolOption(null, makeArgName("fields"), "Restore fields", 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 {
|
public override string Name {
|
||||||
|
@ -57,6 +61,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
DecryptResources = decryptResources.get(),
|
DecryptResources = decryptResources.get(),
|
||||||
DumpEmbeddedAssemblies = dumpEmbeddedAssemblies.get(),
|
DumpEmbeddedAssemblies = dumpEmbeddedAssemblies.get(),
|
||||||
RestoreFields = restoreFields.get(),
|
RestoreFields = restoreFields.get(),
|
||||||
|
RenameResourceKeys = renameResourceKeys.get(),
|
||||||
|
CastDeobfuscation = castDeobfuscation.get(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +73,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
decryptResources,
|
decryptResources,
|
||||||
dumpEmbeddedAssemblies,
|
dumpEmbeddedAssemblies,
|
||||||
restoreFields,
|
restoreFields,
|
||||||
|
renameResourceKeys,
|
||||||
|
castDeobfuscation,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +88,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
ResourceResolver resourceResolver;
|
ResourceResolver resourceResolver;
|
||||||
AssemblyResolver assemblyResolver;
|
AssemblyResolver assemblyResolver;
|
||||||
FieldsRestorer fieldsRestorer;
|
FieldsRestorer fieldsRestorer;
|
||||||
|
ArrayBlockDeobfuscator arrayBlockDeobfuscator;
|
||||||
|
|
||||||
internal class Options : OptionsBase {
|
internal class Options : OptionsBase {
|
||||||
public bool InlineMethods { get; set; }
|
public bool InlineMethods { get; set; }
|
||||||
|
@ -87,6 +96,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
public bool DecryptResources { get; set; }
|
public bool DecryptResources { get; set; }
|
||||||
public bool DumpEmbeddedAssemblies { get; set; }
|
public bool DumpEmbeddedAssemblies { get; set; }
|
||||||
public bool RestoreFields { get; set; }
|
public bool RestoreFields { get; set; }
|
||||||
|
public bool RenameResourceKeys { get; set; }
|
||||||
|
public bool CastDeobfuscation { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Type {
|
public override string Type {
|
||||||
|
@ -105,17 +116,32 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
get { return startedDeobfuscating ? options.InlineMethods : true; }
|
get { return startedDeobfuscating ? options.InlineMethods : true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IMethodCallInliner MethodCallInliner {
|
public override IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
|
||||||
get {
|
get {
|
||||||
|
var list = new List<IBlocksDeobfuscator>(getBlocksDeobfuscators());
|
||||||
if (CanInlineMethods)
|
if (CanInlineMethods)
|
||||||
return new MethodCallInliner();
|
list.Add(new DsMethodCallInliner(new CachedCflowDeobfuscator(getBlocksDeobfuscators())));
|
||||||
return new NoMethodInliner();
|
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)
|
public Deobfuscator(Options options)
|
||||||
: base(options) {
|
: base(options) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
|
if (options.RenameResourceKeys)
|
||||||
|
this.RenamingOptions |= RenamingOptions.RenameResourceKeys;
|
||||||
|
else
|
||||||
|
this.RenamingOptions &= ~RenamingOptions.RenameResourceKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override int detectInternal() {
|
protected override int detectInternal() {
|
||||||
|
@ -131,6 +157,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void scanForObfuscator() {
|
protected override void scanForObfuscator() {
|
||||||
|
staticStringInliner.UseUnknownArgs = true;
|
||||||
|
arrayBlockDeobfuscator = new ArrayBlockDeobfuscator(module);
|
||||||
|
arrayBlockDeobfuscator.init();
|
||||||
stringDecrypter = new StringDecrypter(module);
|
stringDecrypter = new StringDecrypter(module);
|
||||||
stringDecrypter.find(DeobfuscatedFile);
|
stringDecrypter.find(DeobfuscatedFile);
|
||||||
resourceResolver = new ResourceResolver(module, DeobfuscatedFile, this);
|
resourceResolver = new ResourceResolver(module, DeobfuscatedFile, this);
|
||||||
|
@ -146,8 +175,10 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
if (detectMethodProxyObfuscation())
|
if (detectMethodProxyObfuscation())
|
||||||
return DeobfuscatorInfo.THE_NAME + " 3.5";
|
return DeobfuscatorInfo.THE_NAME + " 3.5";
|
||||||
return DeobfuscatorInfo.THE_NAME + " 1.x-3.x";
|
return DeobfuscatorInfo.THE_NAME + " 1.x-3.x";
|
||||||
case StringDecrypter.DecrypterVersion.V4:
|
case StringDecrypter.DecrypterVersion.V4_0:
|
||||||
return DeobfuscatorInfo.THE_NAME + " 4.x";
|
return DeobfuscatorInfo.THE_NAME + " 4.0";
|
||||||
|
case StringDecrypter.DecrypterVersion.V4_1:
|
||||||
|
return DeobfuscatorInfo.THE_NAME + " 4.1";
|
||||||
}
|
}
|
||||||
|
|
||||||
return DeobfuscatorInfo.THE_NAME;
|
return DeobfuscatorInfo.THE_NAME;
|
||||||
|
@ -165,7 +196,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
continue;
|
continue;
|
||||||
if (checkedMethods++ >= 1000)
|
if (checkedMethods++ >= 1000)
|
||||||
goto done;
|
goto done;
|
||||||
if (!DeepSea.MethodCallInliner.canInline(method))
|
if (!DsMethodCallInliner.canInline(method))
|
||||||
continue;
|
continue;
|
||||||
foundProxies++;
|
foundProxies++;
|
||||||
}
|
}
|
||||||
|
@ -207,6 +238,7 @@ done:
|
||||||
addCctorInitCallToBeRemoved(resourceResolver.InitMethod);
|
addCctorInitCallToBeRemoved(resourceResolver.InitMethod);
|
||||||
addCallToBeRemoved(module.EntryPoint, resourceResolver.InitMethod);
|
addCallToBeRemoved(module.EntryPoint, resourceResolver.InitMethod);
|
||||||
addMethodToBeRemoved(resourceResolver.InitMethod, "Resource resolver init method");
|
addMethodToBeRemoved(resourceResolver.InitMethod, "Resource resolver init method");
|
||||||
|
addMethodToBeRemoved(resourceResolver.InitMethod2, "Resource resolver init method #2");
|
||||||
addMethodToBeRemoved(resourceResolver.HandlerMethod, "Resource resolver handler method");
|
addMethodToBeRemoved(resourceResolver.HandlerMethod, "Resource resolver handler method");
|
||||||
addMethodToBeRemoved(resourceResolver.GetDataMethod, "Resource resolver 'get resource data' method");
|
addMethodToBeRemoved(resourceResolver.GetDataMethod, "Resource resolver 'get resource data' method");
|
||||||
}
|
}
|
||||||
|
@ -224,6 +256,7 @@ done:
|
||||||
addCallToBeRemoved(module.EntryPoint, assemblyResolver.InitMethod);
|
addCallToBeRemoved(module.EntryPoint, assemblyResolver.InitMethod);
|
||||||
addMethodToBeRemoved(assemblyResolver.InitMethod, "Assembly resolver init method");
|
addMethodToBeRemoved(assemblyResolver.InitMethod, "Assembly resolver init method");
|
||||||
addMethodToBeRemoved(assemblyResolver.HandlerMethod, "Assembly resolver handler method");
|
addMethodToBeRemoved(assemblyResolver.HandlerMethod, "Assembly resolver handler method");
|
||||||
|
addMethodToBeRemoved(assemblyResolver.DecryptMethod, "Assembly resolver decrypt method");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void deobfuscateMethodEnd(Blocks blocks) {
|
public override void deobfuscateMethodEnd(Blocks blocks) {
|
||||||
|
@ -245,13 +278,15 @@ done:
|
||||||
stringDecrypter.cleanup();
|
stringDecrypter.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addFieldsToBeRemoved(arrayBlockDeobfuscator.cleanUp(), "Control flow obfuscation array");
|
||||||
|
|
||||||
base.deobfuscateEnd();
|
base.deobfuscateEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeInlinedMethods() {
|
void removeInlinedMethods() {
|
||||||
if (!options.InlineMethods || !options.RemoveInlinedMethods)
|
if (!options.InlineMethods || !options.RemoveInlinedMethods)
|
||||||
return;
|
return;
|
||||||
removeInlinedMethods(DsInlinedMethodsFinder.find(module));
|
removeInlinedMethods(DsInlinedMethodsFinder.find(module, staticStringInliner.Methods));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<int> getStringDecrypterMethods() {
|
public override IEnumerable<int> getStringDecrypterMethods() {
|
||||||
|
|
|
@ -17,13 +17,24 @@
|
||||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace de4dot.blocks.cflow {
|
using System.Collections.Generic;
|
||||||
public class NoMethodInliner : IMethodCallInliner {
|
using Mono.Cecil.Cil;
|
||||||
public void init(Blocks blocks, Block block) {
|
using de4dot.blocks;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
|
class DsConstantsReader : ConstantsReader {
|
||||||
|
public DsConstantsReader(List<Instr> instrs)
|
||||||
|
: base(instrs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool deobfuscate() {
|
protected override bool getLocalConstant(Instruction instr, out int value) {
|
||||||
return false;
|
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 {
|
namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
static class DsInlinedMethodsFinder {
|
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>();
|
var inlinedMethods = new List<MethodDefinition>();
|
||||||
|
|
||||||
foreach (var type in module.GetTypes()) {
|
foreach (var type in module.GetTypes()) {
|
||||||
foreach (var method in type.Methods) {
|
foreach (var method in type.Methods) {
|
||||||
if (MethodCallInliner.canInline(method))
|
if (!notInlinedMethodsDict.ContainsKey(method) && DsMethodCallInliner.canInline(method))
|
||||||
inlinedMethods.Add(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 System.Collections.Generic;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Cecil.Metadata;
|
||||||
using de4dot.blocks;
|
using de4dot.blocks;
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.DeepSea {
|
namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
|
@ -35,7 +36,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
get {
|
get {
|
||||||
var list = new List<TypeDefinition>(structToOwners.Count);
|
var list = new List<TypeDefinition>(structToOwners.Count);
|
||||||
foreach (var structType in structToOwners.getKeys()) {
|
foreach (var structType in structToOwners.getKeys()) {
|
||||||
if (structType.Methods.Count != 0)
|
if (!hasNoMethods(structType))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
list.Add(structType);
|
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) {
|
public FieldsRestorer(ModuleDefinition module) {
|
||||||
this.module = module;
|
this.module = module;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +94,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
foreach (var type in module.GetTypes()) {
|
foreach (var type in module.GetTypes()) {
|
||||||
foreach (var field in getPossibleFields(type)) {
|
foreach (var field in getPossibleFields(type)) {
|
||||||
var fieldType = DotNetUtils.getType(module, field.FieldType);
|
var fieldType = DotNetUtils.getType(module, field.FieldType);
|
||||||
if (fieldType == null || !fieldType.IsValueType)
|
if (fieldType == null)
|
||||||
|
continue;
|
||||||
|
if (!checkBaseType(fieldType))
|
||||||
continue;
|
continue;
|
||||||
if ((fieldType.Attributes & ~TypeAttributes.Sealed) != TypeAttributes.NestedAssembly)
|
if ((fieldType.Attributes & ~TypeAttributes.Sealed) != TypeAttributes.NestedAssembly)
|
||||||
continue;
|
continue;
|
||||||
|
@ -90,9 +108,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
continue;
|
continue;
|
||||||
if (fieldType.HasEvents || fieldType.HasProperties || fieldType.HasInterfaces)
|
if (fieldType.HasEvents || fieldType.HasProperties || fieldType.HasInterfaces)
|
||||||
continue;
|
continue;
|
||||||
if (hasNonStaticMethods(fieldType))
|
if (checkMethods(fieldType))
|
||||||
continue;
|
continue;
|
||||||
if (hasStaticFields(fieldType))
|
if (!checkFields(fieldType))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
List<TypeDefinition> list;
|
List<TypeDefinition> list;
|
||||||
|
@ -132,7 +150,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
if (field.Attributes != FieldAttributes.Private)
|
if (field.Attributes != FieldAttributes.Private)
|
||||||
continue;
|
continue;
|
||||||
var fieldType = DotNetUtils.getType(module, field.FieldType);
|
var fieldType = DotNetUtils.getType(module, field.FieldType);
|
||||||
if (fieldType == null || !fieldType.IsValueType)
|
if (fieldType == null)
|
||||||
|
continue;
|
||||||
|
if (!checkBaseType(fieldType))
|
||||||
continue;
|
continue;
|
||||||
var list = typeToFields.find(fieldType);
|
var list = typeToFields.find(fieldType);
|
||||||
if (list == null)
|
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) {
|
void removeType(Dictionary<TypeDefinition, List<TypeDefinition>> candidates, TypeReference type) {
|
||||||
var typeDef = DotNetUtils.getType(module, type);
|
var typeDef = DotNetUtils.getType(module, type);
|
||||||
if (typeDef == null)
|
if (typeDef == null)
|
||||||
|
@ -153,10 +179,12 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
candidates.Remove(typeDef);
|
candidates.Remove(typeDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hasNonStaticMethods(TypeDefinition type) {
|
static bool checkMethods(TypeDefinition type) {
|
||||||
foreach (var method in type.Methods) {
|
foreach (var method in type.Methods) {
|
||||||
if (method.Name == ".cctor")
|
if (method.Name == ".cctor")
|
||||||
continue;
|
continue;
|
||||||
|
if (type.BaseType != null && type.BaseType.EType == ElementType.Object && method.Name == ".ctor" && method.Parameters.Count == 0)
|
||||||
|
continue;
|
||||||
if (!method.IsStatic)
|
if (!method.IsStatic)
|
||||||
return true;
|
return true;
|
||||||
if (method.GenericParameters.Count > 0)
|
if (method.GenericParameters.Count > 0)
|
||||||
|
@ -169,22 +197,31 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return false;
|
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) {
|
foreach (var field in type.Fields) {
|
||||||
if (field.IsStatic)
|
if (field.IsStatic)
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
|
if (!field.IsAssembly)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deobfuscate(Blocks blocks) {
|
public void deobfuscate(Blocks blocks) {
|
||||||
|
deobfuscateNormal(blocks);
|
||||||
|
fixFieldCtorCalls(blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deobfuscateNormal(Blocks blocks) {
|
||||||
var instrsToRemove = new List<int>();
|
var instrsToRemove = new List<int>();
|
||||||
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
|
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
|
||||||
instrsToRemove.Clear();
|
instrsToRemove.Clear();
|
||||||
var instrs = block.Instructions;
|
var instrs = block.Instructions;
|
||||||
for (int i = instrs.Count - 1; i >= 0; i--) {
|
for (int i = instrs.Count - 1; i >= 0; i--) {
|
||||||
var instr = instrs[i];
|
var instr = instrs[i];
|
||||||
if (instr.OpCode.Code != Code.Ldflda)
|
if (instr.OpCode.Code != Code.Ldflda && instr.OpCode.Code != Code.Ldfld)
|
||||||
continue;
|
continue;
|
||||||
var structField = instr.Operand as FieldReference;
|
var structField = instr.Operand as FieldReference;
|
||||||
if (structField == null || !structFieldsToFix.find(structField))
|
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) {
|
FieldDefinition getNewField(FieldReference structField, FieldReference oldFieldRef) {
|
||||||
var fieldsDict = typeToFieldsDict.find(structField.DeclaringType);
|
var fieldsDict = typeToFieldsDict.find(structField.DeclaringType);
|
||||||
if (fieldsDict == null)
|
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);
|
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) {
|
protected static byte[] decryptResourceV3(EmbeddedResource resource) {
|
||||||
return decryptResourceV3(resource.GetResourceData());
|
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) {
|
protected static byte[] decryptResource(byte[] data, int start, int len, int magic) {
|
||||||
for (int i = start; i < start + len; i++)
|
for (int i = start; i < start + len; i++)
|
||||||
data[i] ^= (byte)(i - start + magic);
|
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)
|
if (BitConverter.ToInt16(data, start) != 0x5A4D)
|
||||||
return DeobUtils.inflate(data, start, len, true);
|
return DeobUtils.inflate(data, start, len, true);
|
||||||
|
|
||||||
|
|
|
@ -17,24 +17,70 @@
|
||||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using de4dot.blocks;
|
using de4dot.blocks;
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.DeepSea {
|
namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
class ResourceResolver : ResolverBase {
|
class ResourceResolver : ResolverBase {
|
||||||
EmbeddedResource resource;
|
Data30 data30;
|
||||||
FieldDefinition resourceField;
|
Data40 data40;
|
||||||
MethodDefinition getDataMethod;
|
Data41 data41;
|
||||||
int magicV4;
|
ResourceVersion version = ResourceVersion.Unknown;
|
||||||
bool isV3;
|
|
||||||
|
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 {
|
public MethodDefinition GetDataMethod {
|
||||||
get { return getDataMethod; }
|
get { return data40 != null ? data40.getDataMethod : null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmbeddedResource Resource {
|
public EmbeddedResource Resource {
|
||||||
get { return resource; }
|
get { return data30 != null ? data30.resource : null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceResolver(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob)
|
public ResourceResolver(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob)
|
||||||
|
@ -47,23 +93,119 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
|
|
||||||
protected override bool checkHandlerMethodDesktopInternal(MethodDefinition handler) {
|
protected override bool checkHandlerMethodDesktopInternal(MethodDefinition handler) {
|
||||||
if (checkHandlerV3(handler)) {
|
if (checkHandlerV3(handler)) {
|
||||||
isV3 = true;
|
version = ResourceVersion.V3;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldDefinition resourceFieldTmp;
|
|
||||||
MethodDefinition getDataMethodTmp;
|
|
||||||
simpleDeobfuscator.deobfuscate(handler);
|
simpleDeobfuscator.deobfuscate(handler);
|
||||||
if (checkHandlerV4(handler, out resourceFieldTmp, out getDataMethodTmp, out magicV4)) {
|
if ((data40 = checkHandlerV40(handler)) != null) {
|
||||||
isV3 = false;
|
version = ResourceVersion.V40;
|
||||||
resourceField = resourceFieldTmp;
|
return true;
|
||||||
getDataMethod = getDataMethodTmp;
|
}
|
||||||
|
|
||||||
|
var info = getHandlerArgs41(handler);
|
||||||
|
Data41 data41Tmp;
|
||||||
|
if (info != null && checkHandlerV41(info, out data41Tmp)) {
|
||||||
|
version = ResourceVersion.V41;
|
||||||
|
data41 = data41Tmp;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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[] {
|
static string[] handlerLocalTypes_V3 = new string[] {
|
||||||
"System.AppDomain",
|
"System.AppDomain",
|
||||||
"System.Byte[]",
|
"System.Byte[]",
|
||||||
|
@ -79,7 +221,9 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return new LocalTypes(handler).all(handlerLocalTypes_V3);
|
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;
|
var instrs = handler.Body.Instructions;
|
||||||
for (int i = 0; i < instrs.Count; i++) {
|
for (int i = 0; i < instrs.Count; i++) {
|
||||||
int index = i;
|
int index = i;
|
||||||
|
@ -105,7 +249,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
// 4.0.1.18 .. 4.0.3
|
// 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;
|
continue;
|
||||||
|
|
||||||
var ldci4_len = instrs[index++];
|
var ldci4_len = instrs[index++];
|
||||||
|
@ -120,25 +264,24 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
var ldci4_magic = instrs[index++];
|
var ldci4_magic = instrs[index++];
|
||||||
if (!DotNetUtils.isLdcI4(ldci4_magic))
|
if (!DotNetUtils.isLdcI4(ldci4_magic))
|
||||||
continue;
|
continue;
|
||||||
magic = DotNetUtils.getLdcI4Value(ldci4_magic);
|
data40.magic = DotNetUtils.getLdcI4Value(ldci4_magic);
|
||||||
|
|
||||||
var call = instrs[index++];
|
var call = instrs[index++];
|
||||||
if (call.OpCode.Code == Code.Tail)
|
if (call.OpCode.Code == Code.Tail)
|
||||||
call = instrs[index++];
|
call = instrs[index++];
|
||||||
if (call.OpCode.Code != Code.Call)
|
if (call.OpCode.Code != Code.Call)
|
||||||
continue;
|
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;
|
continue;
|
||||||
|
|
||||||
resourceField = field;
|
data40.resourceField = field;
|
||||||
getDataMethod = method;
|
data40.getDataMethod = method;
|
||||||
return true;
|
data40.resolveHandler2 = resolveHandler2;
|
||||||
|
return data40;
|
||||||
}
|
}
|
||||||
|
|
||||||
magic = 0;
|
return null;
|
||||||
resourceField = null;
|
|
||||||
getDataMethod = null;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static FieldDefinition getResourceField(MethodDefinition method) {
|
static FieldDefinition getResourceField(MethodDefinition method) {
|
||||||
|
@ -157,11 +300,12 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
if (resolveHandler == null)
|
if (resolveHandler == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isV3) {
|
if (version == ResourceVersion.V3) {
|
||||||
simpleDeobfuscator.deobfuscate(resolveHandler);
|
simpleDeobfuscator.deobfuscate(resolveHandler);
|
||||||
simpleDeobfuscator.decryptStrings(resolveHandler, deob);
|
simpleDeobfuscator.decryptStrings(resolveHandler, deob);
|
||||||
resource = DeobUtils.getEmbeddedResourceFromCodeStrings(module, resolveHandler);
|
data30 = new Data30();
|
||||||
if (resource == null) {
|
data30.resource = DeobUtils.getEmbeddedResourceFromCodeStrings(module, resolveHandler);
|
||||||
|
if (data30.resource == null) {
|
||||||
Log.w("Could not find resource of encrypted resources");
|
Log.w("Could not find resource of encrypted resources");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -171,22 +315,34 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
public bool mergeResources(out EmbeddedResource rsrc) {
|
public bool mergeResources(out EmbeddedResource rsrc) {
|
||||||
rsrc = null;
|
rsrc = null;
|
||||||
|
|
||||||
if (isV3) {
|
switch (version) {
|
||||||
if (resource == null)
|
case ResourceVersion.V3:
|
||||||
|
if (data30.resource == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DeobUtils.decryptAndAddResources(module, resource.Name, () => decryptResourceV3(resource));
|
DeobUtils.decryptAndAddResources(module, data30.resource.Name, () => decryptResourceV3(data30.resource));
|
||||||
rsrc = 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)
|
if (resourceField == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
string name = string.Format("Embedded data field {0:X8} RVA {1:X8}", resourceField.MetadataToken.ToInt32(), resourceField.RVA);
|
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.InitialValue = new byte[1];
|
||||||
resourceField.FieldType = module.TypeSystem.Byte;
|
resourceField.FieldType = module.TypeSystem.Byte;
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Cecil.Metadata;
|
||||||
using de4dot.blocks;
|
using de4dot.blocks;
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.DeepSea {
|
namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
|
@ -33,7 +34,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
public enum DecrypterVersion {
|
public enum DecrypterVersion {
|
||||||
Unknown,
|
Unknown,
|
||||||
V1_3,
|
V1_3,
|
||||||
V4,
|
V4_0,
|
||||||
|
V4_1,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IDecrypterInfo {
|
interface IDecrypterInfo {
|
||||||
|
@ -44,6 +46,12 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
}
|
}
|
||||||
|
|
||||||
static short[] findKey(MethodDefinition initMethod, FieldDefinition keyField) {
|
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;
|
var instrs = initMethod.Body.Instructions;
|
||||||
for (int i = 0; i < instrs.Count - 2; i++) {
|
for (int i = 0; i < instrs.Count - 2; i++) {
|
||||||
var ldci4 = instrs[i];
|
var ldci4 = instrs[i];
|
||||||
|
@ -69,7 +77,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
var field = getStoreField(initMethod, startInitIndex, local);
|
var field = getStoreField(initMethod, startInitIndex, local);
|
||||||
if (field == null)
|
if (field == null)
|
||||||
continue;
|
continue;
|
||||||
if (keyField == field)
|
if (fields.find(field))
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,9 +102,267 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return null;
|
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;
|
MethodDefinition cctor;
|
||||||
public MethodDefinition Method { get; set; }
|
|
||||||
int magic;
|
int magic;
|
||||||
FieldDefinition cachedStringsField;
|
FieldDefinition cachedStringsField;
|
||||||
FieldDefinition keyField;
|
FieldDefinition keyField;
|
||||||
|
@ -105,15 +371,23 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
short[] key;
|
short[] key;
|
||||||
ushort[] encryptedData;
|
ushort[] encryptedData;
|
||||||
|
|
||||||
|
public MethodDefinition Method { get; private set; }
|
||||||
|
|
||||||
public DecrypterVersion Version {
|
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.cctor = cctor;
|
||||||
this.Method = method;
|
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() {
|
public bool initialize() {
|
||||||
if (!findMagic(Method, out magic))
|
if (!findMagic(Method, out magic))
|
||||||
return false;
|
return false;
|
||||||
|
@ -140,24 +414,6 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return true;
|
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() {
|
List<FieldDefinition> findFields() {
|
||||||
var charArrayFields = new List<FieldDefinition>();
|
var charArrayFields = new List<FieldDefinition>();
|
||||||
|
|
||||||
|
@ -263,20 +519,27 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DecrypterInfo3 : IDecrypterInfo {
|
class DecrypterInfo13 : IDecrypterInfo {
|
||||||
MethodDefinition cctor;
|
MethodDefinition cctor;
|
||||||
public MethodDefinition Method { get; set; }
|
|
||||||
FieldDefinition cachedStringsField;
|
FieldDefinition cachedStringsField;
|
||||||
FieldDefinition keyField;
|
FieldDefinition keyField;
|
||||||
int magic;
|
int magic;
|
||||||
string[] encryptedStrings;
|
string[] encryptedStrings;
|
||||||
short[] key;
|
short[] key;
|
||||||
|
|
||||||
|
public MethodDefinition Method { get; private set; }
|
||||||
|
|
||||||
public DecrypterVersion Version {
|
public DecrypterVersion Version {
|
||||||
get { return DecrypterVersion.V1_3; }
|
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.cctor = cctor;
|
||||||
this.Method = method;
|
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;
|
bool hasPublicKeyToken = module.Assembly.Name.PublicKeyToken != null && module.Assembly.Name.PublicKeyToken.Length != 0;
|
||||||
foreach (var type in module.GetTypes()) {
|
foreach (var type in module.GetTypes()) {
|
||||||
if (!checkFields(type.Fields))
|
|
||||||
continue;
|
|
||||||
var cctor = DotNetUtils.getMethod(type, ".cctor");
|
var cctor = DotNetUtils.getMethod(type, ".cctor");
|
||||||
if (cctor == null)
|
if (cctor == null)
|
||||||
continue;
|
continue;
|
||||||
if (!hasPublicKeyToken)
|
|
||||||
simpleDeobfuscator.deobfuscate(cctor);
|
|
||||||
|
|
||||||
|
bool deobfuscatedCctor = false;
|
||||||
foreach (var method in type.Methods) {
|
foreach (var method in type.Methods) {
|
||||||
if (method.Body == null)
|
if (!method.IsStatic || method.Body == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
IDecrypterInfo info = null;
|
IDecrypterInfo info = null;
|
||||||
|
|
||||||
if (DotNetUtils.isMethod(method, "System.String", "(System.Int32)")) {
|
if (DecrypterInfo13.isPossibleDecrypterMethod(method)) {
|
||||||
|
deobfuscateCctor(simpleDeobfuscator, cctor, ref deobfuscatedCctor, hasPublicKeyToken);
|
||||||
simpleDeobfuscator.deobfuscate(method);
|
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);
|
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)
|
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) {
|
static bool checkFields(IEnumerable<FieldDefinition> fields) {
|
||||||
bool foundCharAry = false, foundStringAry = false;
|
bool foundCharAry = false, foundStringAry = false;
|
||||||
foreach (var field in fields) {
|
foreach (var field in fields) {
|
||||||
|
@ -490,15 +764,22 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return foundCharAry && foundStringAry;
|
return foundCharAry && foundStringAry;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecrypterInfo3 getInfoV3(MethodDefinition cctor, MethodDefinition method) {
|
DecrypterInfo13 getInfoV13(MethodDefinition cctor, MethodDefinition method) {
|
||||||
var info = new DecrypterInfo3(cctor, method);
|
var info = new DecrypterInfo13(cctor, method);
|
||||||
if (!info.initialize())
|
if (!info.initialize())
|
||||||
return null;
|
return null;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecrypterInfo4 getInfoV4(MethodDefinition cctor, MethodDefinition method) {
|
DecrypterInfo40 getInfoV40(MethodDefinition cctor, MethodDefinition method) {
|
||||||
var info = new DecrypterInfo4(cctor, 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())
|
if (!info.initialize())
|
||||||
return null;
|
return null;
|
||||||
return info;
|
return info;
|
||||||
|
|
|
@ -95,11 +95,12 @@ namespace de4dot.code.deobfuscators {
|
||||||
get { return Operations.DecryptStrings != OpDecryptString.None && staticStringInliner.InlinedAllCalls; }
|
get { return Operations.DecryptStrings != OpDecryptString.None && staticStringInliner.InlinedAllCalls; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual IMethodCallInliner MethodCallInliner {
|
public virtual IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
|
||||||
get {
|
get {
|
||||||
|
var list = new List<IBlocksDeobfuscator>();
|
||||||
if (CanInlineMethods)
|
if (CanInlineMethods)
|
||||||
return new MethodCallInliner(false);
|
list.Add(new MethodCallInliner(false));
|
||||||
return new NoMethodInliner();
|
return list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,6 +745,10 @@ namespace de4dot.code.deobfuscators {
|
||||||
return name != null && checkValidName(name);
|
return name != null && checkValidName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual bool isValidResourceKeyName(string name) {
|
||||||
|
return name != null && checkValidName(name);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void OnBeforeAddingResources(MetadataBuilder builder) {
|
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;
|
int index = 0;
|
||||||
var instrs = method.Body.Instructions;
|
var instrs = method.Body.Instructions;
|
||||||
var constantsReader = new ConstantsReader(method);
|
var constantsReader = new EfConstantsReader(method);
|
||||||
while (true) {
|
while (true) {
|
||||||
int val;
|
int val;
|
||||||
if (!constantsReader.getNextInt32(ref index, out 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;
|
BinaryReader reader;
|
||||||
DecrypterType decrypterType;
|
DecrypterType decrypterType;
|
||||||
StreamHelperType streamHelperType;
|
StreamHelperType streamHelperType;
|
||||||
ConstantsReader stringMethodConsts;
|
EfConstantsReader stringMethodConsts;
|
||||||
bool isV32OrLater;
|
bool isV32OrLater;
|
||||||
|
|
||||||
class StreamHelperType {
|
class StreamHelperType {
|
||||||
|
@ -219,7 +219,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
|
||||||
|
|
||||||
bool findConstants(ISimpleDeobfuscator simpleDeobfuscator) {
|
bool findConstants(ISimpleDeobfuscator simpleDeobfuscator) {
|
||||||
simpleDeobfuscator.deobfuscate(stringMethod);
|
simpleDeobfuscator.deobfuscate(stringMethod);
|
||||||
stringMethodConsts = new ConstantsReader(stringMethod);
|
stringMethodConsts = new EfConstantsReader(stringMethod);
|
||||||
|
|
||||||
if (!findResource(stringMethod))
|
if (!findResource(stringMethod))
|
||||||
return false;
|
return false;
|
||||||
|
@ -658,7 +658,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
|
||||||
return findIntsCctor2(cctor);
|
return findIntsCctor2(cctor);
|
||||||
|
|
||||||
int tmp1, tmp2, tmp3 = 0;
|
int tmp1, tmp2, tmp3 = 0;
|
||||||
var constantsReader = new ConstantsReader(cctor);
|
var constantsReader = new EfConstantsReader(cctor);
|
||||||
if (!constantsReader.getNextInt32(ref index, out tmp1))
|
if (!constantsReader.getNextInt32(ref index, out tmp1))
|
||||||
return false;
|
return false;
|
||||||
if (tmp1 == 0 && !constantsReader.getNextInt32(ref index, out tmp1))
|
if (tmp1 == 0 && !constantsReader.getNextInt32(ref index, out tmp1))
|
||||||
|
@ -684,7 +684,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
|
||||||
bool findIntsCctor2(MethodDefinition cctor) {
|
bool findIntsCctor2(MethodDefinition cctor) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
var instrs = cctor.Body.Instructions;
|
var instrs = cctor.Body.Instructions;
|
||||||
var constantsReader = new ConstantsReader(cctor);
|
var constantsReader = new EfConstantsReader(cctor);
|
||||||
while (index >= 0) {
|
while (index >= 0) {
|
||||||
int val;
|
int val;
|
||||||
if (!constantsReader.getNextInt32(ref index, out val))
|
if (!constantsReader.getNextInt32(ref index, out val))
|
||||||
|
|
|
@ -50,6 +50,7 @@ namespace de4dot.code.deobfuscators {
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum RenamingOptions {
|
public enum RenamingOptions {
|
||||||
RemoveNamespaceIfOneType = 1,
|
RemoveNamespaceIfOneType = 1,
|
||||||
|
RenameResourceKeys = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IDeobfuscator : INameChecker {
|
public interface IDeobfuscator : INameChecker {
|
||||||
|
@ -61,7 +62,7 @@ namespace de4dot.code.deobfuscators {
|
||||||
StringFeatures StringFeatures { get; }
|
StringFeatures StringFeatures { get; }
|
||||||
RenamingOptions RenamingOptions { get; }
|
RenamingOptions RenamingOptions { get; }
|
||||||
DecrypterType DefaultDecrypterType { get; }
|
DecrypterType DefaultDecrypterType { get; }
|
||||||
IMethodCallInliner MethodCallInliner { get; }
|
IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators { get; }
|
||||||
|
|
||||||
// This is non-null only in detect() and deobfuscateBegin().
|
// This is non-null only in detect() and deobfuscateBegin().
|
||||||
IDeobfuscatedFile DeobfuscatedFile { get; set; }
|
IDeobfuscatedFile DeobfuscatedFile { get; set; }
|
||||||
|
|
|
@ -100,11 +100,12 @@ namespace de4dot.code.deobfuscators.Spices_Net {
|
||||||
get { return startedDeobfuscating ? options.InlineMethods : true; }
|
get { return startedDeobfuscating ? options.InlineMethods : true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IMethodCallInliner MethodCallInliner {
|
public override IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
|
||||||
get {
|
get {
|
||||||
|
var list = new List<IBlocksDeobfuscator>();
|
||||||
if (CanInlineMethods)
|
if (CanInlineMethods)
|
||||||
return methodCallInliner;
|
list.Add(methodCallInliner);
|
||||||
return new NoMethodInliner();
|
return list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,10 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 {
|
||||||
return name != null && checkValidName(name, isRandomNameMembers);
|
return name != null && checkValidName(name, isRandomNameMembers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool isValidResourceKeyName(string name) {
|
||||||
|
return name != null && checkValidName(name, isRandomNameMembers);
|
||||||
|
}
|
||||||
|
|
||||||
protected override int detectInternal() {
|
protected override int detectInternal() {
|
||||||
int val = 0;
|
int val = 0;
|
||||||
|
|
||||||
|
|
|
@ -222,6 +222,10 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
|
||||||
return name != null && checkValidName(name, isRandomNameMembers);
|
return name != null && checkValidName(name, isRandomNameMembers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool isValidResourceKeyName(string name) {
|
||||||
|
return name != null && checkValidName(name, isRandomNameMembers);
|
||||||
|
}
|
||||||
|
|
||||||
protected override int detectInternal() {
|
protected override int detectInternal() {
|
||||||
int val = 0;
|
int val = 0;
|
||||||
|
|
||||||
|
|
|
@ -27,5 +27,6 @@ namespace de4dot.code.renamer {
|
||||||
bool isValidFieldName(string name);
|
bool isValidFieldName(string name);
|
||||||
bool isValidGenericParamName(string name);
|
bool isValidGenericParamName(string name);
|
||||||
bool isValidMethodArgName(string name);
|
bool isValidMethodArgName(string name);
|
||||||
|
bool isValidResourceKeyName(string name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ namespace de4dot.code.renamer {
|
||||||
Log.n("Renaming all obfuscated symbols");
|
Log.n("Renaming all obfuscated symbols");
|
||||||
|
|
||||||
modules.initialize();
|
modules.initialize();
|
||||||
|
renameResourceKeys();
|
||||||
var groups = modules.initializeVirtualMembers();
|
var groups = modules.initializeVirtualMembers();
|
||||||
memberInfos.initialize(modules);
|
memberInfos.initialize(modules);
|
||||||
renameTypeDefinitions();
|
renameTypeDefinitions();
|
||||||
|
@ -92,6 +93,14 @@ namespace de4dot.code.renamer {
|
||||||
modules.cleanUp();
|
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) {
|
void removeUselessOverrides(MethodNameGroups groups) {
|
||||||
foreach (var group in groups.getAllGroups()) {
|
foreach (var group in groups.getAllGroups()) {
|
||||||
foreach (var method in group.Methods) {
|
foreach (var method in group.Methods) {
|
||||||
|
@ -396,8 +405,60 @@ namespace de4dot.code.renamer {
|
||||||
var allGroups = groups.getAllGroups();
|
var allGroups = groups.getAllGroups();
|
||||||
restoreVirtualProperties(allGroups);
|
restoreVirtualProperties(allGroups);
|
||||||
restorePropertiesFromNames(allGroups);
|
restorePropertiesFromNames(allGroups);
|
||||||
|
resetVirtualPropertyNames(allGroups);
|
||||||
restoreVirtualEvents(allGroups);
|
restoreVirtualEvents(allGroups);
|
||||||
restoreEventsFromNames(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) {
|
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) {
|
public string getNewPropertyName(PropertyDefinition propertyDefinition) {
|
||||||
var propType = propertyDefinition.PropertyType;
|
var propType = propertyDefinition.PropertyType;
|
||||||
string newName;
|
string newName;
|
||||||
if (propType is GenericParameter)
|
if (isGeneric(propType))
|
||||||
newName = existingPropertyNames.getName(propertyDefinition.Name, genericPropertyNameCreator);
|
newName = existingPropertyNames.getName(propertyDefinition.Name, genericPropertyNameCreator);
|
||||||
else
|
else
|
||||||
newName = existingPropertyNames.getName(propertyDefinition.Name, () => propertyNameCreator.create(propType));
|
newName = existingPropertyNames.getName(propertyDefinition.Name, () => propertyNameCreator.create(propType));
|
||||||
|
@ -97,6 +97,17 @@ namespace de4dot.code.renamer {
|
||||||
return newName;
|
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) {
|
public string getNewEventName(EventDefinition eventDefinition) {
|
||||||
string newName = eventNameCreator.create();
|
string newName = eventNameCreator.create();
|
||||||
addEventName(newName);
|
addEventName(newName);
|
||||||
|
|
|
@ -26,6 +26,10 @@ namespace de4dot.code.resources {
|
||||||
readonly ResourceTypeCode code;
|
readonly ResourceTypeCode code;
|
||||||
readonly object data;
|
readonly object data;
|
||||||
|
|
||||||
|
public object Data {
|
||||||
|
get { return data; }
|
||||||
|
}
|
||||||
|
|
||||||
public ResourceTypeCode Code {
|
public ResourceTypeCode Code {
|
||||||
get { return 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;
|
exitCode = 1;
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
|
if (printFullStackTrace()) {
|
||||||
printStackTrace(ex);
|
printStackTrace(ex);
|
||||||
Log.e("\nTry the latest version before reporting this problem!");
|
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");
|
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;
|
exitCode = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,11 +106,30 @@ namespace de4dot.cui {
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isN00bUser() {
|
static bool printFullStackTrace() {
|
||||||
var env = Environment.GetEnvironmentVariables();
|
if (Log.isAtLeast(Log.LogLevel.verbose))
|
||||||
if (env["VisualStudioDir"] != null)
|
return true;
|
||||||
|
if (hasEnv("STACKTRACE"))
|
||||||
|
return true;
|
||||||
|
|
||||||
return false;
|
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) {
|
public static void printStackTrace(Exception ex, Log.LogLevel logLevel = Log.LogLevel.error) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user